From fc8752108b03a15fce0220e82e95eb81a44b2677 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 4 Apr 2018 14:46:40 +0100 Subject: [PATCH 001/123] revise README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 14cdeae..28c4601 100644 --- a/README.md +++ b/README.md @@ -164,7 +164,7 @@ libvips properties as properties of the PHP `Vips\Image` class. ``` $ phpcs --standard=PSR2 src -$ php ~/packages/php/composer.phar install +$ composer install $ vendor/bin/phpunit $ vendor/bin/phpdoc ``` From 5ce14607fceaea476d82ca0856dd87de15357323 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 4 Apr 2018 15:14:13 +0100 Subject: [PATCH 002/123] remove an eol whitespace --- src/Image.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Image.php b/src/Image.php index 2889852..e3a6e0e 100644 --- a/src/Image.php +++ b/src/Image.php @@ -1978,7 +1978,7 @@ public function bandrank($other, array $options = []): Image /* bandrank will appear as a static class member, as * Image::bandrank([a, b, c]), but it's better as an instance * method. - * + * * We need to define this by hand. */ From 5b52e00dba1e221a1a233c03b3176a1d411ba842 Mon Sep 17 00:00:00 2001 From: Kleis Auke Wolthuizen Date: Sat, 9 Jun 2018 22:03:42 +0200 Subject: [PATCH 003/123] General improvements - Update dependencies. - Test with libvips 8.6.3 on Travis. - Use pyvips for the auto-generated docs/enums (fixes #56). - Regenerate docs/enums. - If statement can be merged with parent one. --- .travis.yml | 2 +- composer.json | 6 +- examples/generate_phpdoc.py | 331 +++++++++++++++++++++ examples/generate_phpdoc.rb | 347 ---------------------- src/DemandStyle.php | 1 - src/Image.php | 10 +- src/ImageAutodoc.php | 562 ++++++++++++++++++------------------ src/ImageType.php | 1 - src/Token.php | 1 - 9 files changed, 617 insertions(+), 644 deletions(-) create mode 100644 examples/generate_phpdoc.py delete mode 100755 examples/generate_phpdoc.rb diff --git a/.travis.yml b/.travis.yml index 582e779..c8d5c9b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,7 @@ env: global: - VIPS_VERSION_MAJOR=8 - VIPS_VERSION_MINOR=6 - - VIPS_VERSION_MICRO=0 + - VIPS_VERSION_MICRO=3 - PATH=$HOME/vips/bin:$PATH - LD_LIBRARY_PATH=$HOME/vips/lib:$LD_LIBRARY_PATH - PKG_CONFIG_PATH=$HOME/vips/lib/pkgconfig:$PKG_CONFIG_PATH diff --git a/composer.json b/composer.json index e0fde80..ead46b0 100644 --- a/composer.json +++ b/composer.json @@ -19,12 +19,12 @@ "require": { "php": ">=7.0.11", "ext-vips": ">=0.1.2", - "psr/log": "^1.0.1" + "psr/log": "^1.0.2" }, "require-dev": { - "phpunit/phpunit": "^6.3", + "phpunit/phpunit": "^6.5", "phpdocumentor/phpdocumentor" : "^2.9", - "jakub-onderka/php-parallel-lint": "^0.9.2", + "jakub-onderka/php-parallel-lint": "^1.0.0", "squizlabs/php_codesniffer": "3.*" }, "autoload": { diff --git a/examples/generate_phpdoc.py b/examples/generate_phpdoc.py new file mode 100644 index 0000000..fd0f750 --- /dev/null +++ b/examples/generate_phpdoc.py @@ -0,0 +1,331 @@ +#!/usr/bin/env python + +from pyvips import Image, Operation, GValue, Error, \ + ffi, values_for_enum, vips_lib, gobject_lib, \ + type_map, type_name, type_from_name, nickname_find + +# This file generates the phpdoc comments for the magic methods and properties. +# It's in Python, since we use the whole of FFI, not just the +# small bit exposed by php-vips-ext. + +# Regenerate docs with something like: +# +# cd src +# python ../examples/generate_phpdoc.py + +# this needs pyvips +# +# pip install --user pyvips + +# map a Python gtype to PHP argument type names +gtype_to_php_arg = { + GValue.gbool_type: 'bool', + GValue.gint_type: 'integer', + GValue.gdouble_type: 'float', + GValue.gstr_type: 'string', + GValue.refstr_type: 'string', + GValue.genum_type: 'string', + GValue.gflags_type: 'integer', + GValue.gobject_type: 'string', + GValue.image_type: 'Image', + GValue.array_int_type: 'integer[]|integer', + GValue.array_double_type: 'float[]|float', + GValue.array_image_type: 'Image[]|Image', + GValue.blob_type: 'string' +} + +# php result type names are different, annoyingly, and very restricted +gtype_to_php_result = { + GValue.gbool_type: 'bool', + GValue.gint_type: 'integer', + GValue.gdouble_type: 'float', + GValue.gstr_type: 'string', + GValue.refstr_type: 'string', + GValue.genum_type: 'string', + GValue.gflags_type: 'integer', + GValue.gobject_type: 'string', + GValue.image_type: 'Image', + GValue.array_int_type: 'array', + GValue.array_double_type: 'array', + GValue.array_image_type: 'array', + GValue.blob_type: 'string' +} + +# values for VipsArgumentFlags +_REQUIRED = 1 +_INPUT = 16 +_OUTPUT = 32 +_DEPRECATED = 64 +_MODIFY = 128 + +# for VipsOperationFlags +_OPERATION_DEPRECATED = 8 + + +def gtype_to_php(gtype, result=False): + """Map a gtype to PHP type name we use to represent it. + """ + + fundamental = gobject_lib.g_type_fundamental(gtype) + + gtype_map = gtype_to_php_result if result else gtype_to_php_arg + + if gtype in gtype_map: + return gtype_map[gtype] + if fundamental in gtype_map: + return gtype_map[fundamental] + return '' + + +def remove_prefix(enum_str): + prefix = 'Vips' + + if enum_str.startswith(prefix): + return enum_str[len(prefix):] + + return enum_str + + +def generate_operation(operation_name): + op = Operation.new_from_name(operation_name) + + # we are only interested in non-deprecated args + args = [[name, flags] for name, flags in op.get_args() + if not flags & _DEPRECATED] + + # find the first required input image arg, if any ... that will be self + member_x = None + for name, flags in args: + if ((flags & _INPUT) != 0 and + (flags & _REQUIRED) != 0 and + op.get_typeof(name) == GValue.image_type): + member_x = name + break + + required_input = [name for name, flags in args + if (flags & _INPUT) != 0 and + (flags & _REQUIRED) != 0 and + name != member_x] + + required_output = [name for name, flags in args + if ((flags & _OUTPUT) != 0 and + (flags & _REQUIRED) != 0) or + ((flags & _INPUT) != 0 and + (flags & _REQUIRED) != 0 and + (flags & _MODIFY) != 0)] + + result = ' * @method ' + if member_x is None: + result += 'static ' + if len(required_output) == 0: + result += 'void ' + elif len(required_output) == 1: + result += '{0} '.format(gtype_to_php(op.get_typeof(required_output[0]), True)) + else: + # we generate a Returns: block for this case, see below + result += 'array ' + + result += '{0}('.format(operation_name) + for name in required_input: + gtype = op.get_typeof(name) + result += '{0} ${1}, '.format(gtype_to_php(gtype), name) + + result += 'array $options = []) ' + + description = op.get_description() + result += description[0].upper() + description[1:] + '.\n' + + # find any Enums we've referenced and output @see lines for them + for name in required_output + required_input: + gtype = op.get_typeof(name) + fundamental = gobject_lib.g_type_fundamental(gtype) + + if fundamental != GValue.genum_type: + continue + + result += ' * @see {0} for possible values for ${1}\n'.format(remove_prefix(type_name(gtype)), name) + + if len(required_output) > 1: + result += ' * Return array with: [\n' + for name in required_output: + gtype = op.get_typeof(name) + blurb = op.get_blurb(name) + result += ' * \'{0}\' => @type {1} {2}\n'.format(name, gtype_to_php(gtype), + blurb[0].upper() + blurb[1:]) + result += ' * ];\n' + + result += ' * @throws Exception\n' + + return result + + +preamble = """ + * @copyright 2016 John Cupitt + * @license https://opensource.org/licenses/MIT MIT + * @link https://github.com/jcupitt/php-vips + */ +""" + +class_header = """ * @category Images + * @package Jcupitt\\Vips + * @author John Cupitt + * @copyright 2016 John Cupitt + * @license https://opensource.org/licenses/MIT MIT + * @link https://github.com/jcupitt/php-vips +""" + + +def generate_auto_doc(filename): + all_nicknames = [] + + def add_nickname(gtype, a, b): + nickname = nickname_find(gtype) + try: + # can fail for abstract types + op = Operation.new_from_name(nickname) + + # we are only interested in non-deprecated operations + if (op.get_flags() & _OPERATION_DEPRECATED) == 0: + all_nicknames.append(nickname) + except Error: + pass + + type_map(gtype, add_nickname) + + return ffi.NULL + + type_map(type_from_name('VipsOperation'), add_nickname) + + # add 'missing' synonyms by hand + all_nicknames.append('crop') + + # make list unique and sort + all_nicknames = list(set(all_nicknames)) + all_nicknames.sort() + + # these have hand-written methods, don't autodoc them + no_generate = [ + 'bandjoin', + 'bandrank', + 'ifthenelse', + 'add', + 'subtract', + 'multiply', + 'divide', + 'remainder' + ] + all_nicknames = [x for x in all_nicknames if x not in no_generate] + + print('Generating {0} ...'.format(filename)) + + with open(filename, 'w') as f: + f.write(preamble) + f.write('\n') + f.write('namespace Jcupitt\\Vips;\n') + f.write('\n') + f.write('/**\n') + f.write(' * Autodocs for the Image class.\n') + f.write(class_header) + f.write(' *\n') + + for nickname in all_nicknames: + f.write(generate_operation(nickname)) + + f.write(' *\n') + + # all magic properties + tmp_file = Image.new_temp_file('%s.v') + all_properties = tmp_file.get_fields() + for name in all_properties: + php_name = name.replace('-', '_') + gtype = tmp_file.get_typeof(name) + fundamental = gobject_lib.g_type_fundamental(gtype) + + f.write(' * @property {0} ${1} {2}\n'.format(gtype_to_php(gtype), php_name, tmp_file.get_blurb(name))) + + if fundamental == GValue.genum_type: + f.write(' * @see {0} for possible values\n'.format(remove_prefix(type_name(gtype)))) + + f.write(' */\n') + f.write('abstract class ImageAutodoc\n') + f.write('{\n') + f.write('}\n') + + +def generate_enums(): + # otherwise we're missing some enums + vips_lib.vips_token_get_type() + vips_lib.vips_saveable_get_type() + vips_lib.vips_image_type_get_type() + + all_enums = [] + + def add_enum(gtype, a, b): + nickname = type_name(gtype) + all_enums.append(nickname) + + type_map(gtype, add_enum) + + return ffi.NULL + + type_map(type_from_name('GEnum'), add_enum) + + for name in all_enums: + gtype = type_from_name(name) + php_name = remove_prefix(name) + + print('Generating {0}.php ...'.format(php_name)) + + with open('{0}.php'.format(php_name), 'w') as f: + f.write(preamble) + f.write('\n') + f.write('namespace Jcupitt\\Vips;\n') + f.write('\n') + f.write('/**\n') + f.write(' * The {0} enum.\n'.format(php_name)) + f.write(class_header) + f.write(' */\n') + f.write('abstract class {0}\n'.format(php_name)) + f.write('{\n') + + for value in values_for_enum(gtype): + php_name = value.replace('-', '_').upper() + f.write(' const {0} = \'{1}\';\n'.format(php_name, value)) + + f.write('}\n') + + +generate_auto_doc('ImageAutodoc.php') +generate_enums() diff --git a/examples/generate_phpdoc.rb b/examples/generate_phpdoc.rb deleted file mode 100755 index 3d7fc16..0000000 --- a/examples/generate_phpdoc.rb +++ /dev/null @@ -1,347 +0,0 @@ -#!/usr/bin/env ruby - -require 'vips' - -# This file generates the phpdoc comments for the magic methods and properties. -# It's in Ruby, since we use the whole of gobject-introspection, not just the -# small bit exposed by php-vips-ext. - -# Regenerate docs with something like: -# -# cd src -# ../examples/generate_phpdoc.rb - -# this needs version 1.x of ruby-vips -# -# gem install ruby-vips -v 1.0.6 - -# gobject-introspection 3.0.7 crashes a lot if it GCs while doing -# something -GC.disable - -Vips::init("") - -# these have hand-written methods, don't autodoc them -$no_generate = %w( - bandjoin - bandrank - ifthenelse - add - subtract - multiply - divide - remainder - extract_area -) - -# Find all the vips enums -$enums = [] -Vips.constants.each do |name| - const = Vips.const_get name - if const.respond_to? :superclass and - const.superclass == GLib::Enum - $enums << name.to_s - end -end - -def enum?(name) - return true if $enums.include?(name) - - # is there a "Vips" at the front of the name? remove it and try the - # enums again - trim = name.to_s.tap{|s| s.slice!("Vips")} - return true if $enums.include?(trim) - - # is there a "::" at the front of the name? remove it and try the - # enums again - trim.slice! "::" - return true if $enums.include?(trim) - - return false -end - -# map Ruby type names to PHP argument type names -$to_php_map = { - "Vips::Image" => "Image", - "Image" => "Image", - "Array" => "integer[]|integer", - "Array" => "float[]|float", - "Array" => "Image[]|image", - "Integer" => "integer", - "gint" => "integer", - "guint64" => "integer", - "Double" => "float", - "gdouble" => "float", - "Float" => "float", - "String" => "string", - "Boolean" => "bool", - "gboolean" => "bool", - "Vips::Blob" => "string", - "gchararray" => "string", - "gpointer" => "string", -} - -# php result type names are different, annoyingly, and very restricted -$to_php_result_map = { - "Vips::Image" => "Image", - "Image" => "Image", - "Array" => "array", - "Array" => "array", - "Array" => "array", - "Integer" => "integer", - "gint" => "integer", - "guint64" => "integer", - "Double" => "float", - "gdouble" => "float", - "Float" => "float", - "String" => "string", - "Boolean" => "bool", - "gboolean" => "bool", - "Vips::Blob" => "string", - "gchararray" => "string", - "gpointer" => "string", -} - -def type_to_php(map, type) - return map[type] if map.include?(type) - return "string" if enum? type - - # no mapping found - puts "no mapping found for #{type}" - return "" -end - -class Vips::Argument - def to_php - type_to_php $to_php_map, type - end - - def to_php_result - type_to_php $to_php_result_map, type - end -end - -def generate_operation(file, op) - flags = op.flags - return if (flags & :deprecated) != 0 - nickname = Vips::nickname_find op.gtype - - return if $no_generate.include? nickname - - all_args = op.get_args.select {|arg| not arg.isset} - - # separate args into various categories - - required_input = all_args.select do |arg| - (arg.flags & :input) != 0 and - (arg.flags & :required) != 0 - end - - optional_input = all_args.select do |arg| - (arg.flags & :input) != 0 and - (arg.flags & :required) == 0 - end - - required_output = all_args.select do |arg| - (arg.flags & :output) != 0 and - (arg.flags & :required) != 0 - end - - # required input args with :modify are copied and appended to - # output - modified_required_input = required_input.select do |arg| - (arg.flags & :modify) != 0 - end - required_output += modified_required_input - - optional_output = all_args.select do |arg| - (arg.flags & :output) != 0 and - (arg.flags & :required) == 0 - end - - # optional input args with :modify are copied and appended to - # output - modified_optional_input = optional_input.select do |arg| - (arg.flags & :modify) != 0 - end - optional_output += modified_optional_input - - # find the first input image, if any ... we will be a method of this - # instance - member_x = required_input.find do |x| - x.gtype.type_is_a? GLib::Type["VipsImage"] - end - if member_x != nil - required_input.delete member_x - end - - file << " * @method " - file << "static " if not member_x - if required_output.length == 0 - file << "void " - elsif required_output.length == 1 - file << "#{required_output[0].to_php_result} " - else - # we generate a Returns: block for this case, see below - file << "array " - end - - file << "#{nickname}(" - - required_input.each do |arg| - file << "#{arg.to_php} $#{arg.name}, " - end - file << "array $options = []) " - - file << "#{op.description.capitalize}.\n" - - # find any Enums we've referenced and output @see lines for them - used_enums = [] - (required_output + required_input).each do |arg| - next if not enum? arg.type - file << " * @see #{arg.type} for possible values for $#{arg.name}\n" - end - - if required_output.length > 1 - file << " * Return array with: [\n" - required_output.each do |arg| - file << " * '#{arg.name}' => " - file << "@type #{arg.to_php} #{arg.blurb.capitalize}.\n" - end - file << " * ];\n" - end - - file << " * @throws Exception\n" -end - -def generate_class(file, gtype) - begin - # can fail for abstract types - # can't find a way to get to #abstract? from a gtype - op = Vips::Operation.new gtype.name - rescue - op = nil - end - - generate_operation(file, op) if op - - gtype.children.each {|x| generate_class file, x} -end - -preamble = < - * @copyright 2016 John Cupitt - * @license https://opensource.org/licenses/MIT MIT - * @link https://github.com/jcupitt/php-vips - */ -EOF - -class_header = < - * @copyright 2016 John Cupitt - * @license https://opensource.org/licenses/MIT MIT - * @link https://github.com/jcupitt/php-vips -EOF - -# The image class is the most complex -puts "Generating ImageAutodoc.php ..." -File.open("ImageAutodoc.php", "w") do |file| - file << preamble - file << "\n" - file << "namespace Jcupitt\\Vips;\n" - - file << "\n" - file << "/**\n" - file << " * Autodocs for the Image class.\n" - file << class_header - file << " *\n" - - generate_class file, GLib::Type["VipsOperation"] - -# extract_area is in there twice, once as "crop" ... do them by hand - file << < @type Image Sums of columns. - * 'rows' => @type Image Sums of rows. - * ]; + * @method Image boolean(Image $right, string $boolean, array $options = []) Boolean operation on two images. + * @see OperationBoolean for possible values for $boolean * @throws Exception - * @method array profile(array $options = []) Find image profiles. - * Return array with: [ - * 'columns' => @type Image First non-zero pixel in column. - * 'rows' => @type Image First non-zero pixel in row. - * ]; + * @method Image boolean_const(string $boolean, float[]|float $c, array $options = []) Boolean operations against a constant. + * @see OperationBoolean for possible values for $boolean * @throws Exception - * @method Image measure(integer $h, integer $v, array $options = []) Measure a set of patches on a colour chart. + * @method Image buildlut(array $options = []) Build a look-up table. * @throws Exception - * @method array getpoint(integer $x, integer $y, array $options = []) Read a point from an image. + * @method Image byteswap(array $options = []) Byteswap an image. * @throws Exception - * @method array find_trim(array $options = []) Search an image for non-edge areas. - * Return array with: [ - * 'left' => @type integer Left edge of image. - * 'top' => @type integer Top edge of extract area. - * 'width' => @type integer Width of extract area. - * 'height' => @type integer Height of extract area. - * ]; + * @method Image cache(array $options = []) Cache an image. * @throws Exception - * @method Image copy(array $options = []) Copy an image. + * @method Image cast(string $format, array $options = []) Cast an image. + * @see BandFormat for possible values for $format * @throws Exception - * @method Image tilecache(array $options = []) Cache an image as a set of tiles. + * @method Image colourspace(string $space, array $options = []) Convert to a new colorspace. + * @see Interpretation for possible values for $space * @throws Exception - * @method Image linecache(array $options = []) Cache an image as a set of lines. + * @method Image compass(Image $mask, array $options = []) Convolve with rotating mask. * @throws Exception - * @method Image sequential(array $options = []) Check sequential access. + * @method Image complex(string $cmplx, array $options = []) Perform a complex operation on an image. + * @see OperationComplex for possible values for $cmplx * @throws Exception - * @method Image cache(array $options = []) Cache an image. + * @method Image complex2(Image $right, string $cmplx, array $options = []) Complex binary operations on two images. + * @see OperationComplex2 for possible values for $cmplx * @throws Exception - * @method Image embed(integer $x, integer $y, integer $width, integer $height, array $options = []) Embed an image in a larger image. + * @method Image complexform(Image $right, array $options = []) Form a complex image from two real images. * @throws Exception - * @method Image gravity(string $direction, integer $width, integer $height, array $options = []) Place an image within a larger image with a certain gravity. - * @see CompassDirection for possible values for $direction + * @method Image complexget(string $get, array $options = []) Get a component from a complex image. + * @see OperationComplexget for possible values for $get * @throws Exception - * @method Image flip(string $direction, array $options = []) Flip an image. - * @see Direction for possible values for $direction + * @method static Image composite(Image[]|Image $in, integer[]|integer $mode, array $options = []) Blend an array of images with an array of blend modes. * @throws Exception - * @method Image insert(Image $sub, integer $x, integer $y, array $options = []) Insert image @sub into @main at @x, @y. + * @method Image composite2(Image $overlay, string $mode, array $options = []) Blend a pair of images with a blend mode. + * @see BlendMode for possible values for $mode * @throws Exception - * @method Image join(Image $in2, string $direction, array $options = []) Join a pair of images. - * @see Direction for possible values for $direction + * @method Image conv(Image $mask, array $options = []) Convolution operation. * @throws Exception - * @method static Image arrayjoin(Image[]|image $in, array $options = []) Join an array of images. + * @method Image conva(Image $mask, array $options = []) Approximate integer convolution. * @throws Exception - * @method Image smartcrop(integer $width, integer $height, array $options = []) Extract an area from an image. + * @method Image convasep(Image $mask, array $options = []) Approximate separable integer convolution. * @throws Exception - * @method Image extract_band(integer $band, array $options = []) Extract band from an image. + * @method Image convf(Image $mask, array $options = []) Float convolution operation. * @throws Exception - * @method Image bandjoin_const(float[]|float $c, array $options = []) Append a constant band to an image. + * @method Image convi(Image $mask, array $options = []) Int convolution operation. * @throws Exception - * @method Image bandmean(array $options = []) Band-wise average. + * @method Image convsep(Image $mask, array $options = []) Seperable convolution operation. * @throws Exception - * @method Image bandbool(string $boolean, array $options = []) Boolean operation across image bands. - * @see OperationBoolean for possible values for $boolean + * @method Image copy(array $options = []) Copy an image. * @throws Exception - * @method Image replicate(integer $across, integer $down, array $options = []) Replicate an image. + * @method float countlines(string $direction, array $options = []) Count lines in an image. + * @see Direction for possible values for $direction * @throws Exception - * @method Image cast(string $format, array $options = []) Cast an image. - * @see BandFormat for possible values for $format + * @method Image crop(integer $left, integer $top, integer $width, integer $height, array $options = []) Extract an area from an image. * @throws Exception - * @method Image rot(string $angle, array $options = []) Rotate an image. - * @see Angle for possible values for $angle + * @method static Image csvload(string $filename, array $options = []) Load csv from file. * @throws Exception - * @method Image rot45(array $options = []) Rotate an image. + * @method void csvsave(string $filename, array $options = []) Save image to csv file. * @throws Exception - * @method Image autorot(array $options = []) Autorotate image by exif tag. + * @method Image dE00(Image $right, array $options = []) Calculate dE00. * @throws Exception - * @method Image recomb(Image $m, array $options = []) Linear recombination with matrix. + * @method Image dE76(Image $right, array $options = []) Calculate dE76. * @throws Exception - * @method Image bandfold(array $options = []) Fold up x axis into bands. + * @method Image dECMC(Image $right, array $options = []) Calculate dECMC. * @throws Exception - * @method Image bandunfold(array $options = []) Unfold image bands into x axis. + * @method float deviate(array $options = []) Find image standard deviation. * @throws Exception - * @method Image flatten(array $options = []) Flatten alpha out of an image. + * @method Image draw_circle(float[]|float $ink, integer $cx, integer $cy, integer $radius, array $options = []) Draw a circle on an image. * @throws Exception - * @method Image premultiply(array $options = []) Premultiply image alpha. + * @method Image draw_flood(float[]|float $ink, integer $x, integer $y, array $options = []) Flood-fill an area. * @throws Exception - * @method Image unpremultiply(array $options = []) Unpremultiply image alpha. + * @method Image draw_image(Image $sub, integer $x, integer $y, array $options = []) Paint an image into another image. * @throws Exception - * @method Image grid(integer $tile_height, integer $across, integer $down, array $options = []) Grid an image. + * @method Image draw_line(float[]|float $ink, integer $x1, integer $y1, integer $x2, integer $y2, array $options = []) Draw a line on an image. * @throws Exception - * @method Image scale(array $options = []) Scale an image to uchar. + * @method Image draw_mask(float[]|float $ink, Image $mask, integer $x, integer $y, array $options = []) Draw a mask on an image. * @throws Exception - * @method Image wrap(array $options = []) Wrap image origin. + * @method Image draw_rect(float[]|float $ink, integer $left, integer $top, integer $width, integer $height, array $options = []) Paint a rectangle on an image. * @throws Exception - * @method Image zoom(integer $xfac, integer $yfac, array $options = []) Zoom an image. + * @method Image draw_smudge(integer $left, integer $top, integer $width, integer $height, array $options = []) Blur a rectangle on an image. * @throws Exception - * @method Image subsample(integer $xfac, integer $yfac, array $options = []) Subsample an image. + * @method void dzsave(string $filename, array $options = []) Save image to deepzoom file. * @throws Exception - * @method Image msb(array $options = []) Pick most-significant byte from an image. + * @method string dzsave_buffer(array $options = []) Save image to dz buffer. * @throws Exception - * @method Image byteswap(array $options = []) Byteswap an image. + * @method Image embed(integer $x, integer $y, integer $width, integer $height, array $options = []) Embed an image in a larger image. * @throws Exception - * @method Image falsecolour(array $options = []) False-colour an image. + * @method Image extract_area(integer $left, integer $top, integer $width, integer $height, array $options = []) Extract an area from an image. * @throws Exception - * @method Image gamma(array $options = []) Gamma an image. + * @method Image extract_band(integer $band, array $options = []) Extract band from an image. * @throws Exception - * @method static Image composite(Image[]|image $in, integer[]|integer $mode, array $options = []) Blend an array of images with an array of blend modes. + * @method static Image eye(integer $width, integer $height, array $options = []) Make an image showing the eye's spatial response. * @throws Exception - * @method Image composite2(Image $overlay, string $mode, array $options = []) Blend a pair of images with a blend mode. - * @see BlendMode for possible values for $mode + * @method Image falsecolour(array $options = []) False-color an image. * @throws Exception - * @method static Image black(integer $width, integer $height, array $options = []) Make a black image. + * @method Image fastcor(Image $ref, array $options = []) Fast correlation. * @throws Exception - * @method static Image gaussnoise(integer $width, integer $height, array $options = []) Make a gaussnoise image. + * @method Image fill_nearest(array $options = []) Fill image zeros with nearest non-zero pixel. * @throws Exception - * @method static Image text(string $text, array $options = []) Make a text image. + * @method array find_trim(array $options = []) Search an image for non-edge areas. + * Return array with: [ + * 'left' => @type integer Left edge of image + * 'top' => @type integer Top edge of extract area + * 'width' => @type integer Width of extract area + * 'height' => @type integer Height of extract area + * ]; * @throws Exception - * @method static Image xyz(integer $width, integer $height, array $options = []) Make an image where pixel values are coordinates. + * @method static Image fitsload(string $filename, array $options = []) Load a FITS image. * @throws Exception - * @method static Image gaussmat(float $sigma, float $min_ampl, array $options = []) Make a gaussian image. + * @method void fitssave(string $filename, array $options = []) Save image to fits file. * @throws Exception - * @method static Image logmat(float $sigma, float $min_ampl, array $options = []) Make a laplacian of gaussian image. + * @method Image flatten(array $options = []) Flatten alpha out of an image. * @throws Exception - * @method static Image eye(integer $width, integer $height, array $options = []) Make an image showing the eye's spatial response. + * @method Image flip(string $direction, array $options = []) Flip an image. + * @see Direction for possible values for $direction * @throws Exception - * @method static Image grey(integer $width, integer $height, array $options = []) Make a grey ramp image. + * @method Image float2rad(array $options = []) Transform float RGB to Radiance coding. * @throws Exception - * @method static Image zone(integer $width, integer $height, array $options = []) Make a zone plate. + * @method static Image fractsurf(integer $width, integer $height, float $fractal_dimension, array $options = []) Make a fractal surface. * @throws Exception - * @method static Image sines(integer $width, integer $height, array $options = []) Make a 2d sine wave. + * @method Image freqmult(Image $mask, array $options = []) Frequency-domain filtering. * @throws Exception - * @method static Image mask_ideal(integer $width, integer $height, float $frequency_cutoff, array $options = []) Make an ideal filter. + * @method Image fwfft(array $options = []) Forward FFT. * @throws Exception - * @method static Image mask_ideal_ring(integer $width, integer $height, float $frequency_cutoff, float $ringwidth, array $options = []) Make an ideal ring filter. + * @method Image gamma(array $options = []) Gamma an image. * @throws Exception - * @method static Image mask_ideal_band(integer $width, integer $height, float $frequency_cutoff_x, float $frequency_cutoff_y, float $radius, array $options = []) Make an ideal band filter. + * @method Image gaussblur(float $sigma, array $options = []) Gaussian blur. * @throws Exception - * @method static Image mask_butterworth(integer $width, integer $height, float $order, float $frequency_cutoff, float $amplitude_cutoff, array $options = []) Make a butterworth filter. + * @method static Image gaussmat(float $sigma, float $min_ampl, array $options = []) Make a gaussian image. * @throws Exception - * @method static Image mask_butterworth_ring(integer $width, integer $height, float $order, float $frequency_cutoff, float $amplitude_cutoff, float $ringwidth, array $options = []) Make a butterworth ring filter. + * @method static Image gaussnoise(integer $width, integer $height, array $options = []) Make a gaussnoise image. * @throws Exception - * @method static Image mask_butterworth_band(integer $width, integer $height, float $order, float $frequency_cutoff_x, float $frequency_cutoff_y, float $radius, float $amplitude_cutoff, array $options = []) Make a butterworth_band filter. + * @method array getpoint(integer $x, integer $y, array $options = []) Read a point from an image. * @throws Exception - * @method static Image mask_gaussian(integer $width, integer $height, float $frequency_cutoff, float $amplitude_cutoff, array $options = []) Make a gaussian filter. + * @method static Image gifload(string $filename, array $options = []) Load GIF with giflib. * @throws Exception - * @method static Image mask_gaussian_ring(integer $width, integer $height, float $frequency_cutoff, float $amplitude_cutoff, float $ringwidth, array $options = []) Make a gaussian ring filter. + * @method static Image gifload_buffer(string $buffer, array $options = []) Load GIF with giflib. * @throws Exception - * @method static Image mask_gaussian_band(integer $width, integer $height, float $frequency_cutoff_x, float $frequency_cutoff_y, float $radius, float $amplitude_cutoff, array $options = []) Make a gaussian filter. + * @method Image globalbalance(array $options = []) Global balance an image mosaic. * @throws Exception - * @method static Image mask_fractal(integer $width, integer $height, float $fractal_dimension, array $options = []) Make fractal filter. + * @method Image gravity(string $direction, integer $width, integer $height, array $options = []) Place an image within a larger image with a certain gravity. + * @see CompassDirection for possible values for $direction * @throws Exception - * @method Image buildlut(array $options = []) Build a look-up table. + * @method static Image grey(integer $width, integer $height, array $options = []) Make a grey ramp image. * @throws Exception - * @method Image invertlut(array $options = []) Build an inverted look-up table. + * @method Image grid(integer $tile_height, integer $across, integer $down, array $options = []) Grid an image. * @throws Exception - * @method static Image tonelut(array $options = []) Build a look-up table. + * @method Image hist_cum(array $options = []) Form cumulative histogram. * @throws Exception - * @method static Image identity(array $options = []) Make a 1d image where pixel values are indexes. + * @method float hist_entropy(array $options = []) Estimate image entropy. * @throws Exception - * @method static Image fractsurf(integer $width, integer $height, float $fractal_dimension, array $options = []) Make a fractal surface. + * @method Image hist_equal(array $options = []) Histogram equalisation. * @throws Exception - * @method static Image worley(integer $width, integer $height, array $options = []) Make a worley noise image. + * @method Image hist_find(array $options = []) Find image histogram. * @throws Exception - * @method static Image perlin(integer $width, integer $height, array $options = []) Make a perlin noise image. + * @method Image hist_find_indexed(Image $index, array $options = []) Find indexed image histogram. * @throws Exception - * @method static Image csvload(string $filename, array $options = []) Load csv from file. + * @method Image hist_find_ndim(array $options = []) Find n-dimensional image histogram. * @throws Exception - * @method static Image matrixload(string $filename, array $options = []) Load matrix from file. + * @method bool hist_ismonotonic(array $options = []) Test for monotonicity. * @throws Exception - * @method static Image rawload(string $filename, integer $width, integer $height, integer $bands, array $options = []) Load raw data from a file. + * @method Image hist_local(integer $width, integer $height, array $options = []) Local histogram equalisation. * @throws Exception - * @method static Image vipsload(string $filename, array $options = []) Load vips from file. + * @method Image hist_match(Image $ref, array $options = []) Match two histograms. * @throws Exception - * @method static Image analyzeload(string $filename, array $options = []) Load an analyze6 image. + * @method Image hist_norm(array $options = []) Normalise histogram. * @throws Exception - * @method static Image ppmload(string $filename, array $options = []) Load ppm from file. + * @method Image hist_plot(array $options = []) Plot histogram. * @throws Exception - * @method static Image radload(string $filename, array $options = []) Load a radiance image from a file. + * @method Image hough_circle(array $options = []) Find hough circle transform. * @throws Exception - * @method static Image pdfload(string $filename, array $options = []) Load pdf with libpoppler. + * @method Image hough_line(array $options = []) Find hough line transform. * @throws Exception - * @method static Image pdfload_buffer(string $buffer, array $options = []) Load pdf with libpoppler. + * @method Image icc_export(array $options = []) Output to device with ICC profile. * @throws Exception - * @method static Image svgload(string $filename, array $options = []) Load svg with rsvg. + * @method Image icc_import(array $options = []) Import from device with ICC profile. * @throws Exception - * @method static Image svgload_buffer(string $buffer, array $options = []) Load svg with rsvg. + * @method Image icc_transform(string $output_profile, array $options = []) Transform between devices with ICC profiles. * @throws Exception - * @method static Image gifload(string $filename, array $options = []) Load gif with giflib. + * @method static Image identity(array $options = []) Make a 1D image where pixel values are indexes. * @throws Exception - * @method static Image gifload_buffer(string $buffer, array $options = []) Load gif with giflib. + * @method Image insert(Image $sub, integer $x, integer $y, array $options = []) Insert image @sub into @main at @x, @y. * @throws Exception - * @method static Image pngload(string $filename, array $options = []) Load png from file. + * @method Image invert(array $options = []) Invert an image. * @throws Exception - * @method static Image pngload_buffer(string $buffer, array $options = []) Load png from buffer. + * @method Image invertlut(array $options = []) Build an inverted look-up table. * @throws Exception - * @method static Image matload(string $filename, array $options = []) Load mat from file. + * @method Image invfft(array $options = []) Inverse FFT. + * @throws Exception + * @method Image join(Image $in2, string $direction, array $options = []) Join a pair of images. + * @see Direction for possible values for $direction * @throws Exception * @method static Image jpegload(string $filename, array $options = []) Load jpeg from file. * @throws Exception * @method static Image jpegload_buffer(string $buffer, array $options = []) Load jpeg from buffer. * @throws Exception - * @method static Image webpload(string $filename, array $options = []) Load webp from file. + * @method void jpegsave(string $filename, array $options = []) Save image to jpeg file. * @throws Exception - * @method static Image webpload_buffer(string $buffer, array $options = []) Load webp from buffer. + * @method string jpegsave_buffer(array $options = []) Save image to jpeg buffer. * @throws Exception - * @method static Image tiffload(string $filename, array $options = []) Load tiff from file. + * @method void jpegsave_mime(array $options = []) Save image to jpeg mime. * @throws Exception - * @method static Image tiffload_buffer(string $buffer, array $options = []) Load tiff from buffer. + * @method Image labelregions(array $options = []) Label regions in an image. * @throws Exception - * @method static Image openslideload(string $filename, array $options = []) Load file with openslide. + * @method Image linear(float[]|float $a, float[]|float $b, array $options = []) Calculate (a * in + b). * @throws Exception - * @method static Image magickload(string $filename, array $options = []) Load file with imagemagick. + * @method Image linecache(array $options = []) Cache an image as a set of lines. * @throws Exception - * @method static Image magickload_buffer(string $buffer, array $options = []) Load buffer with imagemagick. + * @method static Image logmat(float $sigma, float $min_ampl, array $options = []) Make a laplacian of gaussian image. * @throws Exception - * @method static Image fitsload(string $filename, array $options = []) Load a fits image. + * @method static Image magickload(string $filename, array $options = []) Load file with ImageMagick. * @throws Exception - * @method static Image openexrload(string $filename, array $options = []) Load an openexr image. + * @method static Image magickload_buffer(string $buffer, array $options = []) Load buffer with ImageMagick. * @throws Exception - * @method void csvsave(string $filename, array $options = []) Save image to csv file. + * @method Image mapim(Image $index, array $options = []) Resample with an mapim image. * @throws Exception - * @method void matrixsave(string $filename, array $options = []) Save image to matrix file. + * @method Image maplut(Image $lut, array $options = []) Map an image though a lut. * @throws Exception - * @method void matrixprint(array $options = []) Print matrix. + * @method static Image mask_butterworth(integer $width, integer $height, float $order, float $frequency_cutoff, float $amplitude_cutoff, array $options = []) Make a butterworth filter. * @throws Exception - * @method void rawsave(string $filename, array $options = []) Save image to raw file. + * @method static Image mask_butterworth_band(integer $width, integer $height, float $order, float $frequency_cutoff_x, float $frequency_cutoff_y, float $radius, float $amplitude_cutoff, array $options = []) Make a butterworth_band filter. * @throws Exception - * @method void rawsave_fd(integer $fd, array $options = []) Write raw image to file descriptor. + * @method static Image mask_butterworth_ring(integer $width, integer $height, float $order, float $frequency_cutoff, float $amplitude_cutoff, float $ringwidth, array $options = []) Make a butterworth ring filter. * @throws Exception - * @method void vipssave(string $filename, array $options = []) Save image to vips file. + * @method static Image mask_fractal(integer $width, integer $height, float $fractal_dimension, array $options = []) Make fractal filter. * @throws Exception - * @method void ppmsave(string $filename, array $options = []) Save image to ppm file. + * @method static Image mask_gaussian(integer $width, integer $height, float $frequency_cutoff, float $amplitude_cutoff, array $options = []) Make a gaussian filter. * @throws Exception - * @method void radsave(string $filename, array $options = []) Save image to radiance file. + * @method static Image mask_gaussian_band(integer $width, integer $height, float $frequency_cutoff_x, float $frequency_cutoff_y, float $radius, float $amplitude_cutoff, array $options = []) Make a gaussian filter. * @throws Exception - * @method string radsave_buffer(array $options = []) Save image to radiance buffer. + * @method static Image mask_gaussian_ring(integer $width, integer $height, float $frequency_cutoff, float $amplitude_cutoff, float $ringwidth, array $options = []) Make a gaussian ring filter. * @throws Exception - * @method void dzsave(string $filename, array $options = []) Save image to deepzoom file. + * @method static Image mask_ideal(integer $width, integer $height, float $frequency_cutoff, array $options = []) Make an ideal filter. * @throws Exception - * @method string dzsave_buffer(array $options = []) Save image to dz buffer. + * @method static Image mask_ideal_band(integer $width, integer $height, float $frequency_cutoff_x, float $frequency_cutoff_y, float $radius, array $options = []) Make an ideal band filter. * @throws Exception - * @method void pngsave(string $filename, array $options = []) Save image to png file. + * @method static Image mask_ideal_ring(integer $width, integer $height, float $frequency_cutoff, float $ringwidth, array $options = []) Make an ideal ring filter. * @throws Exception - * @method string pngsave_buffer(array $options = []) Save image to png buffer. + * @method Image match(Image $sec, integer $xr1, integer $yr1, integer $xs1, integer $ys1, integer $xr2, integer $yr2, integer $xs2, integer $ys2, array $options = []) First-order match of two images. * @throws Exception - * @method void jpegsave(string $filename, array $options = []) Save image to jpeg file. + * @method Image math(string $math, array $options = []) Apply a math operation to an image. + * @see OperationMath for possible values for $math * @throws Exception - * @method string jpegsave_buffer(array $options = []) Save image to jpeg buffer. + * @method Image math2(Image $right, string $math2, array $options = []) Binary math operations. + * @see OperationMath2 for possible values for $math2 * @throws Exception - * @method void jpegsave_mime(array $options = []) Save image to jpeg mime. + * @method Image math2_const(string $math2, float[]|float $c, array $options = []) Binary math operations with a constant. + * @see OperationMath2 for possible values for $math2 * @throws Exception - * @method void webpsave(string $filename, array $options = []) Save image to webp file. + * @method static Image matload(string $filename, array $options = []) Load mat from file. * @throws Exception - * @method string webpsave_buffer(array $options = []) Save image to webp buffer. + * @method static Image matrixload(string $filename, array $options = []) Load matrix from file. * @throws Exception - * @method void tiffsave(string $filename, array $options = []) Save image to tiff file. + * @method void matrixprint(array $options = []) Print matrix. * @throws Exception - * @method string tiffsave_buffer(array $options = []) Save image to tiff buffer. + * @method void matrixsave(string $filename, array $options = []) Save image to matrix file. * @throws Exception - * @method void fitssave(string $filename, array $options = []) Save image to fits file. + * @method float max(array $options = []) Find image maximum. * @throws Exception - * @method static Image thumbnail(string $filename, integer $width, array $options = []) Generate thumbnail from file. + * @method Image measure(integer $h, integer $v, array $options = []) Measure a set of patches on a color chart. * @throws Exception - * @method static Image thumbnail_buffer(string $buffer, integer $width, array $options = []) Generate thumbnail from buffer. + * @method Image merge(Image $sec, string $direction, integer $dx, integer $dy, array $options = []) Merge two images. + * @see Direction for possible values for $direction * @throws Exception - * @method Image thumbnail_image(integer $width, array $options = []) Generate thumbnail from image. + * @method float min(array $options = []) Find image minimum. * @throws Exception - * @method Image mapim(Image $index, array $options = []) Resample with an mapim image. + * @method Image morph(Image $mask, string $morph, array $options = []) Morphology operation. + * @see OperationMorphology for possible values for $morph * @throws Exception - * @method Image shrink(float $hshrink, float $vshrink, array $options = []) Shrink an image. + * @method Image mosaic(Image $sec, string $direction, integer $xref, integer $yref, integer $xsec, integer $ysec, array $options = []) Mosaic two images. + * @see Direction for possible values for $direction * @throws Exception - * @method Image shrinkh(integer $hshrink, array $options = []) Shrink an image horizontally. + * @method Image mosaic1(Image $sec, string $direction, integer $xr1, integer $yr1, integer $xs1, integer $ys1, integer $xr2, integer $yr2, integer $xs2, integer $ys2, array $options = []) First-order mosaic of two images. + * @see Direction for possible values for $direction * @throws Exception - * @method Image shrinkv(integer $vshrink, array $options = []) Shrink an image vertically. + * @method Image msb(array $options = []) Pick most-significant byte from an image. * @throws Exception - * @method Image reduceh(float $hshrink, array $options = []) Shrink an image horizontally. + * @method static Image openexrload(string $filename, array $options = []) Load an OpenEXR image. * @throws Exception - * @method Image reducev(float $vshrink, array $options = []) Shrink an image vertically. + * @method static Image openslideload(string $filename, array $options = []) Load file with OpenSlide. * @throws Exception - * @method Image reduce(float $hshrink, float $vshrink, array $options = []) Reduce an image. + * @method static Image pdfload(string $filename, array $options = []) Load PDF with libpoppler. * @throws Exception - * @method Image quadratic(Image $coeff, array $options = []) Resample an image with a quadratic transform. + * @method static Image pdfload_buffer(string $buffer, array $options = []) Load PDF with libpoppler. * @throws Exception - * @method Image affine(float[]|float $matrix, array $options = []) Affine transform of an image. + * @method integer percent(float $percent, array $options = []) Find threshold for percent of pixels. * @throws Exception - * @method Image similarity(array $options = []) Similarity transform of an image. + * @method static Image perlin(integer $width, integer $height, array $options = []) Make a perlin noise image. * @throws Exception - * @method Image resize(float $scale, array $options = []) Resize an image. + * @method Image phasecor(Image $in2, array $options = []) Calculate phase correlation. * @throws Exception - * @method Image colourspace(string $space, array $options = []) Convert to a new colourspace. - * @see Interpretation for possible values for $space + * @method static Image pngload(string $filename, array $options = []) Load png from file. + * @throws Exception + * @method static Image pngload_buffer(string $buffer, array $options = []) Load png from buffer. * @throws Exception - * @method Image Lab2XYZ(array $options = []) Transform cielab to xyz. + * @method void pngsave(string $filename, array $options = []) Save image to png file. * @throws Exception - * @method Image XYZ2Lab(array $options = []) Transform xyz to lab. + * @method string pngsave_buffer(array $options = []) Save image to png buffer. * @throws Exception - * @method Image Lab2LCh(array $options = []) Transform lab to lch. + * @method static Image ppmload(string $filename, array $options = []) Load ppm from file. * @throws Exception - * @method Image LCh2Lab(array $options = []) Transform lch to lab. + * @method void ppmsave(string $filename, array $options = []) Save image to ppm file. * @throws Exception - * @method Image LCh2CMC(array $options = []) Transform lch to cmc. + * @method Image premultiply(array $options = []) Premultiply image alpha. * @throws Exception - * @method Image CMC2LCh(array $options = []) Transform lch to cmc. + * @method array profile(array $options = []) Find image profiles. + * Return array with: [ + * 'columns' => @type Image First non-zero pixel in column + * 'rows' => @type Image First non-zero pixel in row + * ]; * @throws Exception - * @method Image XYZ2Yxy(array $options = []) Transform xyz to yxy. + * @method array project(array $options = []) Find image projections. + * Return array with: [ + * 'columns' => @type Image Sums of columns + * 'rows' => @type Image Sums of rows + * ]; * @throws Exception - * @method Image Yxy2XYZ(array $options = []) Transform yxy to xyz. + * @method Image quadratic(Image $coeff, array $options = []) Resample an image with a quadratic transform. * @throws Exception - * @method Image scRGB2XYZ(array $options = []) Transform scrgb to xyz. + * @method Image rad2float(array $options = []) Unpack Radiance coding to float RGB. * @throws Exception - * @method Image XYZ2scRGB(array $options = []) Transform xyz to scrgb. + * @method static Image radload(string $filename, array $options = []) Load a Radiance image from a file. * @throws Exception - * @method Image LabQ2Lab(array $options = []) Unpack a labq image to float lab. + * @method void radsave(string $filename, array $options = []) Save image to Radiance file. * @throws Exception - * @method Image Lab2LabQ(array $options = []) Transform float lab to labq coding. + * @method string radsave_buffer(array $options = []) Save image to Radiance buffer. * @throws Exception - * @method Image LabQ2LabS(array $options = []) Unpack a labq image to short lab. + * @method Image rank(integer $width, integer $height, integer $index, array $options = []) Rank filter. * @throws Exception - * @method Image LabS2LabQ(array $options = []) Transform short lab to labq coding. + * @method static Image rawload(string $filename, integer $width, integer $height, integer $bands, array $options = []) Load raw data from a file. * @throws Exception - * @method Image LabS2Lab(array $options = []) Transform signed short lab to float. + * @method void rawsave(string $filename, array $options = []) Save image to raw file. * @throws Exception - * @method Image Lab2LabS(array $options = []) Transform float lab to signed short. + * @method void rawsave_fd(integer $fd, array $options = []) Write raw image to file descriptor. * @throws Exception - * @method Image rad2float(array $options = []) Unpack radiance coding to float rgb. + * @method Image recomb(Image $m, array $options = []) Linear recombination with matrix. * @throws Exception - * @method Image float2rad(array $options = []) Transform float rgb to radiance coding. + * @method Image reduce(float $hshrink, float $vshrink, array $options = []) Reduce an image. * @throws Exception - * @method Image LabQ2sRGB(array $options = []) Convert a labq image to srgb. + * @method Image reduceh(float $hshrink, array $options = []) Shrink an image horizontally. * @throws Exception - * @method Image sRGB2HSV(array $options = []) Transform srgb to hsv. + * @method Image reducev(float $vshrink, array $options = []) Shrink an image vertically. * @throws Exception - * @method Image HSV2sRGB(array $options = []) Transform hsv to srgb. + * @method Image relational(Image $right, string $relational, array $options = []) Relational operation on two images. + * @see OperationRelational for possible values for $relational * @throws Exception - * @method Image icc_import(array $options = []) Import from device with icc profile. + * @method Image relational_const(string $relational, float[]|float $c, array $options = []) Relational operations against a constant. + * @see OperationRelational for possible values for $relational * @throws Exception - * @method Image icc_export(array $options = []) Output to device with icc profile. + * @method Image remainder_const(float[]|float $c, array $options = []) Remainder after integer division of an image and a constant. * @throws Exception - * @method Image icc_transform(string $output_profile, array $options = []) Transform between devices with icc profiles. + * @method Image replicate(integer $across, integer $down, array $options = []) Replicate an image. * @throws Exception - * @method Image dE76(Image $right, array $options = []) Calculate de76. + * @method Image resize(float $scale, array $options = []) Resize an image. * @throws Exception - * @method Image dE00(Image $right, array $options = []) Calculate de00. + * @method Image rot(string $angle, array $options = []) Rotate an image. + * @see Angle for possible values for $angle * @throws Exception - * @method Image dECMC(Image $right, array $options = []) Calculate decmc. + * @method Image rot45(array $options = []) Rotate an image. * @throws Exception - * @method Image sRGB2scRGB(array $options = []) Convert an srgb image to scrgb. + * @method Image round(string $round, array $options = []) Perform a round function on an image. + * @see OperationRound for possible values for $round * @throws Exception - * @method Image scRGB2BW(array $options = []) Convert scrgb to bw. + * @method Image sRGB2HSV(array $options = []) Transform sRGB to HSV. * @throws Exception - * @method Image scRGB2sRGB(array $options = []) Convert an scrgb image to srgb. + * @method Image sRGB2scRGB(array $options = []) Convert an sRGB image to scRGB. * @throws Exception - * @method Image maplut(Image $lut, array $options = []) Map an image though a lut. + * @method Image scRGB2BW(array $options = []) Convert scRGB to BW. * @throws Exception - * @method integer percent(float $percent, array $options = []) Find threshold for percent of pixels. + * @method Image scRGB2XYZ(array $options = []) Transform scRGB to XYZ. * @throws Exception - * @method Image stdif(integer $width, integer $height, array $options = []) Statistical difference. + * @method Image scRGB2sRGB(array $options = []) Convert an scRGB image to sRGB. * @throws Exception - * @method Image hist_cum(array $options = []) Form cumulative histogram. + * @method Image scale(array $options = []) Scale an image to uchar. * @throws Exception - * @method Image hist_match(Image $ref, array $options = []) Match two histograms. + * @method Image sequential(array $options = []) Check sequential access. * @throws Exception - * @method Image hist_norm(array $options = []) Normalise histogram. + * @method Image sharpen(array $options = []) Unsharp masking for print. * @throws Exception - * @method Image hist_equal(array $options = []) Histogram equalisation. + * @method Image shrink(float $hshrink, float $vshrink, array $options = []) Shrink an image. * @throws Exception - * @method Image hist_plot(array $options = []) Plot histogram. + * @method Image shrinkh(integer $hshrink, array $options = []) Shrink an image horizontally. * @throws Exception - * @method Image hist_local(integer $width, integer $height, array $options = []) Local histogram equalisation. + * @method Image shrinkv(integer $vshrink, array $options = []) Shrink an image vertically. * @throws Exception - * @method bool hist_ismonotonic(array $options = []) Test for monotonicity. + * @method Image sign(array $options = []) Unit vector of pixel. * @throws Exception - * @method float hist_entropy(array $options = []) Estimate image entropy. + * @method Image similarity(array $options = []) Similarity transform of an image. * @throws Exception - * @method Image conv(Image $mask, array $options = []) Convolution operation. + * @method static Image sines(integer $width, integer $height, array $options = []) Make a 2D sine wave. * @throws Exception - * @method Image conva(Image $mask, array $options = []) Approximate integer convolution. + * @method Image smartcrop(integer $width, integer $height, array $options = []) Extract an area from an image. * @throws Exception - * @method Image convf(Image $mask, array $options = []) Float convolution operation. + * @method Image spcor(Image $ref, array $options = []) Spatial correlation. * @throws Exception - * @method Image convi(Image $mask, array $options = []) Int convolution operation. + * @method Image spectrum(array $options = []) Make displayable power spectrum. * @throws Exception - * @method Image compass(Image $mask, array $options = []) Convolve with rotating mask. + * @method Image stats(array $options = []) Find image average. * @throws Exception - * @method Image convsep(Image $mask, array $options = []) Seperable convolution operation. + * @method Image stdif(integer $width, integer $height, array $options = []) Statistical difference. * @throws Exception - * @method Image convasep(Image $mask, array $options = []) Approximate separable integer convolution. + * @method Image subsample(integer $xfac, integer $yfac, array $options = []) Subsample an image. * @throws Exception - * @method Image fastcor(Image $ref, array $options = []) Fast correlation. + * @method static Image sum(Image[]|Image $in, array $options = []) Sum an array of images. * @throws Exception - * @method Image spcor(Image $ref, array $options = []) Spatial correlation. + * @method static Image svgload(string $filename, array $options = []) Load SVG with rsvg. * @throws Exception - * @method Image sharpen(array $options = []) Unsharp masking for print. + * @method static Image svgload_buffer(string $buffer, array $options = []) Load SVG with rsvg. * @throws Exception - * @method Image gaussblur(float $sigma, array $options = []) Gaussian blur. + * @method static void system(string $cmd_format, array $options = []) Run an external command. * @throws Exception - * @method Image fwfft(array $options = []) Forward fft. + * @method static Image text(string $text, array $options = []) Make a text image. * @throws Exception - * @method Image invfft(array $options = []) Inverse fft. + * @method static Image thumbnail(string $filename, integer $width, array $options = []) Generate thumbnail from file. * @throws Exception - * @method Image freqmult(Image $mask, array $options = []) Frequency-domain filtering. + * @method static Image thumbnail_buffer(string $buffer, integer $width, array $options = []) Generate thumbnail from buffer. * @throws Exception - * @method Image spectrum(array $options = []) Make displayable power spectrum. + * @method Image thumbnail_image(integer $width, array $options = []) Generate thumbnail from image. * @throws Exception - * @method Image phasecor(Image $in2, array $options = []) Calculate phase correlation. + * @method static Image tiffload(string $filename, array $options = []) Load tiff from file. * @throws Exception - * @method Image morph(Image $mask, string $morph, array $options = []) Morphology operation. - * @see OperationMorphology for possible values for $morph + * @method static Image tiffload_buffer(string $buffer, array $options = []) Load tiff from buffer. * @throws Exception - * @method Image rank(integer $width, integer $height, integer $index, array $options = []) Rank filter. + * @method void tiffsave(string $filename, array $options = []) Save image to tiff file. * @throws Exception - * @method float countlines(string $direction, array $options = []) Count lines in an image. - * @see Direction for possible values for $direction + * @method string tiffsave_buffer(array $options = []) Save image to tiff buffer. * @throws Exception - * @method Image labelregions(array $options = []) Label regions in an image. + * @method Image tilecache(array $options = []) Cache an image as a set of tiles. * @throws Exception - * @method Image fill_nearest(array $options = []) Fill image zeros with nearest non-zero pixel. + * @method static Image tonelut(array $options = []) Build a look-up table. * @throws Exception - * @method Image draw_rect(float[]|float $ink, integer $left, integer $top, integer $width, integer $height, array $options = []) Paint a rectangle on an image. + * @method Image unpremultiply(array $options = []) Unpremultiply image alpha. * @throws Exception - * @method Image draw_mask(float[]|float $ink, Image $mask, integer $x, integer $y, array $options = []) Draw a mask on an image. + * @method static Image vipsload(string $filename, array $options = []) Load vips from file. * @throws Exception - * @method Image draw_line(float[]|float $ink, integer $x1, integer $y1, integer $x2, integer $y2, array $options = []) Draw a line on an image. + * @method void vipssave(string $filename, array $options = []) Save image to vips file. * @throws Exception - * @method Image draw_circle(float[]|float $ink, integer $cx, integer $cy, integer $radius, array $options = []) Draw a circle on an image. + * @method static Image webpload(string $filename, array $options = []) Load webp from file. * @throws Exception - * @method Image draw_flood(float[]|float $ink, integer $x, integer $y, array $options = []) Flood-fill an area. + * @method static Image webpload_buffer(string $buffer, array $options = []) Load webp from buffer. * @throws Exception - * @method Image draw_image(Image $sub, integer $x, integer $y, array $options = []) Paint an image into another image. + * @method void webpsave(string $filename, array $options = []) Save image to webp file. * @throws Exception - * @method Image draw_smudge(integer $left, integer $top, integer $width, integer $height, array $options = []) Blur a rectangle on an image. + * @method string webpsave_buffer(array $options = []) Save image to webp buffer. * @throws Exception - * @method Image merge(Image $sec, string $direction, integer $dx, integer $dy, array $options = []) Merge two images. - * @see Direction for possible values for $direction + * @method static Image worley(integer $width, integer $height, array $options = []) Make a worley noise image. * @throws Exception - * @method Image mosaic(Image $sec, string $direction, integer $xref, integer $yref, integer $xsec, integer $ysec, array $options = []) Mosaic two images. - * @see Direction for possible values for $direction + * @method Image wrap(array $options = []) Wrap image origin. * @throws Exception - * @method Image mosaic1(Image $sec, string $direction, integer $xr1, integer $yr1, integer $xs1, integer $ys1, integer $xr2, integer $yr2, integer $xs2, integer $ys2, array $options = []) First-order mosaic of two images. - * @see Direction for possible values for $direction + * @method static Image xyz(integer $width, integer $height, array $options = []) Make an image where pixel values are coordinates. * @throws Exception - * @method Image match(Image $sec, integer $xr1, integer $yr1, integer $xs1, integer $ys1, integer $xr2, integer $yr2, integer $xs2, integer $ys2, array $options = []) First-order match of two images. + * @method static Image zone(integer $width, integer $height, array $options = []) Make a zone plate. * @throws Exception - * @method Image globalbalance(array $options = []) Global balance an image mosaic. + * @method Image zoom(integer $xfac, integer $yfac, array $options = []) Zoom an image. * @throws Exception - * @method Image extract_area(integer $left, integer $top, integer $width, integer $height, array $options = []) Extract an area from an image. - * @method Image crop(integer $left, integer $top, integer $width, integer $height, array $options = []) Extract an area from an image. * - * @property string $nickname Class nickname - * @property string $description Class description * @property integer $width Image width in pixels * @property integer $height Image height in pixels * @property integer $bands Number of bands in image @@ -571,17 +571,11 @@ * @see Coding for possible values * @property string $interpretation Pixel interpretation * @see Interpretation for possible values - * @property float $xres Horizontal resolution in pixels/mm - * @property float $yres Vertical resolution in pixels/mm * @property integer $xoffset Horizontal offset of origin * @property integer $yoffset Vertical offset of origin + * @property float $xres Horizontal resolution in pixels/mm + * @property float $yres Vertical resolution in pixels/mm * @property string $filename Image filename - * @property string $mode Open mode - * @property bool $kill Block evaluation on this image - * @property string $demand Preferred demand style for this image - * @see DemandStyle for possible values - * @property integer $sizeof_header Offset in bytes from start of file - * @property string $foreign_buffer Pointer to foreign pixels */ abstract class ImageAutodoc { diff --git a/src/ImageType.php b/src/ImageType.php index 83648f5..3678d22 100644 --- a/src/ImageType.php +++ b/src/ImageType.php @@ -57,5 +57,4 @@ abstract class ImageType const MMAPIN = 'mmapin'; const MMAPINRW = 'mmapinrw'; const OPENOUT = 'openout'; - const PARTIAL = 'partial'; } diff --git a/src/Token.php b/src/Token.php index b48aa2c..40df593 100644 --- a/src/Token.php +++ b/src/Token.php @@ -53,5 +53,4 @@ abstract class Token const RIGHT = 'right'; const STRING = 'string'; const EQUALS = 'equals'; - const COMMA = 'comma'; } From b3b3c74cad50a9a2f031e0354e72146bb4be5985 Mon Sep 17 00:00:00 2001 From: Kleis Auke Wolthuizen Date: Sat, 9 Jun 2018 22:34:51 +0200 Subject: [PATCH 004/123] Use elseif instead --- src/Image.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Image.php b/src/Image.php index f7f52a2..3c6132c 100644 --- a/src/Image.php +++ b/src/Image.php @@ -612,7 +612,7 @@ private static function mapNumeric($value, \Closure $func) { if (is_numeric($value)) { $value = $func($value); - } else if (is_array($value)) { + } elseif (is_array($value)) { array_walk_recursive($value, function (&$value) use ($func) { $value = self::mapNumeric($value, $func); }); From af3edb077271674d2218a293e68996aaa39c8eaf Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Thu, 4 Oct 2018 16:01:41 +0100 Subject: [PATCH 005/123] update links for new home and update autodocs for 8.7 --- CONTRIBUTING.md | 2 +- README.md | 16 +++++------ composer.json | 2 +- examples/generate_phpdoc.py | 0 install-vips.sh | 2 +- src/ImageAutodoc.php | 18 +++++++++++- src/Interesting.php | 2 ++ src/Kernel.php | 1 + src/RegionShrink.php | 55 +++++++++++++++++++++++++++++++++++++ 9 files changed, 86 insertions(+), 12 deletions(-) mode change 100644 => 100755 examples/generate_phpdoc.py create mode 100644 src/RegionShrink.php diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 724f6ac..b092559 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,7 @@ Contributions are **welcome** and will be fully **credited**. -We accept contributions via Pull Requests on [Github](https://github.com/jcupitt/php-vips). +We accept contributions via Pull Requests on [Github](https://github.com/libvips/php-vips). ## Pull Requests diff --git a/README.md b/README.md index 28c4601..1a57513 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # PHP binding for libvips -[![Build Status](https://travis-ci.org/jcupitt/php-vips.svg?branch=master)](https://travis-ci.org/jcupitt/php-vips) +[![Build Status](https://travis-ci.org/libvips/php-vips.svg?branch=master)](https://travis-ci.org/libvips/php-vips) -`php-vips` is a binding for [libvips](https://github.com/jcupitt/libvips) for +`php-vips` is a binding for [libvips](https://github.com/libvips/libvips) for PHP 7. libvips is fast and needs little memory. The @@ -19,7 +19,7 @@ destination in a set of small fragments. This module builds upon the `vips` PHP extension: -https://github.com/jcupitt/php-vips-ext +https://github.com/libvips/php-vips-ext You'll need to install that first. It's tested on Linux and macOS --- Windows would need some work, but should be possible. @@ -27,7 +27,7 @@ Windows would need some work, but should be possible. See the README there, but briefly: 1. [Install the libvips library and - headers](https://jcupitt.github.io/libvips/install.html). It's in + headers](https://libvips.github.io/libvips/install.html). It's in the linux package managers, homebrew and MacPorts, and there are Windows binaries on the vips website. For example, on Debian: @@ -85,7 +85,7 @@ $ ./try1.php ~/pics/k2.jpg x.tif ``` See `examples/`. We have a [complete set of formatted API -docs](https://jcupitt.github.io/php-vips/docs/classes/Jcupitt.Vips.Image.html). +docs](https://libvips.github.io/php-vips/docs/classes/Jcupitt.Vips.Image.html). ### Introduction to the API @@ -135,7 +135,7 @@ For example: $image->writeToFile("fred.jpg", ["Q" => 90]); ``` -`php-vips` comes [with full API docs](https://jcupitt.github.io/php-vips/docs/classes/Jcupitt.Vips.Image.html). To regenerate these from your sources, type: +`php-vips` comes [with full API docs](https://libvips.github.io/php-vips/docs/classes/Jcupitt.Vips.Image.html). To regenerate these from your sources, type: ``` $ vendor/bin/phpdoc @@ -146,7 +146,7 @@ And look in `docs/`. There are around 300 operations in the library, see the vips docs for an introduction: -https://jcupitt.github.io/libvips/API/current +https://libvips.github.io/libvips/API/current ### How it works @@ -173,6 +173,6 @@ $ vendor/bin/phpdoc ``` $ cd src -$ ../examples/generate_phpdoc.rb +$ ../examples/generate_phpdoc.py ``` diff --git a/composer.json b/composer.json index ead46b0..cc95a2f 100644 --- a/composer.json +++ b/composer.json @@ -6,7 +6,7 @@ "processing", "libvips" ], - "homepage": "https://github.com/jcupitt/php-vips", + "homepage": "https://github.com/libvips/php-vips", "license": "MIT", "authors": [ { diff --git a/examples/generate_phpdoc.py b/examples/generate_phpdoc.py old mode 100644 new mode 100755 diff --git a/install-vips.sh b/install-vips.sh index edf02ea..d09dc89 100755 --- a/install-vips.sh +++ b/install-vips.sh @@ -1,6 +1,6 @@ #!/bin/bash -vips_site=https://github.com/jcupitt/libvips/releases/download +vips_site=https://github.com/libvips/libvips/releases/download version=$VIPS_VERSION_MAJOR.$VIPS_VERSION_MINOR.$VIPS_VERSION_MICRO set -e diff --git a/src/ImageAutodoc.php b/src/ImageAutodoc.php index c1b974d..7617c1e 100644 --- a/src/ImageAutodoc.php +++ b/src/ImageAutodoc.php @@ -118,6 +118,8 @@ * @throws Exception * @method Image cache(array $options = []) Cache an image. * @throws Exception + * @method Image canny(array $options = []) Canny edge detector. + * @throws Exception * @method Image cast(string $format, array $options = []) Cast an image. * @see BandFormat for possible values for $format * @throws Exception @@ -320,6 +322,10 @@ * @throws Exception * @method static Image magickload_buffer(string $buffer, array $options = []) Load buffer with ImageMagick. * @throws Exception + * @method void magicksave(string $filename, array $options = []) Save file with ImageMagick. + * @throws Exception + * @method string magicksave_buffer(array $options = []) Save image to magick buffer. + * @throws Exception * @method Image mapim(Image $index, array $options = []) Resample with an mapim image. * @throws Exception * @method Image maplut(Image $lut, array $options = []) Map an image though a lut. @@ -383,6 +389,10 @@ * @throws Exception * @method Image msb(array $options = []) Pick most-significant byte from an image. * @throws Exception + * @method static Image niftiload(string $filename, array $options = []) Load a NIFTI image. + * @throws Exception + * @method void niftisave(string $filename, array $options = []) Save image to nifti file. + * @throws Exception * @method static Image openexrload(string $filename, array $options = []) Load an OpenEXR image. * @throws Exception * @method static Image openslideload(string $filename, array $options = []) Load file with OpenSlide. @@ -466,6 +476,8 @@ * @throws Exception * @method Image rot45(array $options = []) Rotate an image. * @throws Exception + * @method Image rotate(float $angle, array $options = []) Rotate an image by a number of degrees. + * @throws Exception * @method Image round(string $round, array $options = []) Perform a round function on an image. * @see OperationRound for possible values for $round * @throws Exception @@ -499,11 +511,13 @@ * @throws Exception * @method Image smartcrop(integer $width, integer $height, array $options = []) Extract an area from an image. * @throws Exception + * @method Image sobel(array $options = []) Sobel edge detector. + * @throws Exception * @method Image spcor(Image $ref, array $options = []) Spatial correlation. * @throws Exception * @method Image spectrum(array $options = []) Make displayable power spectrum. * @throws Exception - * @method Image stats(array $options = []) Find image average. + * @method Image stats(array $options = []) Find many image stats. * @throws Exception * @method Image stdif(integer $width, integer $height, array $options = []) Statistical difference. * @throws Exception @@ -537,6 +551,8 @@ * @throws Exception * @method static Image tonelut(array $options = []) Build a look-up table. * @throws Exception + * @method Image transpose3d(array $options = []) Transpose3d an image. + * @throws Exception * @method Image unpremultiply(array $options = []) Unpremultiply image alpha. * @throws Exception * @method static Image vipsload(string $filename, array $options = []) Load vips from file. diff --git a/src/Interesting.php b/src/Interesting.php index 2d64c21..80a03b2 100644 --- a/src/Interesting.php +++ b/src/Interesting.php @@ -53,4 +53,6 @@ abstract class Interesting const CENTRE = 'centre'; const ENTROPY = 'entropy'; const ATTENTION = 'attention'; + const LOW = 'low'; + const HIGH = 'high'; } diff --git a/src/Kernel.php b/src/Kernel.php index 43bb236..d5b76dc 100644 --- a/src/Kernel.php +++ b/src/Kernel.php @@ -52,6 +52,7 @@ abstract class Kernel const NEAREST = 'nearest'; const LINEAR = 'linear'; const CUBIC = 'cubic'; + const MITCHELL = 'mitchell'; const LANCZOS2 = 'lanczos2'; const LANCZOS3 = 'lanczos3'; } diff --git a/src/RegionShrink.php b/src/RegionShrink.php new file mode 100644 index 0000000..ebf1d50 --- /dev/null +++ b/src/RegionShrink.php @@ -0,0 +1,55 @@ + + * @copyright 2016 John Cupitt + * @license https://opensource.org/licenses/MIT MIT + * @link https://github.com/jcupitt/php-vips + */ + +namespace Jcupitt\Vips; + +/** + * The RegionShrink enum. + * @category Images + * @package Jcupitt\Vips + * @author John Cupitt + * @copyright 2016 John Cupitt + * @license https://opensource.org/licenses/MIT MIT + * @link https://github.com/jcupitt/php-vips + */ +abstract class RegionShrink +{ + const MEAN = 'mean'; + const MEDIAN = 'median'; + const MODE = 'mode'; +} From 3678b8a4b1c301c9e6f6e136746f5894e85b9dbe Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Thu, 4 Oct 2018 16:25:12 +0100 Subject: [PATCH 006/123] version bump --- RELEASE-1.0.2 => RELEASE-1.0.3 | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename RELEASE-1.0.2 => RELEASE-1.0.3 (100%) diff --git a/RELEASE-1.0.2 b/RELEASE-1.0.3 similarity index 100% rename from RELEASE-1.0.2 rename to RELEASE-1.0.3 From 31b29b8f0a4604c268f987293c4526eb4703c92b Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Thu, 29 Nov 2018 17:31:07 +0000 Subject: [PATCH 007/123] fix phpdoc we need to stop it using "jms/serializer" 1.8.0 or later, there's an issue with paths add a "1" to the end of some autogenerated enum names to stop phpdoc falling over --- composer.json | 1 + examples/generate_phpdoc.py | 10 ++++++++++ src/BlendMode.php | 2 +- src/ForeignWebpPreset.php | 2 +- src/Image.php | 2 +- src/OperationBoolean.php | 4 ++-- 6 files changed, 16 insertions(+), 5 deletions(-) diff --git a/composer.json b/composer.json index cc95a2f..ee764e4 100644 --- a/composer.json +++ b/composer.json @@ -22,6 +22,7 @@ "psr/log": "^1.0.2" }, "require-dev": { + "jms/serializer" : ">=0.12 < 1.8.0", "phpunit/phpunit": "^6.5", "phpdocumentor/phpdocumentor" : "^2.9", "jakub-onderka/php-parallel-lint": "^1.0.0", diff --git a/examples/generate_phpdoc.py b/examples/generate_phpdoc.py index fd0f750..11c7b05 100755 --- a/examples/generate_phpdoc.py +++ b/examples/generate_phpdoc.py @@ -61,6 +61,14 @@ # for VipsOperationFlags _OPERATION_DEPRECATED = 8 +# some names we might generate are reserved PHP names ... just append a "1". +reserved_php_names = { + 'DEFAULT': 'DEFAULT1', + 'XOR': 'XOR1', + 'AND': 'AND1', + 'OR': 'OR1' +} + def gtype_to_php(gtype, result=False): """Map a gtype to PHP type name we use to represent it. @@ -322,6 +330,8 @@ def add_enum(gtype, a, b): for value in values_for_enum(gtype): php_name = value.replace('-', '_').upper() + if php_name in reserved_php_names: + php_name = reserved_php_names[php_name] f.write(' const {0} = \'{1}\';\n'.format(php_name, value)) f.write('}\n') diff --git a/src/BlendMode.php b/src/BlendMode.php index 833ee1b..8f0df6d 100644 --- a/src/BlendMode.php +++ b/src/BlendMode.php @@ -60,7 +60,7 @@ abstract class BlendMode const DEST_IN = 'dest-in'; const DEST_OUT = 'dest-out'; const DEST_ATOP = 'dest-atop'; - const XOR = 'xor'; + const XOR1 = 'xor'; const ADD = 'add'; const SATURATE = 'saturate'; const MULTIPLY = 'multiply'; diff --git a/src/ForeignWebpPreset.php b/src/ForeignWebpPreset.php index 44f84ef..cfbf0b0 100644 --- a/src/ForeignWebpPreset.php +++ b/src/ForeignWebpPreset.php @@ -49,7 +49,7 @@ */ abstract class ForeignWebpPreset { - const DEFAULT = 'default'; + const DEFAULT1 = 'default'; const PICTURE = 'picture'; const PHOTO = 'photo'; const DRAWING = 'drawing'; diff --git a/src/Image.php b/src/Image.php index 3c6132c..3ae0dd6 100644 --- a/src/Image.php +++ b/src/Image.php @@ -558,7 +558,7 @@ class Image extends ImageAutodoc implements \ArrayAccess BlendMode::DEST_IN => 8, BlendMode::DEST_OUT => 9, BlendMode::DEST_ATOP => 10, - BlendMode::XOR => 11, + BlendMode::XOR1 => 11, BlendMode::ADD => 12, BlendMode::SATURATE => 13, BlendMode::MULTIPLY => 14, diff --git a/src/OperationBoolean.php b/src/OperationBoolean.php index 78f6f40..5b69509 100644 --- a/src/OperationBoolean.php +++ b/src/OperationBoolean.php @@ -49,8 +49,8 @@ */ abstract class OperationBoolean { - const AND = 'and'; - const OR = 'or'; + const AND1 = 'and'; + const OR1 = 'or'; const EOR = 'eor'; const LSHIFT = 'lshift'; const RSHIFT = 'rshift'; From e958d35798b8976be2553374be2f939f9e298888 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 22 Dec 2018 12:19:13 +0000 Subject: [PATCH 008/123] polar() and rect() work on non-complex make polar() and recct() work on non-complex images, like Python see https://github.com/libvips/php-vips/issues/81 --- CHANGELOG.md | 17 ++++++++++ RELEASE-1.0.3 => RELEASE-1.0.4 | 0 src/Image.php | 61 ++++++++++++++++++++++++++++++++-- 3 files changed, 76 insertions(+), 2 deletions(-) rename RELEASE-1.0.3 => RELEASE-1.0.4 (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f1ab2f..81b68fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,23 @@ # Changelog All notable changes to `:vips` will be documented in this file. +## 1.0.4 - 2018-12-22 + +### Added +- polar() and rect() now work onadd cross_phase() [John Cupitt] + +### Deprecated +- Nothing + +### Fixed +- Nothing + +### Remove +- Nothing + +### Security +- Nothing + ## 1.0.3 - 2017-06-06 ### Added diff --git a/RELEASE-1.0.3 b/RELEASE-1.0.4 similarity index 100% rename from RELEASE-1.0.3 rename to RELEASE-1.0.4 diff --git a/src/Image.php b/src/Image.php index 3ae0dd6..37deefc 100644 --- a/src/Image.php +++ b/src/Image.php @@ -801,6 +801,59 @@ private static function errorIsArray($result) } } + /** + * Run a function expecting a complex image. If the image is not in complex + * format, try to make it complex by joining adjacant bands as real and + * imaginary. + * + * @param function $filename The function to run. + * @param Image $image The image to run the function on. + * + * @throws Exception + * + * @return Image A new Image. + * + * @internal + */ + private static function runCmplx($func, Image $image): Image + { + $original_format = $image->format; + + if ($image->format != 'complex' && $image->format != 'dpcomplex') { + if ($image->bands % 2 != 0) { + throw new Exception('not an even number of bands'); + } + + if ($image->format != 'float' && $image->format != 'double') { + $image = $image->cast('float'); + } + + if ($image->format == 'double') { + $new_format = 'dpcomplex'; + } else { + $new_format = 'complex'; + } + + $image = $image->copy(['format' => $new_format, + 'bands' => $image->bands / 2]); + } + + $image = $func($image); + + if ($original_format != 'complex' && $original_format != 'dpcomplex') { + if ($image->format == 'dpcomplex') { + $new_format = 'double'; + } else { + $new_format = 'float'; + } + + $image = $image->copy(['format' => $new_format, + 'bands' => $image->bands * 2]); + } + + return $image; + } + /** * Create a new Image from a file on disc. * @@ -2207,7 +2260,9 @@ public function imag(): Image */ public function polar(): Image { - return $this->complex(OperationComplex::POLAR); + return self::runCmplx(function ($image) { + return $image->complex(OperationComplex::POLAR); + }, $this); } /** @@ -2219,7 +2274,9 @@ public function polar(): Image */ public function rect(): Image { - return $this->complex(OperationComplex::RECT); + return self::runCmplx(function ($image) { + return $image->complex(OperationComplex::RECT); + }, $this); } /** From 03cc7b00e15daab8fd9f9426a2e458ec83983ea0 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 22 Dec 2018 12:34:48 +0000 Subject: [PATCH 009/123] add crossPhase() an enum expansion we'd forgotten --- CHANGELOG.md | 4 +++- src/Image.php | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 81b68fb..047d22a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,9 @@ All notable changes to `:vips` will be documented in this file. ## 1.0.4 - 2018-12-22 ### Added -- polar() and rect() now work onadd cross_phase() [John Cupitt] +- polar() and rect() now work on non-complex images [John Cupitt] +- add crossPhase() [John Cupitt] +- update autodocs [John Cupitt] ### Deprecated - Nothing diff --git a/src/Image.php b/src/Image.php index 37deefc..f8a121b 100644 --- a/src/Image.php +++ b/src/Image.php @@ -2291,6 +2291,20 @@ public function conj(): Image return $this->complex(OperationComplex::CONJ); } + /** + * Find the cross-phase of this image with $other. + * + * @param mixed $other The thing to cross-phase by. + * + * @throws Exception + * + * @return Image A new image. + */ + public function crossPhase($other): Image + { + return $this->complex2($other, OperationComplex2::CROSS_PHASE); + } + /** * Return the sine of an image in degrees. * From d6cd69307b086f43bc099471a4653cbf5ecc40d2 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 22 Dec 2018 12:44:40 +0000 Subject: [PATCH 010/123] fix a vips8.8-ism in docs --- src/Interesting.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Interesting.php b/src/Interesting.php index 80a03b2..2d64c21 100644 --- a/src/Interesting.php +++ b/src/Interesting.php @@ -53,6 +53,4 @@ abstract class Interesting const CENTRE = 'centre'; const ENTROPY = 'entropy'; const ATTENTION = 'attention'; - const LOW = 'low'; - const HIGH = 'high'; } From 0338997bf358d1ac87f75ef7b814933ec40caa21 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 22 Feb 2019 11:12:23 +0000 Subject: [PATCH 011/123] update version number in README example --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1a57513..4641e17 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ See the README there, but briefly: ``` "require": { - "jcupitt/vips" : "1.0.2" + "jcupitt/vips" : "1.0.4" } ``` From e0c3d3fb9727c6bd96bd1ff8622d1afae0791935 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Thu, 26 Sep 2019 16:34:52 +0100 Subject: [PATCH 012/123] add writeToArray and bump v to 1.0.5 --- CHANGELOG.md | 17 ++++++++++++++ README.md | 2 +- RELEASE-1.0.4 => RELEASE-1.0.5 | 0 src/Image.php | 43 +++++++++++++++++++++++++++++++++- 4 files changed, 60 insertions(+), 2 deletions(-) rename RELEASE-1.0.4 => RELEASE-1.0.5 (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 047d22a..797e22d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,23 @@ # Changelog All notable changes to `:vips` will be documented in this file. +## 1.0.5 - 2019-09-26 + +### Added +- writeToArray() [John Cupitt] + +### Deprecated +- Nothing + +### Fixed +- Nothing + +### Remove +- Nothing + +### Security +- Nothing + ## 1.0.4 - 2018-12-22 ### Added diff --git a/README.md b/README.md index 4641e17..cf5b4bc 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ See the README there, but briefly: ``` "require": { - "jcupitt/vips" : "1.0.4" + "jcupitt/vips" : "1.0.5" } ``` diff --git a/RELEASE-1.0.4 b/RELEASE-1.0.5 similarity index 100% rename from RELEASE-1.0.4 rename to RELEASE-1.0.5 diff --git a/src/Image.php b/src/Image.php index f8a121b..357a981 100644 --- a/src/Image.php +++ b/src/Image.php @@ -137,7 +137,8 @@ * * `Image::writeToFile` writes an image back to the filesystem. It can * write any format supported by vips: the file type is set from the filename - * suffix. You can also write formatted images to strings. + * suffix. You can write formatted images to strings, and pixel values to + * arrays. * * # Getting more help * @@ -1212,6 +1213,46 @@ public function writeToMemory(): string return $result; } + /** + * Write an image to a PHP array. + * + * Pixels are written as a simple one-dimensional array, for example, if + * you write: + * + * ```php + * $result = $image->crop(100, 100, 10, 1)->writeToArray(); + * ``` + * + * This will crop out 10 pixels and write them to the array. If `$image` + * is an RGB image, then `$array` will contain 30 numbers, with the first + * three being R, G and B for the first pixel. + * + * You'll need to slice and repack the array if you want more dimensions. + * + * This method is much faster than repeatedly calling `getpoint()`. It + * will use a lot of memory. + * + * @throws Exception + * + * @return array The pixel values as a PHP array. + */ + public function writeToArray(): array + { + Utils::debugLog('writeToArray', [ + 'instance' => $this, + 'arguments' => [] + ]); + + $result = vips_image_write_to_array($this->image); + if ($result === -1) { + self::errorVips(); + } + + Utils::debugLog('writeToArray', ['result' => $result]); + + return $result; + } + /** * Copy to memory. * From 3a812dcac5feab9554380678d15e91ef3bce3b17 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 28 Aug 2020 02:43:51 +0100 Subject: [PATCH 013/123] add setType and typeFromName so we can set arbitrary metadata --- CHANGELOG.md | 18 ++++++++++++++++++ RELEASE-1.0.5 => RELEASE-1.0.6 | 0 examples/generate_phpdoc.py | 2 +- src/Image.php | 24 ++++++++++++++++++++++++ src/Utils.php | 15 ++++++++++++++- 5 files changed, 57 insertions(+), 2 deletions(-) rename RELEASE-1.0.5 => RELEASE-1.0.6 (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 797e22d..acbfda6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,24 @@ # Changelog All notable changes to `:vips` will be documented in this file. +## 1.0.6 - 2020-08-28 + +### Added +- Image::setType() +- Utils::typeFromName() + +### Deprecated +- Nothing + +### Fixed +- Nothing + +### Remove +- Nothing + +### Security +- Nothing + ## 1.0.5 - 2019-09-26 ### Added diff --git a/RELEASE-1.0.5 b/RELEASE-1.0.6 similarity index 100% rename from RELEASE-1.0.5 rename to RELEASE-1.0.6 diff --git a/examples/generate_phpdoc.py b/examples/generate_phpdoc.py index 11c7b05..d822f58 100755 --- a/examples/generate_phpdoc.py +++ b/examples/generate_phpdoc.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/python3 from pyvips import Image, Operation, GValue, Error, \ ffi, values_for_enum, vips_lib, gobject_lib, \ diff --git a/src/Image.php b/src/Image.php index 357a981..4fa7c8e 100644 --- a/src/Image.php +++ b/src/Image.php @@ -1383,6 +1383,30 @@ public function set(string $name, $value) } } + /** + * Set the type and value for any property on the underlying image. + * + * This is useful if the type of the property cannot be determined from the + * php type of the value. + * + * Use Utils::typefromName() to look up types by name. + * + * @param int $type The type of the property. + * @param string $name The property name. + * @param mixed $value The value to set for this property. + * + * @throws Exception + * + * @return void + */ + public function setType(int $type, string $name, $value) + { + $result = vips_image_set_type($this->image, $type, $name, $value); + if ($result === -1) { + self::errorVips(); + } + } + /** * Remove a field from the underlying image. * diff --git a/src/Utils.php b/src/Utils.php index 0e1d20b..a122e31 100644 --- a/src/Utils.php +++ b/src/Utils.php @@ -41,7 +41,7 @@ use Psr\Log\LoggerInterface; /** - * Various utilities. For now, just loggers. + * Various utilities. * * @category Images * @package Jcupitt\Vips @@ -83,6 +83,19 @@ public static function errorLog(string $message, \Exception $exception) $logger->error($message, ['exception' => $exception]); } } + + /** + * Look up the GTyoe from a type name. If the type does not exist, + * return 0. + * + * @param string $name The type name. + * + * @return int + */ + public static function typeFromName(string $name) + { + return vips_type_from_name($name); + } } /* From 1ea7d0e66c29557f4e9b9fb03af412d8d4fd02bd Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 28 Aug 2020 03:04:31 +0100 Subject: [PATCH 014/123] update autodocs --- CHANGELOG.md | 1 + examples/generate_phpdoc.py | 70 ++--- src/ForeignDzContainer.php | 1 + src/ForeignDzLayout.php | 1 + src/ForeignHeifCompression.php | 56 ++++ src/ForeignJpegSubsample.php | 55 ++++ src/ForeignTiffCompression.php | 2 + src/GsfOutputCsvQuotingMode.php | 54 ++++ src/ImageAutodoc.php | 446 ++++++++++++++++++-------------- src/Interesting.php | 3 + src/RegionShrink.php | 3 + 11 files changed, 454 insertions(+), 238 deletions(-) create mode 100644 src/ForeignHeifCompression.php create mode 100644 src/ForeignJpegSubsample.php create mode 100644 src/GsfOutputCsvQuotingMode.php diff --git a/CHANGELOG.md b/CHANGELOG.md index acbfda6..4dc6cbc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to `:vips` will be documented in this file. ### Added - Image::setType() - Utils::typeFromName() +- Updated autodocs for libvips 8.10 ### Deprecated - Nothing diff --git a/examples/generate_phpdoc.py b/examples/generate_phpdoc.py index d822f58..d617893 100755 --- a/examples/generate_phpdoc.py +++ b/examples/generate_phpdoc.py @@ -1,6 +1,6 @@ #!/usr/bin/python3 -from pyvips import Image, Operation, GValue, Error, \ +from pyvips import Image, Introspect, GValue, Error, \ ffi, values_for_enum, vips_lib, gobject_lib, \ type_map, type_name, type_from_name, nickname_find @@ -95,71 +95,47 @@ def remove_prefix(enum_str): def generate_operation(operation_name): - op = Operation.new_from_name(operation_name) - - # we are only interested in non-deprecated args - args = [[name, flags] for name, flags in op.get_args() - if not flags & _DEPRECATED] - - # find the first required input image arg, if any ... that will be self - member_x = None - for name, flags in args: - if ((flags & _INPUT) != 0 and - (flags & _REQUIRED) != 0 and - op.get_typeof(name) == GValue.image_type): - member_x = name - break - - required_input = [name for name, flags in args - if (flags & _INPUT) != 0 and - (flags & _REQUIRED) != 0 and - name != member_x] - - required_output = [name for name, flags in args - if ((flags & _OUTPUT) != 0 and - (flags & _REQUIRED) != 0) or - ((flags & _INPUT) != 0 and - (flags & _REQUIRED) != 0 and - (flags & _MODIFY) != 0)] + intro = Introspect.get(operation_name) result = ' * @method ' - if member_x is None: + if intro.member_x is None: result += 'static ' - if len(required_output) == 0: + if len(intro.required_output) == 0: result += 'void ' - elif len(required_output) == 1: - result += '{0} '.format(gtype_to_php(op.get_typeof(required_output[0]), True)) + elif len(intro.required_output) == 1: + arg = intro.required_output[0] + details = intro.details[arg] + result += '{0} '.format(gtype_to_php(details['type'], True)) else: # we generate a Returns: block for this case, see below result += 'array ' result += '{0}('.format(operation_name) - for name in required_input: - gtype = op.get_typeof(name) - result += '{0} ${1}, '.format(gtype_to_php(gtype), name) + for name in intro.required_input: + details = intro.details[name] + result += '{0} ${1}, '.format(gtype_to_php(details['type']), name) result += 'array $options = []) ' - description = op.get_description() + description = intro.description result += description[0].upper() + description[1:] + '.\n' # find any Enums we've referenced and output @see lines for them - for name in required_output + required_input: - gtype = op.get_typeof(name) - fundamental = gobject_lib.g_type_fundamental(gtype) + for name in intro.required_output + intro.required_input: + details = intro.details[name] + fundamental = gobject_lib.g_type_fundamental(details['type']) if fundamental != GValue.genum_type: continue - result += ' * @see {0} for possible values for ${1}\n'.format(remove_prefix(type_name(gtype)), name) + result += ' * @see {0} for possible values for ${1}\n'.format(remove_prefix(type_name(details['type'])), name) - if len(required_output) > 1: + if len(intro.required_output) > 1: result += ' * Return array with: [\n' - for name in required_output: - gtype = op.get_typeof(name) - blurb = op.get_blurb(name) - result += ' * \'{0}\' => @type {1} {2}\n'.format(name, gtype_to_php(gtype), - blurb[0].upper() + blurb[1:]) + for name in intro.required_output: + details = intro.details[name] + result += ' * \'{0}\' => @type {1} {2}\n'.format(name, gtype_to_php(details['type']), + details['blurb'][0].upper() + details['blurb'][1:]) result += ' * ];\n' result += ' * @throws Exception\n' @@ -222,10 +198,10 @@ def add_nickname(gtype, a, b): nickname = nickname_find(gtype) try: # can fail for abstract types - op = Operation.new_from_name(nickname) + intro = Introspect.get(nickname) # we are only interested in non-deprecated operations - if (op.get_flags() & _OPERATION_DEPRECATED) == 0: + if (intro.flags & _OPERATION_DEPRECATED) == 0: all_nicknames.append(nickname) except Error: pass diff --git a/src/ForeignDzContainer.php b/src/ForeignDzContainer.php index 5ba8d34..054b853 100644 --- a/src/ForeignDzContainer.php +++ b/src/ForeignDzContainer.php @@ -51,4 +51,5 @@ abstract class ForeignDzContainer { const FS = 'fs'; const ZIP = 'zip'; + const SZI = 'szi'; } diff --git a/src/ForeignDzLayout.php b/src/ForeignDzLayout.php index c06e3c7..021d75f 100644 --- a/src/ForeignDzLayout.php +++ b/src/ForeignDzLayout.php @@ -52,4 +52,5 @@ abstract class ForeignDzLayout const DZ = 'dz'; const ZOOMIFY = 'zoomify'; const GOOGLE = 'google'; + const IIIF = 'iiif'; } diff --git a/src/ForeignHeifCompression.php b/src/ForeignHeifCompression.php new file mode 100644 index 0000000..926b1ab --- /dev/null +++ b/src/ForeignHeifCompression.php @@ -0,0 +1,56 @@ + + * @copyright 2016 John Cupitt + * @license https://opensource.org/licenses/MIT MIT + * @link https://github.com/jcupitt/php-vips + */ + +namespace Jcupitt\Vips; + +/** + * The ForeignHeifCompression enum. + * @category Images + * @package Jcupitt\Vips + * @author John Cupitt + * @copyright 2016 John Cupitt + * @license https://opensource.org/licenses/MIT MIT + * @link https://github.com/jcupitt/php-vips + */ +abstract class ForeignHeifCompression +{ + const HEVC = 'hevc'; + const AVC = 'avc'; + const JPEG = 'jpeg'; + const AV1 = 'av1'; +} diff --git a/src/ForeignJpegSubsample.php b/src/ForeignJpegSubsample.php new file mode 100644 index 0000000..f87bfa7 --- /dev/null +++ b/src/ForeignJpegSubsample.php @@ -0,0 +1,55 @@ + + * @copyright 2016 John Cupitt + * @license https://opensource.org/licenses/MIT MIT + * @link https://github.com/jcupitt/php-vips + */ + +namespace Jcupitt\Vips; + +/** + * The ForeignJpegSubsample enum. + * @category Images + * @package Jcupitt\Vips + * @author John Cupitt + * @copyright 2016 John Cupitt + * @license https://opensource.org/licenses/MIT MIT + * @link https://github.com/jcupitt/php-vips + */ +abstract class ForeignJpegSubsample +{ + const AUTO = 'auto'; + const ON = 'on'; + const OFF = 'off'; +} diff --git a/src/ForeignTiffCompression.php b/src/ForeignTiffCompression.php index 6354e6e..0d5a6b0 100644 --- a/src/ForeignTiffCompression.php +++ b/src/ForeignTiffCompression.php @@ -55,4 +55,6 @@ abstract class ForeignTiffCompression const PACKBITS = 'packbits'; const CCITTFAX4 = 'ccittfax4'; const LZW = 'lzw'; + const WEBP = 'webp'; + const ZSTD = 'zstd'; } diff --git a/src/GsfOutputCsvQuotingMode.php b/src/GsfOutputCsvQuotingMode.php new file mode 100644 index 0000000..a8a08f0 --- /dev/null +++ b/src/GsfOutputCsvQuotingMode.php @@ -0,0 +1,54 @@ + + * @copyright 2016 John Cupitt + * @license https://opensource.org/licenses/MIT MIT + * @link https://github.com/jcupitt/php-vips + */ + +namespace Jcupitt\Vips; + +/** + * The GsfOutputCsvQuotingMode enum. + * @category Images + * @package Jcupitt\Vips + * @author John Cupitt + * @copyright 2016 John Cupitt + * @license https://opensource.org/licenses/MIT MIT + * @link https://github.com/jcupitt/php-vips + */ +abstract class GsfOutputCsvQuotingMode +{ + const NEVER = 'never'; + const AUTO = 'auto'; +} diff --git a/src/ImageAutodoc.php b/src/ImageAutodoc.php index 7617c1e..0aef9c1 100644 --- a/src/ImageAutodoc.php +++ b/src/ImageAutodoc.php @@ -47,167 +47,177 @@ * @license https://opensource.org/licenses/MIT MIT * @link https://github.com/jcupitt/php-vips * - * @method Image CMC2LCh(array $options = []) Transform LCh to CMC. + * @method Image CMC2LCh(Image $in, array $options = []) Transform LCh to CMC. * @throws Exception - * @method Image HSV2sRGB(array $options = []) Transform HSV to sRGB. + * @method Image CMYK2XYZ(Image $in, array $options = []) Transform CMYK to XYZ. * @throws Exception - * @method Image LCh2CMC(array $options = []) Transform LCh to CMC. + * @method Image HSV2sRGB(Image $in, array $options = []) Transform HSV to sRGB. * @throws Exception - * @method Image LCh2Lab(array $options = []) Transform LCh to Lab. + * @method Image LCh2CMC(Image $in, array $options = []) Transform LCh to CMC. * @throws Exception - * @method Image Lab2LCh(array $options = []) Transform Lab to LCh. + * @method Image LCh2Lab(Image $in, array $options = []) Transform LCh to Lab. * @throws Exception - * @method Image Lab2LabQ(array $options = []) Transform float Lab to LabQ coding. + * @method Image Lab2LCh(Image $in, array $options = []) Transform Lab to LCh. * @throws Exception - * @method Image Lab2LabS(array $options = []) Transform float Lab to signed short. + * @method Image Lab2LabQ(Image $in, array $options = []) Transform float Lab to LabQ coding. * @throws Exception - * @method Image Lab2XYZ(array $options = []) Transform CIELAB to XYZ. + * @method Image Lab2LabS(Image $in, array $options = []) Transform float Lab to signed short. * @throws Exception - * @method Image LabQ2Lab(array $options = []) Unpack a LabQ image to float Lab. + * @method Image Lab2XYZ(Image $in, array $options = []) Transform CIELAB to XYZ. * @throws Exception - * @method Image LabQ2LabS(array $options = []) Unpack a LabQ image to short Lab. + * @method Image LabQ2Lab(Image $in, array $options = []) Unpack a LabQ image to float Lab. * @throws Exception - * @method Image LabQ2sRGB(array $options = []) Convert a LabQ image to sRGB. + * @method Image LabQ2LabS(Image $in, array $options = []) Unpack a LabQ image to short Lab. * @throws Exception - * @method Image LabS2Lab(array $options = []) Transform signed short Lab to float. + * @method Image LabQ2sRGB(Image $in, array $options = []) Convert a LabQ image to sRGB. * @throws Exception - * @method Image LabS2LabQ(array $options = []) Transform short Lab to LabQ coding. + * @method Image LabS2Lab(Image $in, array $options = []) Transform signed short Lab to float. * @throws Exception - * @method Image XYZ2Lab(array $options = []) Transform XYZ to Lab. + * @method Image LabS2LabQ(Image $in, array $options = []) Transform short Lab to LabQ coding. * @throws Exception - * @method Image XYZ2Yxy(array $options = []) Transform XYZ to Yxy. + * @method Image XYZ2CMYK(Image $in, array $options = []) Transform XYZ to CMYK. * @throws Exception - * @method Image XYZ2scRGB(array $options = []) Transform XYZ to scRGB. + * @method Image XYZ2Lab(Image $in, array $options = []) Transform XYZ to Lab. * @throws Exception - * @method Image Yxy2XYZ(array $options = []) Transform Yxy to XYZ. + * @method Image XYZ2Yxy(Image $in, array $options = []) Transform XYZ to Yxy. * @throws Exception - * @method Image abs(array $options = []) Absolute value of an image. + * @method Image XYZ2scRGB(Image $in, array $options = []) Transform XYZ to scRGB. * @throws Exception - * @method Image affine(float[]|float $matrix, array $options = []) Affine transform of an image. + * @method Image Yxy2XYZ(Image $in, array $options = []) Transform Yxy to XYZ. + * @throws Exception + * @method Image abs(Image $in, array $options = []) Absolute value of an image. + * @throws Exception + * @method Image affine(Image $in, float[]|float $matrix, array $options = []) Affine transform of an image. * @throws Exception * @method static Image analyzeload(string $filename, array $options = []) Load an Analyze6 image. * @throws Exception * @method static Image arrayjoin(Image[]|Image $in, array $options = []) Join an array of images. * @throws Exception - * @method Image autorot(array $options = []) Autorotate image by exif tag. + * @method Image autorot(Image $in, array $options = []) Autorotate image by exif tag. * @throws Exception - * @method float avg(array $options = []) Find image average. + * @method float avg(Image $in, array $options = []) Find image average. * @throws Exception - * @method Image bandbool(string $boolean, array $options = []) Boolean operation across image bands. + * @method Image bandbool(Image $in, string $boolean, array $options = []) Boolean operation across image bands. * @see OperationBoolean for possible values for $boolean * @throws Exception - * @method Image bandfold(array $options = []) Fold up x axis into bands. + * @method Image bandfold(Image $in, array $options = []) Fold up x axis into bands. * @throws Exception - * @method Image bandjoin_const(float[]|float $c, array $options = []) Append a constant band to an image. + * @method Image bandjoin_const(Image $in, float[]|float $c, array $options = []) Append a constant band to an image. * @throws Exception - * @method Image bandmean(array $options = []) Band-wise average. + * @method Image bandmean(Image $in, array $options = []) Band-wise average. * @throws Exception - * @method Image bandunfold(array $options = []) Unfold image bands into x axis. + * @method Image bandunfold(Image $in, array $options = []) Unfold image bands into x axis. * @throws Exception * @method static Image black(integer $width, integer $height, array $options = []) Make a black image. * @throws Exception - * @method Image boolean(Image $right, string $boolean, array $options = []) Boolean operation on two images. + * @method Image boolean(Image $left, Image $right, string $boolean, array $options = []) Boolean operation on two images. * @see OperationBoolean for possible values for $boolean * @throws Exception - * @method Image boolean_const(string $boolean, float[]|float $c, array $options = []) Boolean operations against a constant. + * @method Image boolean_const(Image $in, string $boolean, float[]|float $c, array $options = []) Boolean operations against a constant. * @see OperationBoolean for possible values for $boolean * @throws Exception - * @method Image buildlut(array $options = []) Build a look-up table. + * @method Image buildlut(Image $in, array $options = []) Build a look-up table. * @throws Exception - * @method Image byteswap(array $options = []) Byteswap an image. + * @method Image byteswap(Image $in, array $options = []) Byteswap an image. * @throws Exception - * @method Image cache(array $options = []) Cache an image. + * @method Image cache(Image $in, array $options = []) Cache an image. * @throws Exception - * @method Image canny(array $options = []) Canny edge detector. + * @method Image canny(Image $in, array $options = []) Canny edge detector. * @throws Exception - * @method Image cast(string $format, array $options = []) Cast an image. + * @method Image case(Image $index, Image[]|Image $cases, array $options = []) Use pixel values to pick cases from an array of images. + * @throws Exception + * @method Image cast(Image $in, string $format, array $options = []) Cast an image. * @see BandFormat for possible values for $format * @throws Exception - * @method Image colourspace(string $space, array $options = []) Convert to a new colorspace. + * @method Image colourspace(Image $in, string $space, array $options = []) Convert to a new colorspace. * @see Interpretation for possible values for $space * @throws Exception - * @method Image compass(Image $mask, array $options = []) Convolve with rotating mask. + * @method Image compass(Image $in, Image $mask, array $options = []) Convolve with rotating mask. * @throws Exception - * @method Image complex(string $cmplx, array $options = []) Perform a complex operation on an image. + * @method Image complex(Image $in, string $cmplx, array $options = []) Perform a complex operation on an image. * @see OperationComplex for possible values for $cmplx * @throws Exception - * @method Image complex2(Image $right, string $cmplx, array $options = []) Complex binary operations on two images. + * @method Image complex2(Image $left, Image $right, string $cmplx, array $options = []) Complex binary operations on two images. * @see OperationComplex2 for possible values for $cmplx * @throws Exception - * @method Image complexform(Image $right, array $options = []) Form a complex image from two real images. + * @method Image complexform(Image $left, Image $right, array $options = []) Form a complex image from two real images. * @throws Exception - * @method Image complexget(string $get, array $options = []) Get a component from a complex image. + * @method Image complexget(Image $in, string $get, array $options = []) Get a component from a complex image. * @see OperationComplexget for possible values for $get * @throws Exception * @method static Image composite(Image[]|Image $in, integer[]|integer $mode, array $options = []) Blend an array of images with an array of blend modes. * @throws Exception - * @method Image composite2(Image $overlay, string $mode, array $options = []) Blend a pair of images with a blend mode. + * @method Image composite2(Image $base, Image $overlay, string $mode, array $options = []) Blend a pair of images with a blend mode. * @see BlendMode for possible values for $mode * @throws Exception - * @method Image conv(Image $mask, array $options = []) Convolution operation. + * @method Image conv(Image $in, Image $mask, array $options = []) Convolution operation. * @throws Exception - * @method Image conva(Image $mask, array $options = []) Approximate integer convolution. + * @method Image conva(Image $in, Image $mask, array $options = []) Approximate integer convolution. * @throws Exception - * @method Image convasep(Image $mask, array $options = []) Approximate separable integer convolution. + * @method Image convasep(Image $in, Image $mask, array $options = []) Approximate separable integer convolution. * @throws Exception - * @method Image convf(Image $mask, array $options = []) Float convolution operation. + * @method Image convf(Image $in, Image $mask, array $options = []) Float convolution operation. * @throws Exception - * @method Image convi(Image $mask, array $options = []) Int convolution operation. + * @method Image convi(Image $in, Image $mask, array $options = []) Int convolution operation. * @throws Exception - * @method Image convsep(Image $mask, array $options = []) Seperable convolution operation. + * @method Image convsep(Image $in, Image $mask, array $options = []) Seperable convolution operation. * @throws Exception - * @method Image copy(array $options = []) Copy an image. + * @method Image copy(Image $in, array $options = []) Copy an image. * @throws Exception - * @method float countlines(string $direction, array $options = []) Count lines in an image. + * @method float countlines(Image $in, string $direction, array $options = []) Count lines in an image. * @see Direction for possible values for $direction * @throws Exception - * @method Image crop(integer $left, integer $top, integer $width, integer $height, array $options = []) Extract an area from an image. + * @method Image crop(Image $input, integer $left, integer $top, integer $width, integer $height, array $options = []) Extract an area from an image. + * @throws Exception + * @method static Image csvload(string $filename, array $options = []) Load csv. * @throws Exception - * @method static Image csvload(string $filename, array $options = []) Load csv from file. + * @method static Image csvload_source(string $source, array $options = []) Load csv. * @throws Exception - * @method void csvsave(string $filename, array $options = []) Save image to csv file. + * @method void csvsave(Image $in, string $filename, array $options = []) Save image to csv. * @throws Exception - * @method Image dE00(Image $right, array $options = []) Calculate dE00. + * @method void csvsave_target(Image $in, string $target, array $options = []) Save image to csv. * @throws Exception - * @method Image dE76(Image $right, array $options = []) Calculate dE76. + * @method Image dE00(Image $left, Image $right, array $options = []) Calculate dE00. * @throws Exception - * @method Image dECMC(Image $right, array $options = []) Calculate dECMC. + * @method Image dE76(Image $left, Image $right, array $options = []) Calculate dE76. * @throws Exception - * @method float deviate(array $options = []) Find image standard deviation. + * @method Image dECMC(Image $left, Image $right, array $options = []) Calculate dECMC. * @throws Exception - * @method Image draw_circle(float[]|float $ink, integer $cx, integer $cy, integer $radius, array $options = []) Draw a circle on an image. + * @method float deviate(Image $in, array $options = []) Find image standard deviation. * @throws Exception - * @method Image draw_flood(float[]|float $ink, integer $x, integer $y, array $options = []) Flood-fill an area. + * @method Image draw_circle(Image $image, float[]|float $ink, integer $cx, integer $cy, integer $radius, array $options = []) Draw a circle on an image. * @throws Exception - * @method Image draw_image(Image $sub, integer $x, integer $y, array $options = []) Paint an image into another image. + * @method Image draw_flood(Image $image, float[]|float $ink, integer $x, integer $y, array $options = []) Flood-fill an area. * @throws Exception - * @method Image draw_line(float[]|float $ink, integer $x1, integer $y1, integer $x2, integer $y2, array $options = []) Draw a line on an image. + * @method Image draw_image(Image $image, Image $sub, integer $x, integer $y, array $options = []) Paint an image into another image. * @throws Exception - * @method Image draw_mask(float[]|float $ink, Image $mask, integer $x, integer $y, array $options = []) Draw a mask on an image. + * @method Image draw_line(Image $image, float[]|float $ink, integer $x1, integer $y1, integer $x2, integer $y2, array $options = []) Draw a line on an image. * @throws Exception - * @method Image draw_rect(float[]|float $ink, integer $left, integer $top, integer $width, integer $height, array $options = []) Paint a rectangle on an image. + * @method Image draw_mask(Image $image, float[]|float $ink, Image $mask, integer $x, integer $y, array $options = []) Draw a mask on an image. * @throws Exception - * @method Image draw_smudge(integer $left, integer $top, integer $width, integer $height, array $options = []) Blur a rectangle on an image. + * @method Image draw_rect(Image $image, float[]|float $ink, integer $left, integer $top, integer $width, integer $height, array $options = []) Paint a rectangle on an image. * @throws Exception - * @method void dzsave(string $filename, array $options = []) Save image to deepzoom file. + * @method Image draw_smudge(Image $image, integer $left, integer $top, integer $width, integer $height, array $options = []) Blur a rectangle on an image. * @throws Exception - * @method string dzsave_buffer(array $options = []) Save image to dz buffer. + * @method void dzsave(Image $in, string $filename, array $options = []) Save image to deepzoom file. * @throws Exception - * @method Image embed(integer $x, integer $y, integer $width, integer $height, array $options = []) Embed an image in a larger image. + * @method string dzsave_buffer(Image $in, array $options = []) Save image to dz buffer. * @throws Exception - * @method Image extract_area(integer $left, integer $top, integer $width, integer $height, array $options = []) Extract an area from an image. + * @method Image embed(Image $in, integer $x, integer $y, integer $width, integer $height, array $options = []) Embed an image in a larger image. * @throws Exception - * @method Image extract_band(integer $band, array $options = []) Extract band from an image. + * @method Image extract_area(Image $input, integer $left, integer $top, integer $width, integer $height, array $options = []) Extract an area from an image. + * @throws Exception + * @method Image extract_band(Image $in, integer $band, array $options = []) Extract band from an image. * @throws Exception * @method static Image eye(integer $width, integer $height, array $options = []) Make an image showing the eye's spatial response. * @throws Exception - * @method Image falsecolour(array $options = []) False-color an image. + * @method Image falsecolour(Image $in, array $options = []) False-color an image. * @throws Exception - * @method Image fastcor(Image $ref, array $options = []) Fast correlation. + * @method Image fastcor(Image $in, Image $ref, array $options = []) Fast correlation. * @throws Exception - * @method Image fill_nearest(array $options = []) Fill image zeros with nearest non-zero pixel. + * @method Image fill_nearest(Image $in, array $options = []) Fill image zeros with nearest non-zero pixel. * @throws Exception - * @method array find_trim(array $options = []) Search an image for non-edge areas. + * @method array find_trim(Image $in, array $options = []) Search an image for non-edge areas. * Return array with: [ * 'left' => @type integer Left edge of image * 'top' => @type integer Top edge of extract area @@ -217,104 +227,122 @@ * @throws Exception * @method static Image fitsload(string $filename, array $options = []) Load a FITS image. * @throws Exception - * @method void fitssave(string $filename, array $options = []) Save image to fits file. + * @method void fitssave(Image $in, string $filename, array $options = []) Save image to fits file. * @throws Exception - * @method Image flatten(array $options = []) Flatten alpha out of an image. + * @method Image flatten(Image $in, array $options = []) Flatten alpha out of an image. * @throws Exception - * @method Image flip(string $direction, array $options = []) Flip an image. + * @method Image flip(Image $in, string $direction, array $options = []) Flip an image. * @see Direction for possible values for $direction * @throws Exception - * @method Image float2rad(array $options = []) Transform float RGB to Radiance coding. + * @method Image float2rad(Image $in, array $options = []) Transform float RGB to Radiance coding. * @throws Exception * @method static Image fractsurf(integer $width, integer $height, float $fractal_dimension, array $options = []) Make a fractal surface. * @throws Exception - * @method Image freqmult(Image $mask, array $options = []) Frequency-domain filtering. + * @method Image freqmult(Image $in, Image $mask, array $options = []) Frequency-domain filtering. * @throws Exception - * @method Image fwfft(array $options = []) Forward FFT. + * @method Image fwfft(Image $in, array $options = []) Forward FFT. * @throws Exception - * @method Image gamma(array $options = []) Gamma an image. + * @method Image gamma(Image $in, array $options = []) Gamma an image. * @throws Exception - * @method Image gaussblur(float $sigma, array $options = []) Gaussian blur. + * @method Image gaussblur(Image $in, float $sigma, array $options = []) Gaussian blur. * @throws Exception * @method static Image gaussmat(float $sigma, float $min_ampl, array $options = []) Make a gaussian image. * @throws Exception * @method static Image gaussnoise(integer $width, integer $height, array $options = []) Make a gaussnoise image. * @throws Exception - * @method array getpoint(integer $x, integer $y, array $options = []) Read a point from an image. + * @method array getpoint(Image $in, integer $x, integer $y, array $options = []) Read a point from an image. * @throws Exception * @method static Image gifload(string $filename, array $options = []) Load GIF with giflib. * @throws Exception * @method static Image gifload_buffer(string $buffer, array $options = []) Load GIF with giflib. * @throws Exception - * @method Image globalbalance(array $options = []) Global balance an image mosaic. + * @method static Image gifload_source(string $source, array $options = []) Load GIF with giflib. + * @throws Exception + * @method Image globalbalance(Image $in, array $options = []) Global balance an image mosaic. * @throws Exception - * @method Image gravity(string $direction, integer $width, integer $height, array $options = []) Place an image within a larger image with a certain gravity. + * @method Image gravity(Image $in, string $direction, integer $width, integer $height, array $options = []) Place an image within a larger image with a certain gravity. * @see CompassDirection for possible values for $direction * @throws Exception * @method static Image grey(integer $width, integer $height, array $options = []) Make a grey ramp image. * @throws Exception - * @method Image grid(integer $tile_height, integer $across, integer $down, array $options = []) Grid an image. + * @method Image grid(Image $in, integer $tile_height, integer $across, integer $down, array $options = []) Grid an image. + * @throws Exception + * @method static Image heifload(string $filename, array $options = []) Load a HEIF image. + * @throws Exception + * @method static Image heifload_buffer(string $buffer, array $options = []) Load a HEIF image. * @throws Exception - * @method Image hist_cum(array $options = []) Form cumulative histogram. + * @method static Image heifload_source(string $source, array $options = []) Load a HEIF image. * @throws Exception - * @method float hist_entropy(array $options = []) Estimate image entropy. + * @method void heifsave(Image $in, string $filename, array $options = []) Save image in HEIF format. * @throws Exception - * @method Image hist_equal(array $options = []) Histogram equalisation. + * @method string heifsave_buffer(Image $in, array $options = []) Save image in HEIF format. * @throws Exception - * @method Image hist_find(array $options = []) Find image histogram. + * @method void heifsave_target(Image $in, string $target, array $options = []) Save image in HEIF format. * @throws Exception - * @method Image hist_find_indexed(Image $index, array $options = []) Find indexed image histogram. + * @method Image hist_cum(Image $in, array $options = []) Form cumulative histogram. * @throws Exception - * @method Image hist_find_ndim(array $options = []) Find n-dimensional image histogram. + * @method float hist_entropy(Image $in, array $options = []) Estimate image entropy. * @throws Exception - * @method bool hist_ismonotonic(array $options = []) Test for monotonicity. + * @method Image hist_equal(Image $in, array $options = []) Histogram equalisation. * @throws Exception - * @method Image hist_local(integer $width, integer $height, array $options = []) Local histogram equalisation. + * @method Image hist_find(Image $in, array $options = []) Find image histogram. * @throws Exception - * @method Image hist_match(Image $ref, array $options = []) Match two histograms. + * @method Image hist_find_indexed(Image $in, Image $index, array $options = []) Find indexed image histogram. * @throws Exception - * @method Image hist_norm(array $options = []) Normalise histogram. + * @method Image hist_find_ndim(Image $in, array $options = []) Find n-dimensional image histogram. * @throws Exception - * @method Image hist_plot(array $options = []) Plot histogram. + * @method bool hist_ismonotonic(Image $in, array $options = []) Test for monotonicity. * @throws Exception - * @method Image hough_circle(array $options = []) Find hough circle transform. + * @method Image hist_local(Image $in, integer $width, integer $height, array $options = []) Local histogram equalisation. * @throws Exception - * @method Image hough_line(array $options = []) Find hough line transform. + * @method Image hist_match(Image $in, Image $ref, array $options = []) Match two histograms. * @throws Exception - * @method Image icc_export(array $options = []) Output to device with ICC profile. + * @method Image hist_norm(Image $in, array $options = []) Normalise histogram. * @throws Exception - * @method Image icc_import(array $options = []) Import from device with ICC profile. + * @method Image hist_plot(Image $in, array $options = []) Plot histogram. * @throws Exception - * @method Image icc_transform(string $output_profile, array $options = []) Transform between devices with ICC profiles. + * @method Image hough_circle(Image $in, array $options = []) Find hough circle transform. + * @throws Exception + * @method Image hough_line(Image $in, array $options = []) Find hough line transform. + * @throws Exception + * @method Image icc_export(Image $in, array $options = []) Output to device with ICC profile. + * @throws Exception + * @method Image icc_import(Image $in, array $options = []) Import from device with ICC profile. + * @throws Exception + * @method Image icc_transform(Image $in, string $output_profile, array $options = []) Transform between devices with ICC profiles. * @throws Exception * @method static Image identity(array $options = []) Make a 1D image where pixel values are indexes. * @throws Exception - * @method Image insert(Image $sub, integer $x, integer $y, array $options = []) Insert image @sub into @main at @x, @y. + * @method Image insert(Image $main, Image $sub, integer $x, integer $y, array $options = []) Insert image @sub into @main at @x, @y. * @throws Exception - * @method Image invert(array $options = []) Invert an image. + * @method Image invert(Image $in, array $options = []) Invert an image. * @throws Exception - * @method Image invertlut(array $options = []) Build an inverted look-up table. + * @method Image invertlut(Image $in, array $options = []) Build an inverted look-up table. * @throws Exception - * @method Image invfft(array $options = []) Inverse FFT. + * @method Image invfft(Image $in, array $options = []) Inverse FFT. * @throws Exception - * @method Image join(Image $in2, string $direction, array $options = []) Join a pair of images. + * @method Image join(Image $in1, Image $in2, string $direction, array $options = []) Join a pair of images. * @see Direction for possible values for $direction * @throws Exception * @method static Image jpegload(string $filename, array $options = []) Load jpeg from file. * @throws Exception * @method static Image jpegload_buffer(string $buffer, array $options = []) Load jpeg from buffer. * @throws Exception - * @method void jpegsave(string $filename, array $options = []) Save image to jpeg file. + * @method static Image jpegload_source(string $source, array $options = []) Load image from jpeg source. * @throws Exception - * @method string jpegsave_buffer(array $options = []) Save image to jpeg buffer. + * @method void jpegsave(Image $in, string $filename, array $options = []) Save image to jpeg file. * @throws Exception - * @method void jpegsave_mime(array $options = []) Save image to jpeg mime. + * @method string jpegsave_buffer(Image $in, array $options = []) Save image to jpeg buffer. * @throws Exception - * @method Image labelregions(array $options = []) Label regions in an image. + * @method void jpegsave_mime(Image $in, array $options = []) Save image to jpeg mime. * @throws Exception - * @method Image linear(float[]|float $a, float[]|float $b, array $options = []) Calculate (a * in + b). + * @method void jpegsave_target(Image $in, string $target, array $options = []) Save image to jpeg target. * @throws Exception - * @method Image linecache(array $options = []) Cache an image as a set of lines. + * @method Image labelregions(Image $in, array $options = []) Label regions in an image. + * @throws Exception + * @method Image linear(Image $in, float[]|float $a, float[]|float $b, array $options = []) Calculate (a * in + b). + * @throws Exception + * @method Image linecache(Image $in, array $options = []) Cache an image as a set of lines. * @throws Exception * @method static Image logmat(float $sigma, float $min_ampl, array $options = []) Make a laplacian of gaussian image. * @throws Exception @@ -322,13 +350,13 @@ * @throws Exception * @method static Image magickload_buffer(string $buffer, array $options = []) Load buffer with ImageMagick. * @throws Exception - * @method void magicksave(string $filename, array $options = []) Save file with ImageMagick. + * @method void magicksave(Image $in, string $filename, array $options = []) Save file with ImageMagick. * @throws Exception - * @method string magicksave_buffer(array $options = []) Save image to magick buffer. + * @method string magicksave_buffer(Image $in, array $options = []) Save image to magick buffer. * @throws Exception - * @method Image mapim(Image $index, array $options = []) Resample with an mapim image. + * @method Image mapim(Image $in, Image $index, array $options = []) Resample with a map image. * @throws Exception - * @method Image maplut(Image $lut, array $options = []) Map an image though a lut. + * @method Image maplut(Image $in, Image $lut, array $options = []) Map an image though a lut. * @throws Exception * @method static Image mask_butterworth(integer $width, integer $height, float $order, float $frequency_cutoff, float $amplitude_cutoff, array $options = []) Make a butterworth filter. * @throws Exception @@ -350,178 +378,202 @@ * @throws Exception * @method static Image mask_ideal_ring(integer $width, integer $height, float $frequency_cutoff, float $ringwidth, array $options = []) Make an ideal ring filter. * @throws Exception - * @method Image match(Image $sec, integer $xr1, integer $yr1, integer $xs1, integer $ys1, integer $xr2, integer $yr2, integer $xs2, integer $ys2, array $options = []) First-order match of two images. + * @method Image match(Image $ref, Image $sec, integer $xr1, integer $yr1, integer $xs1, integer $ys1, integer $xr2, integer $yr2, integer $xs2, integer $ys2, array $options = []) First-order match of two images. * @throws Exception - * @method Image math(string $math, array $options = []) Apply a math operation to an image. + * @method Image math(Image $in, string $math, array $options = []) Apply a math operation to an image. * @see OperationMath for possible values for $math * @throws Exception - * @method Image math2(Image $right, string $math2, array $options = []) Binary math operations. + * @method Image math2(Image $left, Image $right, string $math2, array $options = []) Binary math operations. * @see OperationMath2 for possible values for $math2 * @throws Exception - * @method Image math2_const(string $math2, float[]|float $c, array $options = []) Binary math operations with a constant. + * @method Image math2_const(Image $in, string $math2, float[]|float $c, array $options = []) Binary math operations with a constant. * @see OperationMath2 for possible values for $math2 * @throws Exception * @method static Image matload(string $filename, array $options = []) Load mat from file. * @throws Exception - * @method static Image matrixload(string $filename, array $options = []) Load matrix from file. + * @method Image matrixinvert(Image $in, array $options = []) Invert an matrix. + * @throws Exception + * @method static Image matrixload(string $filename, array $options = []) Load matrix. + * @throws Exception + * @method static Image matrixload_source(string $source, array $options = []) Load matrix. * @throws Exception - * @method void matrixprint(array $options = []) Print matrix. + * @method void matrixprint(Image $in, array $options = []) Print matrix. * @throws Exception - * @method void matrixsave(string $filename, array $options = []) Save image to matrix file. + * @method void matrixsave(Image $in, string $filename, array $options = []) Save image to matrix. * @throws Exception - * @method float max(array $options = []) Find image maximum. + * @method void matrixsave_target(Image $in, string $target, array $options = []) Save image to matrix. * @throws Exception - * @method Image measure(integer $h, integer $v, array $options = []) Measure a set of patches on a color chart. + * @method float max(Image $in, array $options = []) Find image maximum. * @throws Exception - * @method Image merge(Image $sec, string $direction, integer $dx, integer $dy, array $options = []) Merge two images. + * @method Image measure(Image $in, integer $h, integer $v, array $options = []) Measure a set of patches on a color chart. + * @throws Exception + * @method Image merge(Image $ref, Image $sec, string $direction, integer $dx, integer $dy, array $options = []) Merge two images. * @see Direction for possible values for $direction * @throws Exception - * @method float min(array $options = []) Find image minimum. + * @method float min(Image $in, array $options = []) Find image minimum. * @throws Exception - * @method Image morph(Image $mask, string $morph, array $options = []) Morphology operation. + * @method Image morph(Image $in, Image $mask, string $morph, array $options = []) Morphology operation. * @see OperationMorphology for possible values for $morph * @throws Exception - * @method Image mosaic(Image $sec, string $direction, integer $xref, integer $yref, integer $xsec, integer $ysec, array $options = []) Mosaic two images. + * @method Image mosaic(Image $ref, Image $sec, string $direction, integer $xref, integer $yref, integer $xsec, integer $ysec, array $options = []) Mosaic two images. * @see Direction for possible values for $direction * @throws Exception - * @method Image mosaic1(Image $sec, string $direction, integer $xr1, integer $yr1, integer $xs1, integer $ys1, integer $xr2, integer $yr2, integer $xs2, integer $ys2, array $options = []) First-order mosaic of two images. + * @method Image mosaic1(Image $ref, Image $sec, string $direction, integer $xr1, integer $yr1, integer $xs1, integer $ys1, integer $xr2, integer $yr2, integer $xs2, integer $ys2, array $options = []) First-order mosaic of two images. * @see Direction for possible values for $direction * @throws Exception - * @method Image msb(array $options = []) Pick most-significant byte from an image. + * @method Image msb(Image $in, array $options = []) Pick most-significant byte from an image. * @throws Exception * @method static Image niftiload(string $filename, array $options = []) Load a NIFTI image. * @throws Exception - * @method void niftisave(string $filename, array $options = []) Save image to nifti file. + * @method void niftisave(Image $in, string $filename, array $options = []) Save image to nifti file. * @throws Exception * @method static Image openexrload(string $filename, array $options = []) Load an OpenEXR image. * @throws Exception * @method static Image openslideload(string $filename, array $options = []) Load file with OpenSlide. * @throws Exception - * @method static Image pdfload(string $filename, array $options = []) Load PDF with libpoppler. + * @method static Image pdfload(string $filename, array $options = []) Load PDF from file. + * @throws Exception + * @method static Image pdfload_buffer(string $buffer, array $options = []) Load PDF from buffer. * @throws Exception - * @method static Image pdfload_buffer(string $buffer, array $options = []) Load PDF with libpoppler. + * @method static Image pdfload_source(string $source, array $options = []) Load PDF from source. * @throws Exception - * @method integer percent(float $percent, array $options = []) Find threshold for percent of pixels. + * @method integer percent(Image $in, float $percent, array $options = []) Find threshold for percent of pixels. * @throws Exception * @method static Image perlin(integer $width, integer $height, array $options = []) Make a perlin noise image. * @throws Exception - * @method Image phasecor(Image $in2, array $options = []) Calculate phase correlation. + * @method Image phasecor(Image $in, Image $in2, array $options = []) Calculate phase correlation. * @throws Exception * @method static Image pngload(string $filename, array $options = []) Load png from file. * @throws Exception * @method static Image pngload_buffer(string $buffer, array $options = []) Load png from buffer. * @throws Exception - * @method void pngsave(string $filename, array $options = []) Save image to png file. + * @method static Image pngload_source(string $source, array $options = []) Load png from source. + * @throws Exception + * @method void pngsave(Image $in, string $filename, array $options = []) Save image to png file. + * @throws Exception + * @method string pngsave_buffer(Image $in, array $options = []) Save image to png buffer. * @throws Exception - * @method string pngsave_buffer(array $options = []) Save image to png buffer. + * @method void pngsave_target(Image $in, string $target, array $options = []) Save image to target as PNG. * @throws Exception * @method static Image ppmload(string $filename, array $options = []) Load ppm from file. * @throws Exception - * @method void ppmsave(string $filename, array $options = []) Save image to ppm file. + * @method static Image ppmload_source(string $source, array $options = []) Load ppm base class. * @throws Exception - * @method Image premultiply(array $options = []) Premultiply image alpha. + * @method void ppmsave(Image $in, string $filename, array $options = []) Save image to ppm file. * @throws Exception - * @method array profile(array $options = []) Find image profiles. + * @method void ppmsave_target(Image $in, string $target, array $options = []) Save to ppm. + * @throws Exception + * @method Image premultiply(Image $in, array $options = []) Premultiply image alpha. + * @throws Exception + * @method array profile(Image $in, array $options = []) Find image profiles. * Return array with: [ * 'columns' => @type Image First non-zero pixel in column * 'rows' => @type Image First non-zero pixel in row * ]; * @throws Exception - * @method array project(array $options = []) Find image projections. + * @method static string profile_load(string $name, array $options = []) Load named ICC profile. + * @throws Exception + * @method array project(Image $in, array $options = []) Find image projections. * Return array with: [ * 'columns' => @type Image Sums of columns * 'rows' => @type Image Sums of rows * ]; * @throws Exception - * @method Image quadratic(Image $coeff, array $options = []) Resample an image with a quadratic transform. + * @method Image quadratic(Image $in, Image $coeff, array $options = []) Resample an image with a quadratic transform. * @throws Exception - * @method Image rad2float(array $options = []) Unpack Radiance coding to float RGB. + * @method Image rad2float(Image $in, array $options = []) Unpack Radiance coding to float RGB. * @throws Exception * @method static Image radload(string $filename, array $options = []) Load a Radiance image from a file. * @throws Exception - * @method void radsave(string $filename, array $options = []) Save image to Radiance file. + * @method static Image radload_buffer(string $buffer, array $options = []) Load rad from buffer. + * @throws Exception + * @method static Image radload_source(string $source, array $options = []) Load rad from source. * @throws Exception - * @method string radsave_buffer(array $options = []) Save image to Radiance buffer. + * @method void radsave(Image $in, string $filename, array $options = []) Save image to Radiance file. * @throws Exception - * @method Image rank(integer $width, integer $height, integer $index, array $options = []) Rank filter. + * @method string radsave_buffer(Image $in, array $options = []) Save image to Radiance buffer. + * @throws Exception + * @method void radsave_target(Image $in, string $target, array $options = []) Save image to Radiance target. + * @throws Exception + * @method Image rank(Image $in, integer $width, integer $height, integer $index, array $options = []) Rank filter. * @throws Exception * @method static Image rawload(string $filename, integer $width, integer $height, integer $bands, array $options = []) Load raw data from a file. * @throws Exception - * @method void rawsave(string $filename, array $options = []) Save image to raw file. + * @method void rawsave(Image $in, string $filename, array $options = []) Save image to raw file. * @throws Exception - * @method void rawsave_fd(integer $fd, array $options = []) Write raw image to file descriptor. + * @method void rawsave_fd(Image $in, integer $fd, array $options = []) Write raw image to file descriptor. * @throws Exception - * @method Image recomb(Image $m, array $options = []) Linear recombination with matrix. + * @method Image recomb(Image $in, Image $m, array $options = []) Linear recombination with matrix. * @throws Exception - * @method Image reduce(float $hshrink, float $vshrink, array $options = []) Reduce an image. + * @method Image reduce(Image $in, float $hshrink, float $vshrink, array $options = []) Reduce an image. * @throws Exception - * @method Image reduceh(float $hshrink, array $options = []) Shrink an image horizontally. + * @method Image reduceh(Image $in, float $hshrink, array $options = []) Shrink an image horizontally. * @throws Exception - * @method Image reducev(float $vshrink, array $options = []) Shrink an image vertically. + * @method Image reducev(Image $in, float $vshrink, array $options = []) Shrink an image vertically. * @throws Exception - * @method Image relational(Image $right, string $relational, array $options = []) Relational operation on two images. + * @method Image relational(Image $left, Image $right, string $relational, array $options = []) Relational operation on two images. * @see OperationRelational for possible values for $relational * @throws Exception - * @method Image relational_const(string $relational, float[]|float $c, array $options = []) Relational operations against a constant. + * @method Image relational_const(Image $in, string $relational, float[]|float $c, array $options = []) Relational operations against a constant. * @see OperationRelational for possible values for $relational * @throws Exception - * @method Image remainder_const(float[]|float $c, array $options = []) Remainder after integer division of an image and a constant. + * @method Image remainder_const(Image $in, float[]|float $c, array $options = []) Remainder after integer division of an image and a constant. * @throws Exception - * @method Image replicate(integer $across, integer $down, array $options = []) Replicate an image. + * @method Image replicate(Image $in, integer $across, integer $down, array $options = []) Replicate an image. * @throws Exception - * @method Image resize(float $scale, array $options = []) Resize an image. + * @method Image resize(Image $in, float $scale, array $options = []) Resize an image. * @throws Exception - * @method Image rot(string $angle, array $options = []) Rotate an image. + * @method Image rot(Image $in, string $angle, array $options = []) Rotate an image. * @see Angle for possible values for $angle * @throws Exception - * @method Image rot45(array $options = []) Rotate an image. + * @method Image rot45(Image $in, array $options = []) Rotate an image. * @throws Exception - * @method Image rotate(float $angle, array $options = []) Rotate an image by a number of degrees. + * @method Image rotate(Image $in, float $angle, array $options = []) Rotate an image by a number of degrees. * @throws Exception - * @method Image round(string $round, array $options = []) Perform a round function on an image. + * @method Image round(Image $in, string $round, array $options = []) Perform a round function on an image. * @see OperationRound for possible values for $round * @throws Exception - * @method Image sRGB2HSV(array $options = []) Transform sRGB to HSV. + * @method Image sRGB2HSV(Image $in, array $options = []) Transform sRGB to HSV. * @throws Exception - * @method Image sRGB2scRGB(array $options = []) Convert an sRGB image to scRGB. + * @method Image sRGB2scRGB(Image $in, array $options = []) Convert an sRGB image to scRGB. * @throws Exception - * @method Image scRGB2BW(array $options = []) Convert scRGB to BW. + * @method Image scRGB2BW(Image $in, array $options = []) Convert scRGB to BW. * @throws Exception - * @method Image scRGB2XYZ(array $options = []) Transform scRGB to XYZ. + * @method Image scRGB2XYZ(Image $in, array $options = []) Transform scRGB to XYZ. * @throws Exception - * @method Image scRGB2sRGB(array $options = []) Convert an scRGB image to sRGB. + * @method Image scRGB2sRGB(Image $in, array $options = []) Convert an scRGB image to sRGB. * @throws Exception - * @method Image scale(array $options = []) Scale an image to uchar. + * @method Image scale(Image $in, array $options = []) Scale an image to uchar. * @throws Exception - * @method Image sequential(array $options = []) Check sequential access. + * @method Image sequential(Image $in, array $options = []) Check sequential access. * @throws Exception - * @method Image sharpen(array $options = []) Unsharp masking for print. + * @method Image sharpen(Image $in, array $options = []) Unsharp masking for print. * @throws Exception - * @method Image shrink(float $hshrink, float $vshrink, array $options = []) Shrink an image. + * @method Image shrink(Image $in, float $hshrink, float $vshrink, array $options = []) Shrink an image. * @throws Exception - * @method Image shrinkh(integer $hshrink, array $options = []) Shrink an image horizontally. + * @method Image shrinkh(Image $in, integer $hshrink, array $options = []) Shrink an image horizontally. * @throws Exception - * @method Image shrinkv(integer $vshrink, array $options = []) Shrink an image vertically. + * @method Image shrinkv(Image $in, integer $vshrink, array $options = []) Shrink an image vertically. * @throws Exception - * @method Image sign(array $options = []) Unit vector of pixel. + * @method Image sign(Image $in, array $options = []) Unit vector of pixel. * @throws Exception - * @method Image similarity(array $options = []) Similarity transform of an image. + * @method Image similarity(Image $in, array $options = []) Similarity transform of an image. * @throws Exception * @method static Image sines(integer $width, integer $height, array $options = []) Make a 2D sine wave. * @throws Exception - * @method Image smartcrop(integer $width, integer $height, array $options = []) Extract an area from an image. + * @method Image smartcrop(Image $input, integer $width, integer $height, array $options = []) Extract an area from an image. * @throws Exception - * @method Image sobel(array $options = []) Sobel edge detector. + * @method Image sobel(Image $in, array $options = []) Sobel edge detector. * @throws Exception - * @method Image spcor(Image $ref, array $options = []) Spatial correlation. + * @method Image spcor(Image $in, Image $ref, array $options = []) Spatial correlation. * @throws Exception - * @method Image spectrum(array $options = []) Make displayable power spectrum. + * @method Image spectrum(Image $in, array $options = []) Make displayable power spectrum. * @throws Exception - * @method Image stats(array $options = []) Find many image stats. + * @method Image stats(Image $in, array $options = []) Find many image stats. * @throws Exception - * @method Image stdif(integer $width, integer $height, array $options = []) Statistical difference. + * @method Image stdif(Image $in, integer $width, integer $height, array $options = []) Statistical difference. * @throws Exception - * @method Image subsample(integer $xfac, integer $yfac, array $options = []) Subsample an image. + * @method Image subsample(Image $input, integer $xfac, integer $yfac, array $options = []) Subsample an image. * @throws Exception * @method static Image sum(Image[]|Image $in, array $options = []) Sum an array of images. * @throws Exception @@ -529,6 +581,10 @@ * @throws Exception * @method static Image svgload_buffer(string $buffer, array $options = []) Load SVG with rsvg. * @throws Exception + * @method static Image svgload_source(string $source, array $options = []) Load svg from source. + * @throws Exception + * @method static Image switch(Image[]|Image $tests, array $options = []) Find the index of the first non-zero pixel in tests. + * @throws Exception * @method static void system(string $cmd_format, array $options = []) Run an external command. * @throws Exception * @method static Image text(string $text, array $options = []) Make a text image. @@ -537,45 +593,53 @@ * @throws Exception * @method static Image thumbnail_buffer(string $buffer, integer $width, array $options = []) Generate thumbnail from buffer. * @throws Exception - * @method Image thumbnail_image(integer $width, array $options = []) Generate thumbnail from image. + * @method Image thumbnail_image(Image $in, integer $width, array $options = []) Generate thumbnail from image. + * @throws Exception + * @method static Image thumbnail_source(string $source, integer $width, array $options = []) Generate thumbnail from source. * @throws Exception * @method static Image tiffload(string $filename, array $options = []) Load tiff from file. * @throws Exception * @method static Image tiffload_buffer(string $buffer, array $options = []) Load tiff from buffer. * @throws Exception - * @method void tiffsave(string $filename, array $options = []) Save image to tiff file. + * @method static Image tiffload_source(string $source, array $options = []) Load tiff from source. + * @throws Exception + * @method void tiffsave(Image $in, string $filename, array $options = []) Save image to tiff file. * @throws Exception - * @method string tiffsave_buffer(array $options = []) Save image to tiff buffer. + * @method string tiffsave_buffer(Image $in, array $options = []) Save image to tiff buffer. * @throws Exception - * @method Image tilecache(array $options = []) Cache an image as a set of tiles. + * @method Image tilecache(Image $in, array $options = []) Cache an image as a set of tiles. * @throws Exception * @method static Image tonelut(array $options = []) Build a look-up table. * @throws Exception - * @method Image transpose3d(array $options = []) Transpose3d an image. + * @method Image transpose3d(Image $in, array $options = []) Transpose3d an image. * @throws Exception - * @method Image unpremultiply(array $options = []) Unpremultiply image alpha. + * @method Image unpremultiply(Image $in, array $options = []) Unpremultiply image alpha. * @throws Exception * @method static Image vipsload(string $filename, array $options = []) Load vips from file. * @throws Exception - * @method void vipssave(string $filename, array $options = []) Save image to vips file. + * @method void vipssave(Image $in, string $filename, array $options = []) Save image to vips file. * @throws Exception * @method static Image webpload(string $filename, array $options = []) Load webp from file. * @throws Exception * @method static Image webpload_buffer(string $buffer, array $options = []) Load webp from buffer. * @throws Exception - * @method void webpsave(string $filename, array $options = []) Save image to webp file. + * @method static Image webpload_source(string $source, array $options = []) Load webp from source. + * @throws Exception + * @method void webpsave(Image $in, string $filename, array $options = []) Save image to webp file. + * @throws Exception + * @method string webpsave_buffer(Image $in, array $options = []) Save image to webp buffer. * @throws Exception - * @method string webpsave_buffer(array $options = []) Save image to webp buffer. + * @method void webpsave_target(Image $in, string $target, array $options = []) Save image to webp target. * @throws Exception * @method static Image worley(integer $width, integer $height, array $options = []) Make a worley noise image. * @throws Exception - * @method Image wrap(array $options = []) Wrap image origin. + * @method Image wrap(Image $in, array $options = []) Wrap image origin. * @throws Exception * @method static Image xyz(integer $width, integer $height, array $options = []) Make an image where pixel values are coordinates. * @throws Exception * @method static Image zone(integer $width, integer $height, array $options = []) Make a zone plate. * @throws Exception - * @method Image zoom(integer $xfac, integer $yfac, array $options = []) Zoom an image. + * @method Image zoom(Image $input, integer $xfac, integer $yfac, array $options = []) Zoom an image. * @throws Exception * * @property integer $width Image width in pixels diff --git a/src/Interesting.php b/src/Interesting.php index 2d64c21..b7166cf 100644 --- a/src/Interesting.php +++ b/src/Interesting.php @@ -53,4 +53,7 @@ abstract class Interesting const CENTRE = 'centre'; const ENTROPY = 'entropy'; const ATTENTION = 'attention'; + const LOW = 'low'; + const HIGH = 'high'; + const ALL = 'all'; } diff --git a/src/RegionShrink.php b/src/RegionShrink.php index ebf1d50..48773d0 100644 --- a/src/RegionShrink.php +++ b/src/RegionShrink.php @@ -52,4 +52,7 @@ abstract class RegionShrink const MEAN = 'mean'; const MEDIAN = 'median'; const MODE = 'mode'; + const MAX = 'max'; + const MIN = 'min'; + const NEAREST = 'nearest'; } From 3a6fba8b1f203550c5df43dbaf9146d7b650f0d3 Mon Sep 17 00:00:00 2001 From: Kleis Auke Wolthuizen Date: Fri, 28 Aug 2020 13:30:43 +0200 Subject: [PATCH 015/123] Take advantage of PHP >= 7.1 functionality - Use nullable types and void return type were possible. - Fix autodocs for non-static methods. - Update Travis configuration (use bionic and update to vips 8.10). - Update dependencies. - Improve install-vips.sh script. --- .gitignore | 1 + .travis.yml | 46 +++-- CHANGELOG.md | 17 ++ README.md | 3 +- composer.json | 17 +- examples/generate_phpdoc.py | 4 +- install-vips.sh | 27 ++- phpcs-ruleset.xml | 3 + phpunit.xml | 3 +- src/Config.php | 18 +- src/Image.php | 40 ++-- src/ImageAutodoc.php | 402 ++++++++++++++++++------------------ src/Utils.php | 10 +- tests/ConvenienceTest.php | 2 +- tests/ExceptionTest.php | 2 +- tests/MetaTest.php | 2 +- tests/ShortcutTest.php | 2 +- tests/WriteTest.php | 4 +- 18 files changed, 311 insertions(+), 292 deletions(-) diff --git a/.gitignore b/.gitignore index a66d4dd..1cd2994 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ vendor composer.lock /docs *.swp +.phpunit.result.cache diff --git a/.travis.yml b/.travis.yml index c8d5c9b..914efa1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,23 +1,17 @@ -sudo: false - language: php -dist: trusty +dist: bionic php: - - 7.0.13 - 7.1 + - 7.4 env: global: - - VIPS_VERSION_MAJOR=8 - - VIPS_VERSION_MINOR=6 - - VIPS_VERSION_MICRO=3 + - VIPS_VERSION=8.10.0 - PATH=$HOME/vips/bin:$PATH - LD_LIBRARY_PATH=$HOME/vips/lib:$LD_LIBRARY_PATH - PKG_CONFIG_PATH=$HOME/vips/lib/pkgconfig:$PKG_CONFIG_PATH - - PYTHONPATH=$HOME/vips/lib/python2.7/site-packages:$PYTHONPATH - - GI_TYPELIB_PATH=$HOME/vips/lib/girepository-1.0:$GI_TYPELIB_PATH cache: apt: true @@ -28,30 +22,38 @@ cache: addons: apt: packages: - - gobject-introspection - - libcfitsio3-dev + # main dependencies + - libcfitsio-dev + - libexif-dev + - libexpat1-dev - libfftw3-dev - libgif-dev - - libgs-dev - libgsf-1-dev + - libgsl-dev + - libheif-dev + - liblcms2-dev + - libmagickwand-dev - libmatio-dev + - libnifti-dev + - libopenexr-dev - libopenslide-dev - liborc-0.4-dev - libpango1.0-dev + - libpng-dev - libpoppler-glib-dev + - librsvg2-dev + - libtiff5-dev - libwebp-dev + # needed for building libvips from source + - gtk-doc-tools + - gobject-introspection before_install: - - bash install-vips.sh - --disable-debug - --disable-dependency-tracking - --disable-introspection - --disable-static - --enable-gtk-doc-html=no - --enable-gtk-doc=no - --enable-pyvips8=no - --without-orc - --without-python + - bash install-vips.sh + --disable-dependency-tracking + --disable-introspection + --disable-gtk-doc-html + --disable-gtk-doc - yes '' | pecl install vips install: composer install --prefer-dist diff --git a/CHANGELOG.md b/CHANGELOG.md index 4dc6cbc..f03a6fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,23 @@ # Changelog All notable changes to `:vips` will be documented in this file. +### 1.0.7 - 2020-08-28 + +### Added +- use nullable types and void return type were possible + +### Deprecated +- requires php >= 7.1 + +### Fixed +- fix autodocs for non-static methods + +### Remove +- Nothing + +### Security +- Nothing + ## 1.0.6 - 2020-08-28 ### Added diff --git a/README.md b/README.md index cf5b4bc..e3b067f 100644 --- a/README.md +++ b/README.md @@ -163,9 +163,8 @@ libvips properties as properties of the PHP `Vips\Image` class. ### Test and install ``` -$ phpcs --standard=PSR2 src $ composer install -$ vendor/bin/phpunit +$ composer test $ vendor/bin/phpdoc ``` diff --git a/composer.json b/composer.json index ee764e4..d472bf0 100644 --- a/composer.json +++ b/composer.json @@ -17,16 +17,15 @@ } ], "require": { - "php": ">=7.0.11", + "php": ">=7.1", "ext-vips": ">=0.1.2", - "psr/log": "^1.0.2" + "psr/log": "^1.1.3" }, "require-dev": { - "jms/serializer" : ">=0.12 < 1.8.0", - "phpunit/phpunit": "^6.5", - "phpdocumentor/phpdocumentor" : "^2.9", - "jakub-onderka/php-parallel-lint": "^1.0.0", - "squizlabs/php_codesniffer": "3.*" + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpdocumentor/phpdocumentor": "3.0.0-rc", + "phpunit/phpunit": "^9.3", + "squizlabs/php_codesniffer": "^3.5" }, "autoload": { "psr-4": { @@ -49,5 +48,7 @@ "phpunit", "phpcs --standard=phpcs-ruleset.xml ." ] - } + }, + "minimum-stability": "dev", + "prefer-stable": true } diff --git a/examples/generate_phpdoc.py b/examples/generate_phpdoc.py index d617893..7ad9eda 100755 --- a/examples/generate_phpdoc.py +++ b/examples/generate_phpdoc.py @@ -111,7 +111,7 @@ def generate_operation(operation_name): result += 'array ' result += '{0}('.format(operation_name) - for name in intro.required_input: + for name in intro.method_args: details = intro.details[name] result += '{0} ${1}, '.format(gtype_to_php(details['type']), name) @@ -121,7 +121,7 @@ def generate_operation(operation_name): result += description[0].upper() + description[1:] + '.\n' # find any Enums we've referenced and output @see lines for them - for name in intro.required_output + intro.required_input: + for name in intro.required_output + intro.method_args: details = intro.details[name] fundamental = gobject_lib.g_type_fundamental(details['type']) diff --git a/install-vips.sh b/install-vips.sh index d09dc89..db7c4b6 100755 --- a/install-vips.sh +++ b/install-vips.sh @@ -1,26 +1,25 @@ #!/bin/bash -vips_site=https://github.com/libvips/libvips/releases/download -version=$VIPS_VERSION_MAJOR.$VIPS_VERSION_MINOR.$VIPS_VERSION_MICRO +version=$VIPS_VERSION +vips_tarball=https://github.com/libvips/libvips/releases/download/v$version/vips-$version.tar.gz set -e # do we already have the correct vips built? early exit if yes # we could check the configure params as well I guess if [ -d "$HOME/vips/bin" ]; then - installed_version=$($HOME/vips/bin/vips --version) - escaped_version="$VIPS_VERSION_MAJOR\.$VIPS_VERSION_MINOR\.$VIPS_VERSION_MICRO" - echo "Need vips-$version" - echo "Found $installed_version" - if [[ "$installed_version" =~ ^vips-$escaped_version ]]; then - echo "Using cached directory" - exit 0 - fi + installed_version=$($HOME/vips/bin/vips --version | awk -F- '{print $2}') + echo "Need vips $version" + echo "Found vips $installed_version" + + if [ "$installed_version" == "$version" ]; then + echo "Using cached vips directory" + exit 0 + fi fi rm -rf $HOME/vips -wget $vips_site/v$version/vips-$version.tar.gz -tar xf vips-$version.tar.gz +curl -Ls $vips_tarball | tar xz cd vips-$version -CXXFLAGS=-D_GLIBCXX_USE_CXX11_ABI=0 ./configure --prefix=$HOME/vips $* -make && make install +CXXFLAGS=-D_GLIBCXX_USE_CXX11_ABI=0 ./configure --prefix=$HOME/vips "$@" +make -j`nproc` && make install diff --git a/phpcs-ruleset.xml b/phpcs-ruleset.xml index fd567e0..f2bc93b 100644 --- a/phpcs-ruleset.xml +++ b/phpcs-ruleset.xml @@ -9,6 +9,9 @@ vendor/ + + docs/ + diff --git a/phpunit.xml b/phpunit.xml index 0bc9183..cd2dbad 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -8,8 +8,7 @@ convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" - stopOnFailure="false" - syntaxCheck="false"> + stopOnFailure="false"> ./tests/ diff --git a/src/Config.php b/src/Config.php index 1c78b0c..b546870 100644 --- a/src/Config.php +++ b/src/Config.php @@ -71,7 +71,7 @@ class Config * * @return void */ - public static function setLogger(LoggerInterface $logger) + public static function setLogger(LoggerInterface $logger): void { self::$logger = $logger; } @@ -79,9 +79,9 @@ public static function setLogger(LoggerInterface $logger) /** * Gets a logger. * - * @return LoggerInterface $logger|null + * @return LoggerInterface|null The logger or null. */ - public static function getLogger() + public static function getLogger(): ?LoggerInterface { return self::$logger; } @@ -94,7 +94,7 @@ public static function getLogger() * * @return void */ - public static function cacheSetMax($value) + public static function cacheSetMax(int $value): void { vips_cache_set_max($value); } @@ -103,12 +103,12 @@ public static function cacheSetMax($value) * Set the maximum amount of memory to allow cached operations to use, in * bytes. * - * @param integer $value The maximum amount of memory cached opertations can + * @param integer $value The maximum amount of memory cached operations can * hold, in bytes. * * @return void */ - public static function cacheSetMaxMem($value) + public static function cacheSetMaxMem(int $value): void { vips_cache_set_max_mem($value); } @@ -121,7 +121,7 @@ public static function cacheSetMaxMem($value) * * @return void */ - public static function cacheSetMaxFiles($value) + public static function cacheSetMaxFiles(int $value): void { vips_cache_set_max_files($value); } @@ -135,13 +135,13 @@ public static function cacheSetMaxFiles($value) * * @return void */ - public static function concurrencySet($value) + public static function concurrencySet(int $value): void { vips_concurrency_set($value); } /** - * Gets the libvips version number as a atring of the form + * Gets the libvips version number as a string of the form * MAJOR.MINOR.MICRO, for example "8.6.1". * * @return string diff --git a/src/Image.php b/src/Image.php index 4fa7c8e..4d26810 100644 --- a/src/Image.php +++ b/src/Image.php @@ -772,7 +772,7 @@ private static function wrapResult($result) * * @internal */ - private static function errorVips() + private static function errorVips(): void { $message = vips_error_buffer(); $exception = new Exception($message); @@ -795,7 +795,7 @@ private static function errorVips() * * @internal */ - private static function errorIsArray($result) + private static function errorIsArray($result): void { if (!is_array($result)) { self::errorVips(); @@ -807,8 +807,8 @@ private static function errorIsArray($result) * format, try to make it complex by joining adjacant bands as real and * imaginary. * - * @param function $filename The function to run. - * @param Image $image The image to run the function on. + * @param \Closure $func The function to run. + * @param Image $image The image to run the function on. * * @throws Exception * @@ -816,7 +816,7 @@ private static function errorIsArray($result) * * @internal */ - private static function runCmplx($func, Image $image): Image + private static function runCmplx(\Closure $func, Image $image): Image { $original_format = $image->format; @@ -893,7 +893,7 @@ public static function newFromFile( * * @return string|null The name of the load operation, or null. */ - public static function findLoad(string $filename) + public static function findLoad(string $filename): ?string { Utils::debugLog('findLoad', [ 'instance' => null, @@ -963,7 +963,7 @@ public static function newFromBuffer( * * @return string|null The name of the load operation, or null. */ - public static function findLoadBuffer(string $buffer) + public static function findLoadBuffer(string $buffer): ?string { Utils::debugLog('findLoadBuffer', [ 'instance' => null, @@ -1145,7 +1145,7 @@ public function newFromImage($value): Image * * @return void */ - public function writeToFile(string $filename, array $options = []) + public function writeToFile(string $filename, array $options = []): void { Utils::debugLog('writeToFile', [ 'instance' => $this, @@ -1309,7 +1309,7 @@ public function __get(string $name) * * @return void */ - public function __set(string $name, $value) + public function __set(string $name, $value): void { vips_image_set($this->image, $name, $value); } @@ -1321,7 +1321,7 @@ public function __set(string $name, $value) * * @return bool */ - public function __isset(string $name) + public function __isset(string $name): bool { return $this->typeof($name) !== 0; } @@ -1375,7 +1375,7 @@ public function typeof(string $name): int * * @return void */ - public function set(string $name, $value) + public function set(string $name, $value): void { $result = vips_image_set($this->image, $name, $value); if ($result === -1) { @@ -1389,7 +1389,7 @@ public function set(string $name, $value) * This is useful if the type of the property cannot be determined from the * php type of the value. * - * Use Utils::typefromName() to look up types by name. + * Use Utils::typeFromName() to look up types by name. * * @param int $type The type of the property. * @param string $name The property name. @@ -1399,7 +1399,7 @@ public function set(string $name, $value) * * @return void */ - public function setType(int $type, string $name, $value) + public function setType(int $type, string $name, $value): void { $result = vips_image_set_type($this->image, $type, $name, $value); if ($result === -1) { @@ -1416,7 +1416,7 @@ public function setType(int $type, string $name, $value) * * @return void */ - public function remove(string $name) + public function remove(string $name): void { $result = vips_image_remove($this->image, $name); if ($result === -1) { @@ -1462,7 +1462,7 @@ public function __toString() */ public static function callBase( string $name, - $instance, + ?Image $instance, array $arguments ) { Utils::debugLog($name, [ @@ -1500,7 +1500,7 @@ public static function callBase( */ public static function call( string $name, - $instance, + ?Image $instance, array $arguments, array $options = [] ) { @@ -1611,9 +1611,9 @@ public function offsetExists($offset): bool * * @throws Exception * - * @return Image the extracted band. + * @return Image|null the extracted band or null. */ - public function offsetGet($offset): Image + public function offsetGet($offset): ?Image { return $this->offsetExists($offset) ? $this->extract_band($offset) : null; } @@ -1642,7 +1642,7 @@ public function offsetGet($offset): Image * * @return void */ - public function offsetSet($offset, $value) + public function offsetSet($offset, $value): void { // no offset means append if ($offset === null) { @@ -1688,7 +1688,7 @@ public function offsetSet($offset, $value) * * @return void */ - public function offsetUnset($offset) + public function offsetUnset($offset): void { if (is_int($offset) && $offset >= 0 && $offset < $this->bands) { if ($this->bands === 1) { diff --git a/src/ImageAutodoc.php b/src/ImageAutodoc.php index 0aef9c1..79abaf8 100644 --- a/src/ImageAutodoc.php +++ b/src/ImageAutodoc.php @@ -47,177 +47,177 @@ * @license https://opensource.org/licenses/MIT MIT * @link https://github.com/jcupitt/php-vips * - * @method Image CMC2LCh(Image $in, array $options = []) Transform LCh to CMC. + * @method Image CMC2LCh(array $options = []) Transform LCh to CMC. * @throws Exception - * @method Image CMYK2XYZ(Image $in, array $options = []) Transform CMYK to XYZ. + * @method Image CMYK2XYZ(array $options = []) Transform CMYK to XYZ. * @throws Exception - * @method Image HSV2sRGB(Image $in, array $options = []) Transform HSV to sRGB. + * @method Image HSV2sRGB(array $options = []) Transform HSV to sRGB. * @throws Exception - * @method Image LCh2CMC(Image $in, array $options = []) Transform LCh to CMC. + * @method Image LCh2CMC(array $options = []) Transform LCh to CMC. * @throws Exception - * @method Image LCh2Lab(Image $in, array $options = []) Transform LCh to Lab. + * @method Image LCh2Lab(array $options = []) Transform LCh to Lab. * @throws Exception - * @method Image Lab2LCh(Image $in, array $options = []) Transform Lab to LCh. + * @method Image Lab2LCh(array $options = []) Transform Lab to LCh. * @throws Exception - * @method Image Lab2LabQ(Image $in, array $options = []) Transform float Lab to LabQ coding. + * @method Image Lab2LabQ(array $options = []) Transform float Lab to LabQ coding. * @throws Exception - * @method Image Lab2LabS(Image $in, array $options = []) Transform float Lab to signed short. + * @method Image Lab2LabS(array $options = []) Transform float Lab to signed short. * @throws Exception - * @method Image Lab2XYZ(Image $in, array $options = []) Transform CIELAB to XYZ. + * @method Image Lab2XYZ(array $options = []) Transform CIELAB to XYZ. * @throws Exception - * @method Image LabQ2Lab(Image $in, array $options = []) Unpack a LabQ image to float Lab. + * @method Image LabQ2Lab(array $options = []) Unpack a LabQ image to float Lab. * @throws Exception - * @method Image LabQ2LabS(Image $in, array $options = []) Unpack a LabQ image to short Lab. + * @method Image LabQ2LabS(array $options = []) Unpack a LabQ image to short Lab. * @throws Exception - * @method Image LabQ2sRGB(Image $in, array $options = []) Convert a LabQ image to sRGB. + * @method Image LabQ2sRGB(array $options = []) Convert a LabQ image to sRGB. * @throws Exception - * @method Image LabS2Lab(Image $in, array $options = []) Transform signed short Lab to float. + * @method Image LabS2Lab(array $options = []) Transform signed short Lab to float. * @throws Exception - * @method Image LabS2LabQ(Image $in, array $options = []) Transform short Lab to LabQ coding. + * @method Image LabS2LabQ(array $options = []) Transform short Lab to LabQ coding. * @throws Exception - * @method Image XYZ2CMYK(Image $in, array $options = []) Transform XYZ to CMYK. + * @method Image XYZ2CMYK(array $options = []) Transform XYZ to CMYK. * @throws Exception - * @method Image XYZ2Lab(Image $in, array $options = []) Transform XYZ to Lab. + * @method Image XYZ2Lab(array $options = []) Transform XYZ to Lab. * @throws Exception - * @method Image XYZ2Yxy(Image $in, array $options = []) Transform XYZ to Yxy. + * @method Image XYZ2Yxy(array $options = []) Transform XYZ to Yxy. * @throws Exception - * @method Image XYZ2scRGB(Image $in, array $options = []) Transform XYZ to scRGB. + * @method Image XYZ2scRGB(array $options = []) Transform XYZ to scRGB. * @throws Exception - * @method Image Yxy2XYZ(Image $in, array $options = []) Transform Yxy to XYZ. + * @method Image Yxy2XYZ(array $options = []) Transform Yxy to XYZ. * @throws Exception - * @method Image abs(Image $in, array $options = []) Absolute value of an image. + * @method Image abs(array $options = []) Absolute value of an image. * @throws Exception - * @method Image affine(Image $in, float[]|float $matrix, array $options = []) Affine transform of an image. + * @method Image affine(float[]|float $matrix, array $options = []) Affine transform of an image. * @throws Exception * @method static Image analyzeload(string $filename, array $options = []) Load an Analyze6 image. * @throws Exception * @method static Image arrayjoin(Image[]|Image $in, array $options = []) Join an array of images. * @throws Exception - * @method Image autorot(Image $in, array $options = []) Autorotate image by exif tag. + * @method Image autorot(array $options = []) Autorotate image by exif tag. * @throws Exception - * @method float avg(Image $in, array $options = []) Find image average. + * @method float avg(array $options = []) Find image average. * @throws Exception - * @method Image bandbool(Image $in, string $boolean, array $options = []) Boolean operation across image bands. + * @method Image bandbool(string $boolean, array $options = []) Boolean operation across image bands. * @see OperationBoolean for possible values for $boolean * @throws Exception - * @method Image bandfold(Image $in, array $options = []) Fold up x axis into bands. + * @method Image bandfold(array $options = []) Fold up x axis into bands. * @throws Exception - * @method Image bandjoin_const(Image $in, float[]|float $c, array $options = []) Append a constant band to an image. + * @method Image bandjoin_const(float[]|float $c, array $options = []) Append a constant band to an image. * @throws Exception - * @method Image bandmean(Image $in, array $options = []) Band-wise average. + * @method Image bandmean(array $options = []) Band-wise average. * @throws Exception - * @method Image bandunfold(Image $in, array $options = []) Unfold image bands into x axis. + * @method Image bandunfold(array $options = []) Unfold image bands into x axis. * @throws Exception * @method static Image black(integer $width, integer $height, array $options = []) Make a black image. * @throws Exception - * @method Image boolean(Image $left, Image $right, string $boolean, array $options = []) Boolean operation on two images. + * @method Image boolean(Image $right, string $boolean, array $options = []) Boolean operation on two images. * @see OperationBoolean for possible values for $boolean * @throws Exception - * @method Image boolean_const(Image $in, string $boolean, float[]|float $c, array $options = []) Boolean operations against a constant. + * @method Image boolean_const(string $boolean, float[]|float $c, array $options = []) Boolean operations against a constant. * @see OperationBoolean for possible values for $boolean * @throws Exception - * @method Image buildlut(Image $in, array $options = []) Build a look-up table. + * @method Image buildlut(array $options = []) Build a look-up table. * @throws Exception - * @method Image byteswap(Image $in, array $options = []) Byteswap an image. + * @method Image byteswap(array $options = []) Byteswap an image. * @throws Exception - * @method Image cache(Image $in, array $options = []) Cache an image. + * @method Image cache(array $options = []) Cache an image. * @throws Exception - * @method Image canny(Image $in, array $options = []) Canny edge detector. + * @method Image canny(array $options = []) Canny edge detector. * @throws Exception - * @method Image case(Image $index, Image[]|Image $cases, array $options = []) Use pixel values to pick cases from an array of images. + * @method Image case(Image[]|Image $cases, array $options = []) Use pixel values to pick cases from an array of images. * @throws Exception - * @method Image cast(Image $in, string $format, array $options = []) Cast an image. + * @method Image cast(string $format, array $options = []) Cast an image. * @see BandFormat for possible values for $format * @throws Exception - * @method Image colourspace(Image $in, string $space, array $options = []) Convert to a new colorspace. + * @method Image colourspace(string $space, array $options = []) Convert to a new colorspace. * @see Interpretation for possible values for $space * @throws Exception - * @method Image compass(Image $in, Image $mask, array $options = []) Convolve with rotating mask. + * @method Image compass(Image $mask, array $options = []) Convolve with rotating mask. * @throws Exception - * @method Image complex(Image $in, string $cmplx, array $options = []) Perform a complex operation on an image. + * @method Image complex(string $cmplx, array $options = []) Perform a complex operation on an image. * @see OperationComplex for possible values for $cmplx * @throws Exception - * @method Image complex2(Image $left, Image $right, string $cmplx, array $options = []) Complex binary operations on two images. + * @method Image complex2(Image $right, string $cmplx, array $options = []) Complex binary operations on two images. * @see OperationComplex2 for possible values for $cmplx * @throws Exception - * @method Image complexform(Image $left, Image $right, array $options = []) Form a complex image from two real images. + * @method Image complexform(Image $right, array $options = []) Form a complex image from two real images. * @throws Exception - * @method Image complexget(Image $in, string $get, array $options = []) Get a component from a complex image. + * @method Image complexget(string $get, array $options = []) Get a component from a complex image. * @see OperationComplexget for possible values for $get * @throws Exception * @method static Image composite(Image[]|Image $in, integer[]|integer $mode, array $options = []) Blend an array of images with an array of blend modes. * @throws Exception - * @method Image composite2(Image $base, Image $overlay, string $mode, array $options = []) Blend a pair of images with a blend mode. + * @method Image composite2(Image $overlay, string $mode, array $options = []) Blend a pair of images with a blend mode. * @see BlendMode for possible values for $mode * @throws Exception - * @method Image conv(Image $in, Image $mask, array $options = []) Convolution operation. + * @method Image conv(Image $mask, array $options = []) Convolution operation. * @throws Exception - * @method Image conva(Image $in, Image $mask, array $options = []) Approximate integer convolution. + * @method Image conva(Image $mask, array $options = []) Approximate integer convolution. * @throws Exception - * @method Image convasep(Image $in, Image $mask, array $options = []) Approximate separable integer convolution. + * @method Image convasep(Image $mask, array $options = []) Approximate separable integer convolution. * @throws Exception - * @method Image convf(Image $in, Image $mask, array $options = []) Float convolution operation. + * @method Image convf(Image $mask, array $options = []) Float convolution operation. * @throws Exception - * @method Image convi(Image $in, Image $mask, array $options = []) Int convolution operation. + * @method Image convi(Image $mask, array $options = []) Int convolution operation. * @throws Exception - * @method Image convsep(Image $in, Image $mask, array $options = []) Seperable convolution operation. + * @method Image convsep(Image $mask, array $options = []) Seperable convolution operation. * @throws Exception - * @method Image copy(Image $in, array $options = []) Copy an image. + * @method Image copy(array $options = []) Copy an image. * @throws Exception - * @method float countlines(Image $in, string $direction, array $options = []) Count lines in an image. + * @method float countlines(string $direction, array $options = []) Count lines in an image. * @see Direction for possible values for $direction * @throws Exception - * @method Image crop(Image $input, integer $left, integer $top, integer $width, integer $height, array $options = []) Extract an area from an image. + * @method Image crop(integer $left, integer $top, integer $width, integer $height, array $options = []) Extract an area from an image. * @throws Exception * @method static Image csvload(string $filename, array $options = []) Load csv. * @throws Exception * @method static Image csvload_source(string $source, array $options = []) Load csv. * @throws Exception - * @method void csvsave(Image $in, string $filename, array $options = []) Save image to csv. + * @method void csvsave(string $filename, array $options = []) Save image to csv. * @throws Exception - * @method void csvsave_target(Image $in, string $target, array $options = []) Save image to csv. + * @method void csvsave_target(string $target, array $options = []) Save image to csv. * @throws Exception - * @method Image dE00(Image $left, Image $right, array $options = []) Calculate dE00. + * @method Image dE00(Image $right, array $options = []) Calculate dE00. * @throws Exception - * @method Image dE76(Image $left, Image $right, array $options = []) Calculate dE76. + * @method Image dE76(Image $right, array $options = []) Calculate dE76. * @throws Exception - * @method Image dECMC(Image $left, Image $right, array $options = []) Calculate dECMC. + * @method Image dECMC(Image $right, array $options = []) Calculate dECMC. * @throws Exception - * @method float deviate(Image $in, array $options = []) Find image standard deviation. + * @method float deviate(array $options = []) Find image standard deviation. * @throws Exception - * @method Image draw_circle(Image $image, float[]|float $ink, integer $cx, integer $cy, integer $radius, array $options = []) Draw a circle on an image. + * @method Image draw_circle(float[]|float $ink, integer $cx, integer $cy, integer $radius, array $options = []) Draw a circle on an image. * @throws Exception - * @method Image draw_flood(Image $image, float[]|float $ink, integer $x, integer $y, array $options = []) Flood-fill an area. + * @method Image draw_flood(float[]|float $ink, integer $x, integer $y, array $options = []) Flood-fill an area. * @throws Exception - * @method Image draw_image(Image $image, Image $sub, integer $x, integer $y, array $options = []) Paint an image into another image. + * @method Image draw_image(Image $sub, integer $x, integer $y, array $options = []) Paint an image into another image. * @throws Exception - * @method Image draw_line(Image $image, float[]|float $ink, integer $x1, integer $y1, integer $x2, integer $y2, array $options = []) Draw a line on an image. + * @method Image draw_line(float[]|float $ink, integer $x1, integer $y1, integer $x2, integer $y2, array $options = []) Draw a line on an image. * @throws Exception - * @method Image draw_mask(Image $image, float[]|float $ink, Image $mask, integer $x, integer $y, array $options = []) Draw a mask on an image. + * @method Image draw_mask(float[]|float $ink, Image $mask, integer $x, integer $y, array $options = []) Draw a mask on an image. * @throws Exception - * @method Image draw_rect(Image $image, float[]|float $ink, integer $left, integer $top, integer $width, integer $height, array $options = []) Paint a rectangle on an image. + * @method Image draw_rect(float[]|float $ink, integer $left, integer $top, integer $width, integer $height, array $options = []) Paint a rectangle on an image. * @throws Exception - * @method Image draw_smudge(Image $image, integer $left, integer $top, integer $width, integer $height, array $options = []) Blur a rectangle on an image. + * @method Image draw_smudge(integer $left, integer $top, integer $width, integer $height, array $options = []) Blur a rectangle on an image. * @throws Exception - * @method void dzsave(Image $in, string $filename, array $options = []) Save image to deepzoom file. + * @method void dzsave(string $filename, array $options = []) Save image to deepzoom file. * @throws Exception - * @method string dzsave_buffer(Image $in, array $options = []) Save image to dz buffer. + * @method string dzsave_buffer(array $options = []) Save image to dz buffer. * @throws Exception - * @method Image embed(Image $in, integer $x, integer $y, integer $width, integer $height, array $options = []) Embed an image in a larger image. + * @method Image embed(integer $x, integer $y, integer $width, integer $height, array $options = []) Embed an image in a larger image. * @throws Exception - * @method Image extract_area(Image $input, integer $left, integer $top, integer $width, integer $height, array $options = []) Extract an area from an image. + * @method Image extract_area(integer $left, integer $top, integer $width, integer $height, array $options = []) Extract an area from an image. * @throws Exception - * @method Image extract_band(Image $in, integer $band, array $options = []) Extract band from an image. + * @method Image extract_band(integer $band, array $options = []) Extract band from an image. * @throws Exception * @method static Image eye(integer $width, integer $height, array $options = []) Make an image showing the eye's spatial response. * @throws Exception - * @method Image falsecolour(Image $in, array $options = []) False-color an image. + * @method Image falsecolour(array $options = []) False-color an image. * @throws Exception - * @method Image fastcor(Image $in, Image $ref, array $options = []) Fast correlation. + * @method Image fastcor(Image $ref, array $options = []) Fast correlation. * @throws Exception - * @method Image fill_nearest(Image $in, array $options = []) Fill image zeros with nearest non-zero pixel. + * @method Image fill_nearest(array $options = []) Fill image zeros with nearest non-zero pixel. * @throws Exception - * @method array find_trim(Image $in, array $options = []) Search an image for non-edge areas. + * @method array find_trim(array $options = []) Search an image for non-edge areas. * Return array with: [ * 'left' => @type integer Left edge of image * 'top' => @type integer Top edge of extract area @@ -227,30 +227,30 @@ * @throws Exception * @method static Image fitsload(string $filename, array $options = []) Load a FITS image. * @throws Exception - * @method void fitssave(Image $in, string $filename, array $options = []) Save image to fits file. + * @method void fitssave(string $filename, array $options = []) Save image to fits file. * @throws Exception - * @method Image flatten(Image $in, array $options = []) Flatten alpha out of an image. + * @method Image flatten(array $options = []) Flatten alpha out of an image. * @throws Exception - * @method Image flip(Image $in, string $direction, array $options = []) Flip an image. + * @method Image flip(string $direction, array $options = []) Flip an image. * @see Direction for possible values for $direction * @throws Exception - * @method Image float2rad(Image $in, array $options = []) Transform float RGB to Radiance coding. + * @method Image float2rad(array $options = []) Transform float RGB to Radiance coding. * @throws Exception * @method static Image fractsurf(integer $width, integer $height, float $fractal_dimension, array $options = []) Make a fractal surface. * @throws Exception - * @method Image freqmult(Image $in, Image $mask, array $options = []) Frequency-domain filtering. + * @method Image freqmult(Image $mask, array $options = []) Frequency-domain filtering. * @throws Exception - * @method Image fwfft(Image $in, array $options = []) Forward FFT. + * @method Image fwfft(array $options = []) Forward FFT. * @throws Exception - * @method Image gamma(Image $in, array $options = []) Gamma an image. + * @method Image gamma(array $options = []) Gamma an image. * @throws Exception - * @method Image gaussblur(Image $in, float $sigma, array $options = []) Gaussian blur. + * @method Image gaussblur(float $sigma, array $options = []) Gaussian blur. * @throws Exception * @method static Image gaussmat(float $sigma, float $min_ampl, array $options = []) Make a gaussian image. * @throws Exception * @method static Image gaussnoise(integer $width, integer $height, array $options = []) Make a gaussnoise image. * @throws Exception - * @method array getpoint(Image $in, integer $x, integer $y, array $options = []) Read a point from an image. + * @method array getpoint(integer $x, integer $y, array $options = []) Read a point from an image. * @throws Exception * @method static Image gifload(string $filename, array $options = []) Load GIF with giflib. * @throws Exception @@ -258,14 +258,14 @@ * @throws Exception * @method static Image gifload_source(string $source, array $options = []) Load GIF with giflib. * @throws Exception - * @method Image globalbalance(Image $in, array $options = []) Global balance an image mosaic. + * @method Image globalbalance(array $options = []) Global balance an image mosaic. * @throws Exception - * @method Image gravity(Image $in, string $direction, integer $width, integer $height, array $options = []) Place an image within a larger image with a certain gravity. + * @method Image gravity(string $direction, integer $width, integer $height, array $options = []) Place an image within a larger image with a certain gravity. * @see CompassDirection for possible values for $direction * @throws Exception * @method static Image grey(integer $width, integer $height, array $options = []) Make a grey ramp image. * @throws Exception - * @method Image grid(Image $in, integer $tile_height, integer $across, integer $down, array $options = []) Grid an image. + * @method Image grid(integer $tile_height, integer $across, integer $down, array $options = []) Grid an image. * @throws Exception * @method static Image heifload(string $filename, array $options = []) Load a HEIF image. * @throws Exception @@ -273,55 +273,55 @@ * @throws Exception * @method static Image heifload_source(string $source, array $options = []) Load a HEIF image. * @throws Exception - * @method void heifsave(Image $in, string $filename, array $options = []) Save image in HEIF format. + * @method void heifsave(string $filename, array $options = []) Save image in HEIF format. * @throws Exception - * @method string heifsave_buffer(Image $in, array $options = []) Save image in HEIF format. + * @method string heifsave_buffer(array $options = []) Save image in HEIF format. * @throws Exception - * @method void heifsave_target(Image $in, string $target, array $options = []) Save image in HEIF format. + * @method void heifsave_target(string $target, array $options = []) Save image in HEIF format. * @throws Exception - * @method Image hist_cum(Image $in, array $options = []) Form cumulative histogram. + * @method Image hist_cum(array $options = []) Form cumulative histogram. * @throws Exception - * @method float hist_entropy(Image $in, array $options = []) Estimate image entropy. + * @method float hist_entropy(array $options = []) Estimate image entropy. * @throws Exception - * @method Image hist_equal(Image $in, array $options = []) Histogram equalisation. + * @method Image hist_equal(array $options = []) Histogram equalisation. * @throws Exception - * @method Image hist_find(Image $in, array $options = []) Find image histogram. + * @method Image hist_find(array $options = []) Find image histogram. * @throws Exception - * @method Image hist_find_indexed(Image $in, Image $index, array $options = []) Find indexed image histogram. + * @method Image hist_find_indexed(Image $index, array $options = []) Find indexed image histogram. * @throws Exception - * @method Image hist_find_ndim(Image $in, array $options = []) Find n-dimensional image histogram. + * @method Image hist_find_ndim(array $options = []) Find n-dimensional image histogram. * @throws Exception - * @method bool hist_ismonotonic(Image $in, array $options = []) Test for monotonicity. + * @method bool hist_ismonotonic(array $options = []) Test for monotonicity. * @throws Exception - * @method Image hist_local(Image $in, integer $width, integer $height, array $options = []) Local histogram equalisation. + * @method Image hist_local(integer $width, integer $height, array $options = []) Local histogram equalisation. * @throws Exception - * @method Image hist_match(Image $in, Image $ref, array $options = []) Match two histograms. + * @method Image hist_match(Image $ref, array $options = []) Match two histograms. * @throws Exception - * @method Image hist_norm(Image $in, array $options = []) Normalise histogram. + * @method Image hist_norm(array $options = []) Normalise histogram. * @throws Exception - * @method Image hist_plot(Image $in, array $options = []) Plot histogram. + * @method Image hist_plot(array $options = []) Plot histogram. * @throws Exception - * @method Image hough_circle(Image $in, array $options = []) Find hough circle transform. + * @method Image hough_circle(array $options = []) Find hough circle transform. * @throws Exception - * @method Image hough_line(Image $in, array $options = []) Find hough line transform. + * @method Image hough_line(array $options = []) Find hough line transform. * @throws Exception - * @method Image icc_export(Image $in, array $options = []) Output to device with ICC profile. + * @method Image icc_export(array $options = []) Output to device with ICC profile. * @throws Exception - * @method Image icc_import(Image $in, array $options = []) Import from device with ICC profile. + * @method Image icc_import(array $options = []) Import from device with ICC profile. * @throws Exception - * @method Image icc_transform(Image $in, string $output_profile, array $options = []) Transform between devices with ICC profiles. + * @method Image icc_transform(string $output_profile, array $options = []) Transform between devices with ICC profiles. * @throws Exception * @method static Image identity(array $options = []) Make a 1D image where pixel values are indexes. * @throws Exception - * @method Image insert(Image $main, Image $sub, integer $x, integer $y, array $options = []) Insert image @sub into @main at @x, @y. + * @method Image insert(Image $sub, integer $x, integer $y, array $options = []) Insert image @sub into @main at @x, @y. * @throws Exception - * @method Image invert(Image $in, array $options = []) Invert an image. + * @method Image invert(array $options = []) Invert an image. * @throws Exception - * @method Image invertlut(Image $in, array $options = []) Build an inverted look-up table. + * @method Image invertlut(array $options = []) Build an inverted look-up table. * @throws Exception - * @method Image invfft(Image $in, array $options = []) Inverse FFT. + * @method Image invfft(array $options = []) Inverse FFT. * @throws Exception - * @method Image join(Image $in1, Image $in2, string $direction, array $options = []) Join a pair of images. + * @method Image join(Image $in2, string $direction, array $options = []) Join a pair of images. * @see Direction for possible values for $direction * @throws Exception * @method static Image jpegload(string $filename, array $options = []) Load jpeg from file. @@ -330,19 +330,19 @@ * @throws Exception * @method static Image jpegload_source(string $source, array $options = []) Load image from jpeg source. * @throws Exception - * @method void jpegsave(Image $in, string $filename, array $options = []) Save image to jpeg file. + * @method void jpegsave(string $filename, array $options = []) Save image to jpeg file. * @throws Exception - * @method string jpegsave_buffer(Image $in, array $options = []) Save image to jpeg buffer. + * @method string jpegsave_buffer(array $options = []) Save image to jpeg buffer. * @throws Exception - * @method void jpegsave_mime(Image $in, array $options = []) Save image to jpeg mime. + * @method void jpegsave_mime(array $options = []) Save image to jpeg mime. * @throws Exception - * @method void jpegsave_target(Image $in, string $target, array $options = []) Save image to jpeg target. + * @method void jpegsave_target(string $target, array $options = []) Save image to jpeg target. * @throws Exception - * @method Image labelregions(Image $in, array $options = []) Label regions in an image. + * @method Image labelregions(array $options = []) Label regions in an image. * @throws Exception - * @method Image linear(Image $in, float[]|float $a, float[]|float $b, array $options = []) Calculate (a * in + b). + * @method Image linear(float[]|float $a, float[]|float $b, array $options = []) Calculate (a * in + b). * @throws Exception - * @method Image linecache(Image $in, array $options = []) Cache an image as a set of lines. + * @method Image linecache(array $options = []) Cache an image as a set of lines. * @throws Exception * @method static Image logmat(float $sigma, float $min_ampl, array $options = []) Make a laplacian of gaussian image. * @throws Exception @@ -350,13 +350,13 @@ * @throws Exception * @method static Image magickload_buffer(string $buffer, array $options = []) Load buffer with ImageMagick. * @throws Exception - * @method void magicksave(Image $in, string $filename, array $options = []) Save file with ImageMagick. + * @method void magicksave(string $filename, array $options = []) Save file with ImageMagick. * @throws Exception - * @method string magicksave_buffer(Image $in, array $options = []) Save image to magick buffer. + * @method string magicksave_buffer(array $options = []) Save image to magick buffer. * @throws Exception - * @method Image mapim(Image $in, Image $index, array $options = []) Resample with a map image. + * @method Image mapim(Image $index, array $options = []) Resample with a map image. * @throws Exception - * @method Image maplut(Image $in, Image $lut, array $options = []) Map an image though a lut. + * @method Image maplut(Image $lut, array $options = []) Map an image though a lut. * @throws Exception * @method static Image mask_butterworth(integer $width, integer $height, float $order, float $frequency_cutoff, float $amplitude_cutoff, array $options = []) Make a butterworth filter. * @throws Exception @@ -378,54 +378,54 @@ * @throws Exception * @method static Image mask_ideal_ring(integer $width, integer $height, float $frequency_cutoff, float $ringwidth, array $options = []) Make an ideal ring filter. * @throws Exception - * @method Image match(Image $ref, Image $sec, integer $xr1, integer $yr1, integer $xs1, integer $ys1, integer $xr2, integer $yr2, integer $xs2, integer $ys2, array $options = []) First-order match of two images. + * @method Image match(Image $sec, integer $xr1, integer $yr1, integer $xs1, integer $ys1, integer $xr2, integer $yr2, integer $xs2, integer $ys2, array $options = []) First-order match of two images. * @throws Exception - * @method Image math(Image $in, string $math, array $options = []) Apply a math operation to an image. + * @method Image math(string $math, array $options = []) Apply a math operation to an image. * @see OperationMath for possible values for $math * @throws Exception - * @method Image math2(Image $left, Image $right, string $math2, array $options = []) Binary math operations. + * @method Image math2(Image $right, string $math2, array $options = []) Binary math operations. * @see OperationMath2 for possible values for $math2 * @throws Exception - * @method Image math2_const(Image $in, string $math2, float[]|float $c, array $options = []) Binary math operations with a constant. + * @method Image math2_const(string $math2, float[]|float $c, array $options = []) Binary math operations with a constant. * @see OperationMath2 for possible values for $math2 * @throws Exception * @method static Image matload(string $filename, array $options = []) Load mat from file. * @throws Exception - * @method Image matrixinvert(Image $in, array $options = []) Invert an matrix. + * @method Image matrixinvert(array $options = []) Invert an matrix. * @throws Exception * @method static Image matrixload(string $filename, array $options = []) Load matrix. * @throws Exception * @method static Image matrixload_source(string $source, array $options = []) Load matrix. * @throws Exception - * @method void matrixprint(Image $in, array $options = []) Print matrix. + * @method void matrixprint(array $options = []) Print matrix. * @throws Exception - * @method void matrixsave(Image $in, string $filename, array $options = []) Save image to matrix. + * @method void matrixsave(string $filename, array $options = []) Save image to matrix. * @throws Exception - * @method void matrixsave_target(Image $in, string $target, array $options = []) Save image to matrix. + * @method void matrixsave_target(string $target, array $options = []) Save image to matrix. * @throws Exception - * @method float max(Image $in, array $options = []) Find image maximum. + * @method float max(array $options = []) Find image maximum. * @throws Exception - * @method Image measure(Image $in, integer $h, integer $v, array $options = []) Measure a set of patches on a color chart. + * @method Image measure(integer $h, integer $v, array $options = []) Measure a set of patches on a color chart. * @throws Exception - * @method Image merge(Image $ref, Image $sec, string $direction, integer $dx, integer $dy, array $options = []) Merge two images. + * @method Image merge(Image $sec, string $direction, integer $dx, integer $dy, array $options = []) Merge two images. * @see Direction for possible values for $direction * @throws Exception - * @method float min(Image $in, array $options = []) Find image minimum. + * @method float min(array $options = []) Find image minimum. * @throws Exception - * @method Image morph(Image $in, Image $mask, string $morph, array $options = []) Morphology operation. + * @method Image morph(Image $mask, string $morph, array $options = []) Morphology operation. * @see OperationMorphology for possible values for $morph * @throws Exception - * @method Image mosaic(Image $ref, Image $sec, string $direction, integer $xref, integer $yref, integer $xsec, integer $ysec, array $options = []) Mosaic two images. + * @method Image mosaic(Image $sec, string $direction, integer $xref, integer $yref, integer $xsec, integer $ysec, array $options = []) Mosaic two images. * @see Direction for possible values for $direction * @throws Exception - * @method Image mosaic1(Image $ref, Image $sec, string $direction, integer $xr1, integer $yr1, integer $xs1, integer $ys1, integer $xr2, integer $yr2, integer $xs2, integer $ys2, array $options = []) First-order mosaic of two images. + * @method Image mosaic1(Image $sec, string $direction, integer $xr1, integer $yr1, integer $xs1, integer $ys1, integer $xr2, integer $yr2, integer $xs2, integer $ys2, array $options = []) First-order mosaic of two images. * @see Direction for possible values for $direction * @throws Exception - * @method Image msb(Image $in, array $options = []) Pick most-significant byte from an image. + * @method Image msb(array $options = []) Pick most-significant byte from an image. * @throws Exception * @method static Image niftiload(string $filename, array $options = []) Load a NIFTI image. * @throws Exception - * @method void niftisave(Image $in, string $filename, array $options = []) Save image to nifti file. + * @method void niftisave(string $filename, array $options = []) Save image to nifti file. * @throws Exception * @method static Image openexrload(string $filename, array $options = []) Load an OpenEXR image. * @throws Exception @@ -437,11 +437,11 @@ * @throws Exception * @method static Image pdfload_source(string $source, array $options = []) Load PDF from source. * @throws Exception - * @method integer percent(Image $in, float $percent, array $options = []) Find threshold for percent of pixels. + * @method integer percent(float $percent, array $options = []) Find threshold for percent of pixels. * @throws Exception * @method static Image perlin(integer $width, integer $height, array $options = []) Make a perlin noise image. * @throws Exception - * @method Image phasecor(Image $in, Image $in2, array $options = []) Calculate phase correlation. + * @method Image phasecor(Image $in2, array $options = []) Calculate phase correlation. * @throws Exception * @method static Image pngload(string $filename, array $options = []) Load png from file. * @throws Exception @@ -449,23 +449,23 @@ * @throws Exception * @method static Image pngload_source(string $source, array $options = []) Load png from source. * @throws Exception - * @method void pngsave(Image $in, string $filename, array $options = []) Save image to png file. + * @method void pngsave(string $filename, array $options = []) Save image to png file. * @throws Exception - * @method string pngsave_buffer(Image $in, array $options = []) Save image to png buffer. + * @method string pngsave_buffer(array $options = []) Save image to png buffer. * @throws Exception - * @method void pngsave_target(Image $in, string $target, array $options = []) Save image to target as PNG. + * @method void pngsave_target(string $target, array $options = []) Save image to target as PNG. * @throws Exception * @method static Image ppmload(string $filename, array $options = []) Load ppm from file. * @throws Exception * @method static Image ppmload_source(string $source, array $options = []) Load ppm base class. * @throws Exception - * @method void ppmsave(Image $in, string $filename, array $options = []) Save image to ppm file. + * @method void ppmsave(string $filename, array $options = []) Save image to ppm file. * @throws Exception - * @method void ppmsave_target(Image $in, string $target, array $options = []) Save to ppm. + * @method void ppmsave_target(string $target, array $options = []) Save to ppm. * @throws Exception - * @method Image premultiply(Image $in, array $options = []) Premultiply image alpha. + * @method Image premultiply(array $options = []) Premultiply image alpha. * @throws Exception - * @method array profile(Image $in, array $options = []) Find image profiles. + * @method array profile(array $options = []) Find image profiles. * Return array with: [ * 'columns' => @type Image First non-zero pixel in column * 'rows' => @type Image First non-zero pixel in row @@ -473,15 +473,15 @@ * @throws Exception * @method static string profile_load(string $name, array $options = []) Load named ICC profile. * @throws Exception - * @method array project(Image $in, array $options = []) Find image projections. + * @method array project(array $options = []) Find image projections. * Return array with: [ * 'columns' => @type Image Sums of columns * 'rows' => @type Image Sums of rows * ]; * @throws Exception - * @method Image quadratic(Image $in, Image $coeff, array $options = []) Resample an image with a quadratic transform. + * @method Image quadratic(Image $coeff, array $options = []) Resample an image with a quadratic transform. * @throws Exception - * @method Image rad2float(Image $in, array $options = []) Unpack Radiance coding to float RGB. + * @method Image rad2float(array $options = []) Unpack Radiance coding to float RGB. * @throws Exception * @method static Image radload(string $filename, array $options = []) Load a Radiance image from a file. * @throws Exception @@ -489,91 +489,91 @@ * @throws Exception * @method static Image radload_source(string $source, array $options = []) Load rad from source. * @throws Exception - * @method void radsave(Image $in, string $filename, array $options = []) Save image to Radiance file. + * @method void radsave(string $filename, array $options = []) Save image to Radiance file. * @throws Exception - * @method string radsave_buffer(Image $in, array $options = []) Save image to Radiance buffer. + * @method string radsave_buffer(array $options = []) Save image to Radiance buffer. * @throws Exception - * @method void radsave_target(Image $in, string $target, array $options = []) Save image to Radiance target. + * @method void radsave_target(string $target, array $options = []) Save image to Radiance target. * @throws Exception - * @method Image rank(Image $in, integer $width, integer $height, integer $index, array $options = []) Rank filter. + * @method Image rank(integer $width, integer $height, integer $index, array $options = []) Rank filter. * @throws Exception * @method static Image rawload(string $filename, integer $width, integer $height, integer $bands, array $options = []) Load raw data from a file. * @throws Exception - * @method void rawsave(Image $in, string $filename, array $options = []) Save image to raw file. + * @method void rawsave(string $filename, array $options = []) Save image to raw file. * @throws Exception - * @method void rawsave_fd(Image $in, integer $fd, array $options = []) Write raw image to file descriptor. + * @method void rawsave_fd(integer $fd, array $options = []) Write raw image to file descriptor. * @throws Exception - * @method Image recomb(Image $in, Image $m, array $options = []) Linear recombination with matrix. + * @method Image recomb(Image $m, array $options = []) Linear recombination with matrix. * @throws Exception - * @method Image reduce(Image $in, float $hshrink, float $vshrink, array $options = []) Reduce an image. + * @method Image reduce(float $hshrink, float $vshrink, array $options = []) Reduce an image. * @throws Exception - * @method Image reduceh(Image $in, float $hshrink, array $options = []) Shrink an image horizontally. + * @method Image reduceh(float $hshrink, array $options = []) Shrink an image horizontally. * @throws Exception - * @method Image reducev(Image $in, float $vshrink, array $options = []) Shrink an image vertically. + * @method Image reducev(float $vshrink, array $options = []) Shrink an image vertically. * @throws Exception - * @method Image relational(Image $left, Image $right, string $relational, array $options = []) Relational operation on two images. + * @method Image relational(Image $right, string $relational, array $options = []) Relational operation on two images. * @see OperationRelational for possible values for $relational * @throws Exception - * @method Image relational_const(Image $in, string $relational, float[]|float $c, array $options = []) Relational operations against a constant. + * @method Image relational_const(string $relational, float[]|float $c, array $options = []) Relational operations against a constant. * @see OperationRelational for possible values for $relational * @throws Exception - * @method Image remainder_const(Image $in, float[]|float $c, array $options = []) Remainder after integer division of an image and a constant. + * @method Image remainder_const(float[]|float $c, array $options = []) Remainder after integer division of an image and a constant. * @throws Exception - * @method Image replicate(Image $in, integer $across, integer $down, array $options = []) Replicate an image. + * @method Image replicate(integer $across, integer $down, array $options = []) Replicate an image. * @throws Exception - * @method Image resize(Image $in, float $scale, array $options = []) Resize an image. + * @method Image resize(float $scale, array $options = []) Resize an image. * @throws Exception - * @method Image rot(Image $in, string $angle, array $options = []) Rotate an image. + * @method Image rot(string $angle, array $options = []) Rotate an image. * @see Angle for possible values for $angle * @throws Exception - * @method Image rot45(Image $in, array $options = []) Rotate an image. + * @method Image rot45(array $options = []) Rotate an image. * @throws Exception - * @method Image rotate(Image $in, float $angle, array $options = []) Rotate an image by a number of degrees. + * @method Image rotate(float $angle, array $options = []) Rotate an image by a number of degrees. * @throws Exception - * @method Image round(Image $in, string $round, array $options = []) Perform a round function on an image. + * @method Image round(string $round, array $options = []) Perform a round function on an image. * @see OperationRound for possible values for $round * @throws Exception - * @method Image sRGB2HSV(Image $in, array $options = []) Transform sRGB to HSV. + * @method Image sRGB2HSV(array $options = []) Transform sRGB to HSV. * @throws Exception - * @method Image sRGB2scRGB(Image $in, array $options = []) Convert an sRGB image to scRGB. + * @method Image sRGB2scRGB(array $options = []) Convert an sRGB image to scRGB. * @throws Exception - * @method Image scRGB2BW(Image $in, array $options = []) Convert scRGB to BW. + * @method Image scRGB2BW(array $options = []) Convert scRGB to BW. * @throws Exception - * @method Image scRGB2XYZ(Image $in, array $options = []) Transform scRGB to XYZ. + * @method Image scRGB2XYZ(array $options = []) Transform scRGB to XYZ. * @throws Exception - * @method Image scRGB2sRGB(Image $in, array $options = []) Convert an scRGB image to sRGB. + * @method Image scRGB2sRGB(array $options = []) Convert an scRGB image to sRGB. * @throws Exception - * @method Image scale(Image $in, array $options = []) Scale an image to uchar. + * @method Image scale(array $options = []) Scale an image to uchar. * @throws Exception - * @method Image sequential(Image $in, array $options = []) Check sequential access. + * @method Image sequential(array $options = []) Check sequential access. * @throws Exception - * @method Image sharpen(Image $in, array $options = []) Unsharp masking for print. + * @method Image sharpen(array $options = []) Unsharp masking for print. * @throws Exception - * @method Image shrink(Image $in, float $hshrink, float $vshrink, array $options = []) Shrink an image. + * @method Image shrink(float $hshrink, float $vshrink, array $options = []) Shrink an image. * @throws Exception - * @method Image shrinkh(Image $in, integer $hshrink, array $options = []) Shrink an image horizontally. + * @method Image shrinkh(integer $hshrink, array $options = []) Shrink an image horizontally. * @throws Exception - * @method Image shrinkv(Image $in, integer $vshrink, array $options = []) Shrink an image vertically. + * @method Image shrinkv(integer $vshrink, array $options = []) Shrink an image vertically. * @throws Exception - * @method Image sign(Image $in, array $options = []) Unit vector of pixel. + * @method Image sign(array $options = []) Unit vector of pixel. * @throws Exception - * @method Image similarity(Image $in, array $options = []) Similarity transform of an image. + * @method Image similarity(array $options = []) Similarity transform of an image. * @throws Exception * @method static Image sines(integer $width, integer $height, array $options = []) Make a 2D sine wave. * @throws Exception - * @method Image smartcrop(Image $input, integer $width, integer $height, array $options = []) Extract an area from an image. + * @method Image smartcrop(integer $width, integer $height, array $options = []) Extract an area from an image. * @throws Exception - * @method Image sobel(Image $in, array $options = []) Sobel edge detector. + * @method Image sobel(array $options = []) Sobel edge detector. * @throws Exception - * @method Image spcor(Image $in, Image $ref, array $options = []) Spatial correlation. + * @method Image spcor(Image $ref, array $options = []) Spatial correlation. * @throws Exception - * @method Image spectrum(Image $in, array $options = []) Make displayable power spectrum. + * @method Image spectrum(array $options = []) Make displayable power spectrum. * @throws Exception - * @method Image stats(Image $in, array $options = []) Find many image stats. + * @method Image stats(array $options = []) Find many image stats. * @throws Exception - * @method Image stdif(Image $in, integer $width, integer $height, array $options = []) Statistical difference. + * @method Image stdif(integer $width, integer $height, array $options = []) Statistical difference. * @throws Exception - * @method Image subsample(Image $input, integer $xfac, integer $yfac, array $options = []) Subsample an image. + * @method Image subsample(integer $xfac, integer $yfac, array $options = []) Subsample an image. * @throws Exception * @method static Image sum(Image[]|Image $in, array $options = []) Sum an array of images. * @throws Exception @@ -593,7 +593,7 @@ * @throws Exception * @method static Image thumbnail_buffer(string $buffer, integer $width, array $options = []) Generate thumbnail from buffer. * @throws Exception - * @method Image thumbnail_image(Image $in, integer $width, array $options = []) Generate thumbnail from image. + * @method Image thumbnail_image(integer $width, array $options = []) Generate thumbnail from image. * @throws Exception * @method static Image thumbnail_source(string $source, integer $width, array $options = []) Generate thumbnail from source. * @throws Exception @@ -603,21 +603,21 @@ * @throws Exception * @method static Image tiffload_source(string $source, array $options = []) Load tiff from source. * @throws Exception - * @method void tiffsave(Image $in, string $filename, array $options = []) Save image to tiff file. + * @method void tiffsave(string $filename, array $options = []) Save image to tiff file. * @throws Exception - * @method string tiffsave_buffer(Image $in, array $options = []) Save image to tiff buffer. + * @method string tiffsave_buffer(array $options = []) Save image to tiff buffer. * @throws Exception - * @method Image tilecache(Image $in, array $options = []) Cache an image as a set of tiles. + * @method Image tilecache(array $options = []) Cache an image as a set of tiles. * @throws Exception * @method static Image tonelut(array $options = []) Build a look-up table. * @throws Exception - * @method Image transpose3d(Image $in, array $options = []) Transpose3d an image. + * @method Image transpose3d(array $options = []) Transpose3d an image. * @throws Exception - * @method Image unpremultiply(Image $in, array $options = []) Unpremultiply image alpha. + * @method Image unpremultiply(array $options = []) Unpremultiply image alpha. * @throws Exception * @method static Image vipsload(string $filename, array $options = []) Load vips from file. * @throws Exception - * @method void vipssave(Image $in, string $filename, array $options = []) Save image to vips file. + * @method void vipssave(string $filename, array $options = []) Save image to vips file. * @throws Exception * @method static Image webpload(string $filename, array $options = []) Load webp from file. * @throws Exception @@ -625,21 +625,21 @@ * @throws Exception * @method static Image webpload_source(string $source, array $options = []) Load webp from source. * @throws Exception - * @method void webpsave(Image $in, string $filename, array $options = []) Save image to webp file. + * @method void webpsave(string $filename, array $options = []) Save image to webp file. * @throws Exception - * @method string webpsave_buffer(Image $in, array $options = []) Save image to webp buffer. + * @method string webpsave_buffer(array $options = []) Save image to webp buffer. * @throws Exception - * @method void webpsave_target(Image $in, string $target, array $options = []) Save image to webp target. + * @method void webpsave_target(string $target, array $options = []) Save image to webp target. * @throws Exception * @method static Image worley(integer $width, integer $height, array $options = []) Make a worley noise image. * @throws Exception - * @method Image wrap(Image $in, array $options = []) Wrap image origin. + * @method Image wrap(array $options = []) Wrap image origin. * @throws Exception * @method static Image xyz(integer $width, integer $height, array $options = []) Make an image where pixel values are coordinates. * @throws Exception * @method static Image zone(integer $width, integer $height, array $options = []) Make a zone plate. * @throws Exception - * @method Image zoom(Image $input, integer $xfac, integer $yfac, array $options = []) Zoom an image. + * @method Image zoom(integer $xfac, integer $yfac, array $options = []) Zoom an image. * @throws Exception * * @property integer $width Image width in pixels diff --git a/src/Utils.php b/src/Utils.php index a122e31..2f4fe53 100644 --- a/src/Utils.php +++ b/src/Utils.php @@ -38,8 +38,6 @@ namespace Jcupitt\Vips; -use Psr\Log\LoggerInterface; - /** * Various utilities. * @@ -60,7 +58,7 @@ class Utils * * @return void */ - public static function debugLog(string $name, array $arguments) + public static function debugLog(string $name, array $arguments): void { $logger = Config::getLogger(); if ($logger) { @@ -76,7 +74,7 @@ public static function debugLog(string $name, array $arguments) * * @return void */ - public static function errorLog(string $message, \Exception $exception) + public static function errorLog(string $message, \Exception $exception): void { $logger = Config::getLogger(); if ($logger) { @@ -88,11 +86,11 @@ public static function errorLog(string $message, \Exception $exception) * Look up the GTyoe from a type name. If the type does not exist, * return 0. * - * @param string $name The type name. + * @param string $name The type name. * * @return int */ - public static function typeFromName(string $name) + public static function typeFromName(string $name): int { return vips_type_from_name($name); } diff --git a/tests/ConvenienceTest.php b/tests/ConvenienceTest.php index 49492ef..34fa4f9 100644 --- a/tests/ConvenienceTest.php +++ b/tests/ConvenienceTest.php @@ -17,7 +17,7 @@ class ConvenienceTest extends TestCase */ private $pixel; - protected function setUp() + protected function setUp(): void { $filename = __DIR__ . '/images/img_0076.jpg'; $this->image = Vips\Image::newFromFile($filename); diff --git a/tests/ExceptionTest.php b/tests/ExceptionTest.php index b38c43b..262e519 100644 --- a/tests/ExceptionTest.php +++ b/tests/ExceptionTest.php @@ -17,7 +17,7 @@ class ExceptionTest extends TestCase */ private $pixel; - protected function setUp() + protected function setUp(): void { $filename = __DIR__ . '/images/img_0076.jpg'; $this->image = Vips\Image::newFromFile($filename); diff --git a/tests/MetaTest.php b/tests/MetaTest.php index 9fd3cb5..9447fe3 100644 --- a/tests/MetaTest.php +++ b/tests/MetaTest.php @@ -17,7 +17,7 @@ class MetaTest extends TestCase */ private $png_image; - protected function setUp() + protected function setUp(): void { $filename = __DIR__ . '/images/img_0076.jpg'; $this->image = Vips\Image::newFromFile($filename); diff --git a/tests/ShortcutTest.php b/tests/ShortcutTest.php index d66798c..da251a2 100644 --- a/tests/ShortcutTest.php +++ b/tests/ShortcutTest.php @@ -30,7 +30,7 @@ public static function mapNumeric($value, $func) return $value; } - protected function setUp() + protected function setUp(): void { $filename = __DIR__ . '/images/img_0076.jpg'; $this->image = Vips\Image::newFromFile($filename, ['shrink' => 8]); diff --git a/tests/WriteTest.php b/tests/WriteTest.php index 6fb5dc4..cc27b81 100644 --- a/tests/WriteTest.php +++ b/tests/WriteTest.php @@ -12,12 +12,12 @@ class WriteTest extends TestCase */ private $tmps; - protected function setUp() + protected function setUp(): void { $this->tmps = []; } - protected function tearDown() + protected function tearDown(): void { foreach ($this->tmps as $tmp) { @unlink($tmp); From 5adde1b76fd59db27b0890e4ef8f6c356e944225 Mon Sep 17 00:00:00 2001 From: Kleis Auke Wolthuizen Date: Fri, 28 Aug 2020 13:36:02 +0200 Subject: [PATCH 016/123] Skip installing libheif --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 914efa1..90b3cd3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,7 +30,6 @@ addons: - libgif-dev - libgsf-1-dev - libgsl-dev - - libheif-dev - liblcms2-dev - libmagickwand-dev - libmatio-dev From 315ede6b0dc8268d47846d18654f22c83105df54 Mon Sep 17 00:00:00 2001 From: Kleis Auke Wolthuizen Date: Fri, 28 Aug 2020 13:44:03 +0200 Subject: [PATCH 017/123] Do not test PHP 7.1 on Travis Our development dependencies require newer versions of PHP. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 90b3cd3..fb20029 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ language: php dist: bionic php: - - 7.1 + - 7.3 - 7.4 env: From a2cd8b6d3408ca0f3317d0a1affd92c39533ecb1 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 28 Aug 2020 18:35:19 +0100 Subject: [PATCH 018/123] update README for new docs url --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e3b067f..f0b077f 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ See the README there, but briefly: ``` "require": { - "jcupitt/vips" : "1.0.5" + "jcupitt/vips" : "1.0.7" } ``` @@ -85,7 +85,7 @@ $ ./try1.php ~/pics/k2.jpg x.tif ``` See `examples/`. We have a [complete set of formatted API -docs](https://libvips.github.io/php-vips/docs/classes/Jcupitt.Vips.Image.html). +docs](https://libvips.github.io/php-vips/docs/classes/Jcupitt-Vips-Image.html). ### Introduction to the API From d3cf61bd087bd9c00cfc5ae32f6ded7165963058 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 29 Aug 2020 14:10:03 +0100 Subject: [PATCH 019/123] allow type names to setType see https://github.com/libvips/php-vips-ext/issues/38 --- CHANGELOG.md | 20 +++++++++++++++++++- src/Image.php | 11 ++++++----- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f03a6fa..aecf06d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,28 @@ # Changelog All notable changes to `:vips` will be documented in this file. +### 1.0.8 - 2020-08-29 + +### Added +- allow type names as type params to Image::setType() -- fixes issue with GType + on 32-bit platforms + +### Deprecated +- Nothing + +### Fixed +- Nothing + +### Remove +- Nothing + +### Security +- Nothing + ### 1.0.7 - 2020-08-28 ### Added -- use nullable types and void return type were possible +- use nullable types and void return type where possible ### Deprecated - requires php >= 7.1 diff --git a/src/Image.php b/src/Image.php index 4d26810..1f364bc 100644 --- a/src/Image.php +++ b/src/Image.php @@ -1389,17 +1389,18 @@ public function set(string $name, $value): void * This is useful if the type of the property cannot be determined from the * php type of the value. * - * Use Utils::typeFromName() to look up types by name. + * Pass the type name directly, or use Utils::typeFromName() to look up + * types by name. * - * @param int $type The type of the property. - * @param string $name The property name. - * @param mixed $value The value to set for this property. + * @param string|int $type The type of the property. + * @param string $name The property name. + * @param mixed $value The value to set for this property. * * @throws Exception * * @return void */ - public function setType(int $type, string $name, $value): void + public function setType($type, string $name, $value): void { $result = vips_image_set_type($this->image, $type, $name, $value); if ($result === -1) { From 52d492c50e0475a55c552b19dc69b24a9bf0f4e2 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 24 Feb 2021 09:15:55 +0000 Subject: [PATCH 020/123] add watermark-text example renders text in a box on every page --- examples/watermark-text.php | 55 +++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100755 examples/watermark-text.php diff --git a/examples/watermark-text.php b/examples/watermark-text.php new file mode 100755 index 0000000..2c403fd --- /dev/null +++ b/examples/watermark-text.php @@ -0,0 +1,55 @@ +#!/usr/bin/env php + 'sequential', + 'n' => -1 +]); + +$output_filename = $argv[2]; +$text = $argv[3]; + +// the size of each frame +$page_height = $image->get('page-height'); + +$text_mask = Vips\Image::text($text, ['width' => $image->width, 'dpi' => 150]); + +// semi-transparent white text on a blue background +$foreground = [255, 255, 255, 50]; +$background = [0, 0, 255, 50]; + +// and a 10-pixel marghin +$margin = 10; + +$overlay = $text_mask->ifthenelse($foreground, $background, ['blend' => TRUE ]); + +// add a margin, with the same background +$overlay = $overlay->embed($margin, $margin, + $overlay->width + 2 * $margin, $overlay->height + 2 * $margin, + ['extend' => 'background', 'background' => $background]); + +// tag as srgb +$overlay = $overlay->copy(['interpretation' => 'srgb']); + +// expand to the size of a frame, transparent background, place at the bottom +// left +$overlay = $overlay->embed($margin, $page_height - $overlay->height - $margin, + $image->width, $page_height); + +// expand to the full size of the gif roll +$overlay = $overlay->replicate(1, $image->height / $page_height); + +// composite on top of the gif +$image = $image->composite2($overlay, 'over'); + +// and write back +$image->writeToFile($output_filename); From 96dde84bc6e9ebad0f8bebe1a742cca4c5e123ee Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 24 Feb 2021 09:36:13 +0000 Subject: [PATCH 021/123] watermark formatting --- examples/watermark-text.php | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/examples/watermark-text.php b/examples/watermark-text.php index 2c403fd..3a0377d 100755 --- a/examples/watermark-text.php +++ b/examples/watermark-text.php @@ -4,14 +4,14 @@ require __DIR__ . '/vendor/autoload.php'; use Jcupitt\Vips; -if(count($argv) != 4) { - echo("usage: ./watermark-text.php input-image output-image \"some text\"\n"); +if (count($argv) != 4) { + echo("usage: ./watermark-text.php input output \"some text\"\n"); exit(1); } // we can stream the main image, and we want all frames $image = Vips\Image::newFromFile($argv[1], [ - 'access' => 'sequential', + 'access' => 'sequential', 'n' => -1 ]); @@ -21,7 +21,10 @@ // the size of each frame $page_height = $image->get('page-height'); -$text_mask = Vips\Image::text($text, ['width' => $image->width, 'dpi' => 150]); +$text_mask = Vips\Image::text($text, [ + 'width' => $image->width, + 'dpi' => 150 +]); // semi-transparent white text on a blue background $foreground = [255, 255, 255, 50]; @@ -30,20 +33,33 @@ // and a 10-pixel marghin $margin = 10; -$overlay = $text_mask->ifthenelse($foreground, $background, ['blend' => TRUE ]); +$overlay = $text_mask->ifthenelse($foreground, $background, [ + 'blend' => true +]); // add a margin, with the same background -$overlay = $overlay->embed($margin, $margin, - $overlay->width + 2 * $margin, $overlay->height + 2 * $margin, - ['extend' => 'background', 'background' => $background]); +$overlay = $overlay->embed( + $margin, + $margin, + $overlay->width + 2 * $margin, + $overlay->height + 2 * $margin, + [ + 'extend' => 'background', + 'background' => $background + ] +); // tag as srgb $overlay = $overlay->copy(['interpretation' => 'srgb']); // expand to the size of a frame, transparent background, place at the bottom // left -$overlay = $overlay->embed($margin, $page_height - $overlay->height - $margin, - $image->width, $page_height); +$overlay = $overlay->embed( + $margin, + $page_height - $overlay->height - $margin, + $image->width, + $page_height +); // expand to the full size of the gif roll $overlay = $overlay->replicate(1, $image->height / $page_height); From 4d995296b8362e8bc042ef7be5890b2e9c9c4c7e Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 4 Mar 2021 10:37:12 +0100 Subject: [PATCH 022/123] Add missing pear lib ```pecl install vips Command 'pecl' not found, but can be installed with: apt install php-pear ``` after adding pear all works fine --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f0b077f..645976d 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ See the README there, but briefly: binaries on the vips website. For example, on Debian: ``` - sudo apt-get install libvips-dev + sudo apt-get install libvips-dev php-pear ``` Or macOS: From a5fcbd7d6f5fde27d749fb2bdf59749e31d42910 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Thu, 4 Mar 2021 09:49:11 +0000 Subject: [PATCH 023/123] clarify need for PHOP dev envirionment see https://github.com/libvips/php-vips/pull/114 --- README.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 645976d..447f4ef 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ See the README there, but briefly: binaries on the vips website. For example, on Debian: ``` - sudo apt-get install libvips-dev php-pear + sudo apt-get install libvips-dev ``` Or macOS: @@ -41,7 +41,15 @@ See the README there, but briefly: brew install vips ``` -2. Install the binary PHP extension: +2. Install the binary PHP extension. You'll need a PHP development environment + for this, since it will download and build the sources for the extension. + For example, on Debian: + + ``` + sudo apt-get install php-pear + ``` + + Then to download and build the extension it's: ``` pecl install vips From 1f83dd0058854f0212938850c6894a1d5429a735 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 10 Sep 2021 16:30:48 +0100 Subject: [PATCH 024/123] update php-doc --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index d472bf0..b945a16 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,7 @@ }, "require-dev": { "php-parallel-lint/php-parallel-lint": "^1.2", - "phpdocumentor/phpdocumentor": "3.0.0-rc", + "phpdocumentor/phpdocumentor": "3.0.0", "phpunit/phpunit": "^9.3", "squizlabs/php_codesniffer": "^3.5" }, From 5015e7fb823e68ff914cd421b1a2e2674c9a1424 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 20 Nov 2021 12:28:59 +0000 Subject: [PATCH 025/123] update docs for libvips 8.12 --- CHANGELOG.md | 17 +++++++++++ RELEASE-1.0.6 => RELEASE-1.0.8 | 0 src/FailOn.php | 56 ++++++++++++++++++++++++++++++++++ src/ForeignDzLayout.php | 1 + src/ForeignPpmFormat.php | 56 ++++++++++++++++++++++++++++++++++ src/ForeignSubsample.php | 55 +++++++++++++++++++++++++++++++++ src/ForeignTiffCompression.php | 1 + src/ImageAutodoc.php | 52 +++++++++++++++++++++++++++---- src/OperationMath.php | 6 ++++ src/OperationMath2.php | 1 + 10 files changed, 239 insertions(+), 6 deletions(-) rename RELEASE-1.0.6 => RELEASE-1.0.8 (100%) create mode 100644 src/FailOn.php create mode 100644 src/ForeignPpmFormat.php create mode 100644 src/ForeignSubsample.php diff --git a/CHANGELOG.md b/CHANGELOG.md index aecf06d..b798b57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,23 @@ # Changelog All notable changes to `:vips` will be documented in this file. +### 1.0.9 - 2021-11-20 + +### Added +- update docs for libvips 8.12 + +### Deprecated +- Nothing + +### Fixed +- Nothing + +### Remove +- Nothing + +### Security +- Nothing + ### 1.0.8 - 2020-08-29 ### Added diff --git a/RELEASE-1.0.6 b/RELEASE-1.0.8 similarity index 100% rename from RELEASE-1.0.6 rename to RELEASE-1.0.8 diff --git a/src/FailOn.php b/src/FailOn.php new file mode 100644 index 0000000..eaa4d9c --- /dev/null +++ b/src/FailOn.php @@ -0,0 +1,56 @@ + + * @copyright 2016 John Cupitt + * @license https://opensource.org/licenses/MIT MIT + * @link https://github.com/jcupitt/php-vips + */ + +namespace Jcupitt\Vips; + +/** + * The FailOn enum. + * @category Images + * @package Jcupitt\Vips + * @author John Cupitt + * @copyright 2016 John Cupitt + * @license https://opensource.org/licenses/MIT MIT + * @link https://github.com/jcupitt/php-vips + */ +abstract class FailOn +{ + const NONE = 'none'; + const TRUNCATED = 'truncated'; + const ERROR = 'error'; + const WARNING = 'warning'; +} diff --git a/src/ForeignDzLayout.php b/src/ForeignDzLayout.php index 021d75f..ae36259 100644 --- a/src/ForeignDzLayout.php +++ b/src/ForeignDzLayout.php @@ -53,4 +53,5 @@ abstract class ForeignDzLayout const ZOOMIFY = 'zoomify'; const GOOGLE = 'google'; const IIIF = 'iiif'; + const IIIF3 = 'iiif3'; } diff --git a/src/ForeignPpmFormat.php b/src/ForeignPpmFormat.php new file mode 100644 index 0000000..ed28dc8 --- /dev/null +++ b/src/ForeignPpmFormat.php @@ -0,0 +1,56 @@ + + * @copyright 2016 John Cupitt + * @license https://opensource.org/licenses/MIT MIT + * @link https://github.com/jcupitt/php-vips + */ + +namespace Jcupitt\Vips; + +/** + * The ForeignPpmFormat enum. + * @category Images + * @package Jcupitt\Vips + * @author John Cupitt + * @copyright 2016 John Cupitt + * @license https://opensource.org/licenses/MIT MIT + * @link https://github.com/jcupitt/php-vips + */ +abstract class ForeignPpmFormat +{ + const PBM = 'pbm'; + const PGM = 'pgm'; + const PPM = 'ppm'; + const PFM = 'pfm'; +} diff --git a/src/ForeignSubsample.php b/src/ForeignSubsample.php new file mode 100644 index 0000000..c46f73f --- /dev/null +++ b/src/ForeignSubsample.php @@ -0,0 +1,55 @@ + + * @copyright 2016 John Cupitt + * @license https://opensource.org/licenses/MIT MIT + * @link https://github.com/jcupitt/php-vips + */ + +namespace Jcupitt\Vips; + +/** + * The ForeignSubsample enum. + * @category Images + * @package Jcupitt\Vips + * @author John Cupitt + * @copyright 2016 John Cupitt + * @license https://opensource.org/licenses/MIT MIT + * @link https://github.com/jcupitt/php-vips + */ +abstract class ForeignSubsample +{ + const AUTO = 'auto'; + const ON = 'on'; + const OFF = 'off'; +} diff --git a/src/ForeignTiffCompression.php b/src/ForeignTiffCompression.php index 0d5a6b0..3c459f1 100644 --- a/src/ForeignTiffCompression.php +++ b/src/ForeignTiffCompression.php @@ -57,4 +57,5 @@ abstract class ForeignTiffCompression const LZW = 'lzw'; const WEBP = 'webp'; const ZSTD = 'zstd'; + const JP2K = 'jp2k'; } diff --git a/src/ImageAutodoc.php b/src/ImageAutodoc.php index 79abaf8..e917baf 100644 --- a/src/ImageAutodoc.php +++ b/src/ImageAutodoc.php @@ -227,6 +227,8 @@ * @throws Exception * @method static Image fitsload(string $filename, array $options = []) Load a FITS image. * @throws Exception + * @method static Image fitsload_source(string $source, array $options = []) Load FITS from a source. + * @throws Exception * @method void fitssave(string $filename, array $options = []) Save image to fits file. * @throws Exception * @method Image flatten(array $options = []) Flatten alpha out of an image. @@ -252,11 +254,17 @@ * @throws Exception * @method array getpoint(integer $x, integer $y, array $options = []) Read a point from an image. * @throws Exception - * @method static Image gifload(string $filename, array $options = []) Load GIF with giflib. + * @method static Image gifload(string $filename, array $options = []) Load GIF with libnsgif. + * @throws Exception + * @method static Image gifload_buffer(string $buffer, array $options = []) Load GIF with libnsgif. + * @throws Exception + * @method static Image gifload_source(string $source, array $options = []) Load gif from source. * @throws Exception - * @method static Image gifload_buffer(string $buffer, array $options = []) Load GIF with giflib. + * @method void gifsave(string $filename, array $options = []) Save as gif. * @throws Exception - * @method static Image gifload_source(string $source, array $options = []) Load GIF with giflib. + * @method string gifsave_buffer(array $options = []) Save as gif. + * @throws Exception + * @method void gifsave_target(string $target, array $options = []) Save as gif. * @throws Exception * @method Image globalbalance(array $options = []) Global balance an image mosaic. * @throws Exception @@ -324,6 +332,18 @@ * @method Image join(Image $in2, string $direction, array $options = []) Join a pair of images. * @see Direction for possible values for $direction * @throws Exception + * @method static Image jp2kload(string $filename, array $options = []) Load JPEG2000 image. + * @throws Exception + * @method static Image jp2kload_buffer(string $buffer, array $options = []) Load JPEG2000 image. + * @throws Exception + * @method static Image jp2kload_source(string $source, array $options = []) Load JPEG2000 image. + * @throws Exception + * @method void jp2ksave(string $filename, array $options = []) Save image in JPEG2000 format. + * @throws Exception + * @method string jp2ksave_buffer(array $options = []) Save image in JPEG2000 format. + * @throws Exception + * @method void jp2ksave_target(string $target, array $options = []) Save image in JPEG2000 format. + * @throws Exception * @method static Image jpegload(string $filename, array $options = []) Load jpeg from file. * @throws Exception * @method static Image jpegload_buffer(string $buffer, array $options = []) Load jpeg from buffer. @@ -338,13 +358,25 @@ * @throws Exception * @method void jpegsave_target(string $target, array $options = []) Save image to jpeg target. * @throws Exception + * @method static Image jxlload(string $filename, array $options = []) Load JPEG-XL image. + * @throws Exception + * @method static Image jxlload_buffer(string $buffer, array $options = []) Load JPEG-XL image. + * @throws Exception + * @method static Image jxlload_source(string $source, array $options = []) Load JPEG-XL image. + * @throws Exception + * @method void jxlsave(string $filename, array $options = []) Save image in JPEG-XL format. + * @throws Exception + * @method string jxlsave_buffer(array $options = []) Save image in JPEG-XL format. + * @throws Exception + * @method void jxlsave_target(string $target, array $options = []) Save image in JPEG-XL format. + * @throws Exception * @method Image labelregions(array $options = []) Label regions in an image. * @throws Exception * @method Image linear(float[]|float $a, float[]|float $b, array $options = []) Calculate (a * in + b). * @throws Exception * @method Image linecache(array $options = []) Cache an image as a set of lines. * @throws Exception - * @method static Image logmat(float $sigma, float $min_ampl, array $options = []) Make a laplacian of gaussian image. + * @method static Image logmat(float $sigma, float $min_ampl, array $options = []) Make a Laplacian of Gaussian image. * @throws Exception * @method static Image magickload(string $filename, array $options = []) Load file with ImageMagick. * @throws Exception @@ -423,7 +455,9 @@ * @throws Exception * @method Image msb(array $options = []) Pick most-significant byte from an image. * @throws Exception - * @method static Image niftiload(string $filename, array $options = []) Load a NIFTI image. + * @method static Image niftiload(string $filename, array $options = []) Load NIfTI volume. + * @throws Exception + * @method static Image niftiload_source(string $source, array $options = []) Load NIfTI volumes. * @throws Exception * @method void niftisave(string $filename, array $options = []) Save image to nifti file. * @throws Exception @@ -431,6 +465,8 @@ * @throws Exception * @method static Image openslideload(string $filename, array $options = []) Load file with OpenSlide. * @throws Exception + * @method static Image openslideload_source(string $source, array $options = []) Load source with OpenSlide. + * @throws Exception * @method static Image pdfload(string $filename, array $options = []) Load PDF from file. * @throws Exception * @method static Image pdfload_buffer(string $buffer, array $options = []) Load PDF from buffer. @@ -617,7 +653,11 @@ * @throws Exception * @method static Image vipsload(string $filename, array $options = []) Load vips from file. * @throws Exception - * @method void vipssave(string $filename, array $options = []) Save image to vips file. + * @method static Image vipsload_source(string $source, array $options = []) Load vips from source. + * @throws Exception + * @method void vipssave(string $filename, array $options = []) Save image to file in vips format. + * @throws Exception + * @method void vipssave_target(string $target, array $options = []) Save image to target in vips format. * @throws Exception * @method static Image webpload(string $filename, array $options = []) Load webp from file. * @throws Exception diff --git a/src/OperationMath.php b/src/OperationMath.php index 52ffef1..857b488 100644 --- a/src/OperationMath.php +++ b/src/OperationMath.php @@ -59,4 +59,10 @@ abstract class OperationMath const LOG10 = 'log10'; const EXP = 'exp'; const EXP10 = 'exp10'; + const SINH = 'sinh'; + const COSH = 'cosh'; + const TANH = 'tanh'; + const ASINH = 'asinh'; + const ACOSH = 'acosh'; + const ATANH = 'atanh'; } diff --git a/src/OperationMath2.php b/src/OperationMath2.php index 9e86093..b1fc69e 100644 --- a/src/OperationMath2.php +++ b/src/OperationMath2.php @@ -51,4 +51,5 @@ abstract class OperationMath2 { const POW = 'pow'; const WOP = 'wop'; + const ATAN2 = 'atan2'; } From 9080e310919285e4509d0564954c8c7468f84f06 Mon Sep 17 00:00:00 2001 From: Boris Glumpler Date: Thu, 3 Feb 2022 23:33:01 +0100 Subject: [PATCH 026/123] Allowed for psr/log 2.0 and 3.0 to be installed as well --- composer.json | 4 ++-- src/DebugLogger.php | 2 +- tests/LoggerTest.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index b945a16..fa0b05c 100644 --- a/composer.json +++ b/composer.json @@ -17,9 +17,9 @@ } ], "require": { - "php": ">=7.1", + "php": ">=7.2", "ext-vips": ">=0.1.2", - "psr/log": "^1.1.3" + "psr/log": "^1.1.3|^2.0|^3.0" }, "require-dev": { "php-parallel-lint/php-parallel-lint": "^1.2", diff --git a/src/DebugLogger.php b/src/DebugLogger.php index 4cfa6ee..ddf8373 100644 --- a/src/DebugLogger.php +++ b/src/DebugLogger.php @@ -69,7 +69,7 @@ class DebugLogger implements LoggerInterface * * @return void */ - public function log($level, $message, array $context = []) + public function log($level, $message, array $context = []): void { // `Vips\Image` to string convert array_walk_recursive($context, function (&$value) { diff --git a/tests/LoggerTest.php b/tests/LoggerTest.php index ea79e67..c7f1d37 100644 --- a/tests/LoggerTest.php +++ b/tests/LoggerTest.php @@ -33,7 +33,7 @@ public function testSetLoggerCall() * * @return void */ - public function log($level, $message, array $context = array()) + public function log($level, $message, array $context = array()): void { // Do logging logic here. } From fc7516c58e58c4e577e6f1797ef0054c65b0215f Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Thu, 10 Mar 2022 08:15:14 +0000 Subject: [PATCH 027/123] Switch to php ffi (#133) Rewrite for php-ffi --- CHANGELOG.md | 25 +- README.md | 82 +-- RELEASE-1.0.8 => RELEASE-2.0.0 | 0 composer.json | 14 +- examples/composer.json | 5 + examples/watermark-image.php | 44 ++ examples/watermark-text.php | 4 +- src/ArgumentFlags.php | 69 +++ src/Config.php | 689 +++++++++++++++++++++++- src/GObject.php | 107 ++++ src/GValue.php | 342 ++++++++++++ src/Image.php | 936 +++++++++++++-------------------- src/ImageAutodoc.php | 2 +- src/Interpolate.php | 104 ++++ src/Introspect.php | 252 +++++++++ src/Utils.php | 2 +- src/VipsObject.php | 187 +++++++ src/VipsOperation.php | 413 +++++++++++++++ tests/NewTest.php | 17 - tests/ShortcutTest.php | 2 + tests/WriteTest.php | 14 +- 21 files changed, 2656 insertions(+), 654 deletions(-) rename RELEASE-1.0.8 => RELEASE-2.0.0 (100%) create mode 100644 examples/composer.json create mode 100755 examples/watermark-image.php create mode 100644 src/ArgumentFlags.php create mode 100644 src/GObject.php create mode 100644 src/GValue.php create mode 100644 src/Interpolate.php create mode 100644 src/Introspect.php create mode 100644 src/VipsObject.php create mode 100644 src/VipsOperation.php diff --git a/CHANGELOG.md b/CHANGELOG.md index b798b57..9139f34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,27 @@ # Changelog + All notable changes to `:vips` will be documented in this file. +## 2.0.0 - 2022-1-20 + +Rewritten to use PHP FFI to call into the libvips library rather than a binary +extension. This means php-vips now requires php 7.4 or later. + +### Added +- `Interpolate` class + +### Deprecated +- Nothing + +### Fixed +- Nothing + +### Remove +- Nothing + +### Security +- Nothing + ### 1.0.9 - 2021-11-20 ### Added @@ -18,7 +39,7 @@ All notable changes to `:vips` will be documented in this file. ### Security - Nothing -### 1.0.8 - 2020-08-29 +## 1.0.8 - 2020-08-29 ### Added - allow type names as type params to Image::setType() -- fixes issue with GType @@ -36,7 +57,7 @@ All notable changes to `:vips` will be documented in this file. ### Security - Nothing -### 1.0.7 - 2020-08-28 +## 1.0.7 - 2020-08-28 ### Added - use nullable types and void return type where possible diff --git a/README.md b/README.md index 447f4ef..592ef6b 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ [![Build Status](https://travis-ci.org/libvips/php-vips.svg?branch=master)](https://travis-ci.org/libvips/php-vips) -`php-vips` is a binding for [libvips](https://github.com/libvips/libvips) for -PHP 7. +`php-vips` is a binding for [libvips](https://github.com/libvips/libvips) 8.7 +and later for PHP 7.4 and later. libvips is fast and needs little memory. The [`vips-php-bench`](https://github.com/jcupitt/php-vips-bench) repository @@ -17,54 +17,30 @@ image. When the pipe is connected to a destination, the whole pipeline executes at once and in parallel, streaming the image from source to destination in a set of small fragments. -This module builds upon the `vips` PHP extension: +### Install -https://github.com/libvips/php-vips-ext +You need to [install the libvips +library](https://libvips.github.io/libvips/install.html). It's in the linux +package managers, homebrew and MacPorts, and there are Windows binaries on +the vips website. For example, on Debian: -You'll need to install that first. It's tested on Linux and macOS --- -Windows would need some work, but should be possible. - -See the README there, but briefly: - -1. [Install the libvips library and - headers](https://libvips.github.io/libvips/install.html). It's in - the linux package managers, homebrew and MacPorts, and there are Windows - binaries on the vips website. For example, on Debian: - - ``` - sudo apt-get install libvips-dev - ``` - - Or macOS: - - ``` - brew install vips - ``` - -2. Install the binary PHP extension. You'll need a PHP development environment - for this, since it will download and build the sources for the extension. - For example, on Debian: - - ``` - sudo apt-get install php-pear - ``` - - Then to download and build the extension it's: +``` +sudo apt-get install libvips-dev +``` - ``` - pecl install vips - ``` +Or macOS: - You may need to add `extension=vips.so` or equivalent to `php.ini`, see the - output of pecl. +``` +brew install vips +``` -3. Add vips to your `composer.json`: +Then add vips to your `composer.json`: - ``` - "require": { - "jcupitt/vips" : "1.0.7" - } - ``` +``` +"require": { + "jcupitt/vips" : "2.0.0" +} +``` ### Example @@ -156,17 +132,17 @@ introduction: https://libvips.github.io/libvips/API/current -### How it works +### TODO after merge + +- Support preloading, see https://www.php.net/manual/en/class.ffi.php + +- Rewrite the enum and doc generator in php. + +- Add source/target API -The `vips` extension defines a simple but ugly way to call any libvips -operation from PHP. It uses libvips' own introspection facilities -and does not depend on anything else (so no gobject-introspection, -for example). It's a fairly short 1,600 lines of C. +- Add progress callbacks etc. -This module is a PHP layer over the ugly `vips` extension that -tries to make a nice interface for programmers. It uses `__call()` and -`__get()` to make all libvips operations appear as methods, and all -libvips properties as properties of the PHP `Vips\Image` class. +- Add mutable. ### Test and install diff --git a/RELEASE-1.0.8 b/RELEASE-2.0.0 similarity index 100% rename from RELEASE-1.0.8 rename to RELEASE-2.0.0 diff --git a/composer.json b/composer.json index fa0b05c..2c61251 100644 --- a/composer.json +++ b/composer.json @@ -17,15 +17,15 @@ } ], "require": { - "php": ">=7.2", - "ext-vips": ">=0.1.2", + "php": ">=7.4", + "ext-ffi": "*", "psr/log": "^1.1.3|^2.0|^3.0" }, "require-dev": { - "php-parallel-lint/php-parallel-lint": "^1.2", - "phpdocumentor/phpdocumentor": "3.0.0", - "phpunit/phpunit": "^9.3", - "squizlabs/php_codesniffer": "^3.5" + "php-parallel-lint/php-parallel-lint": "^1.3", + "phpdocumentor/shim": "^3.3", + "phpunit/phpunit": "^9.5", + "squizlabs/php_codesniffer": "^3.6" }, "autoload": { "psr-4": { @@ -39,7 +39,7 @@ }, "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0.x-dev" } }, "scripts": { diff --git a/examples/composer.json b/examples/composer.json new file mode 100644 index 0000000..2de9b0e --- /dev/null +++ b/examples/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "jcupitt/vips": "2.0.0" + } +} diff --git a/examples/watermark-image.php b/examples/watermark-image.php new file mode 100755 index 0000000..195343f --- /dev/null +++ b/examples/watermark-image.php @@ -0,0 +1,44 @@ +#!/usr/bin/env php + 'sequential']); + +// we'll read the watermark image many times, so we need random access for this +$watermark = Vips\Image::newFromFile($argv[3]); + +// the watermark image needs to have an alpha channel +if (!$watermark->hasAlpha() || $watermark->bands != 4) { + echo("watermark image is not RGBA\n"); + exit(1); +} + +// make the watermark semi-transparent +$watermark = $watermark->multiply([1, 1, 1, 0.3])->cast("uchar"); + +// repeat the watermark to the size of the image +$watermark = $watermark->replicate( + 1 + $image->width / $watermark->width, + 1 + $image->height / $watermark->height +); +$watermark = $watermark->crop(0, 0, $image->width, $image->height); + +// composite the watermark over the main image +$image = $image->composite2($watermark, 'over'); + +$image->writeToFile($argv[2]); + +$image = null; +$watermark = null; + +Vips\Config::shutDown(); diff --git a/examples/watermark-text.php b/examples/watermark-text.php index 3a0377d..654cada 100755 --- a/examples/watermark-text.php +++ b/examples/watermark-text.php @@ -4,6 +4,8 @@ require __DIR__ . '/vendor/autoload.php'; use Jcupitt\Vips; +#Vips\Config::setLogger(new Vips\DebugLogger()); + if (count($argv) != 4) { echo("usage: ./watermark-text.php input output \"some text\"\n"); exit(1); @@ -30,7 +32,7 @@ $foreground = [255, 255, 255, 50]; $background = [0, 0, 255, 50]; -// and a 10-pixel marghin +// and a 10-pixel margin $margin = 10; $overlay = $text_mask->ifthenelse($foreground, $background, [ diff --git a/src/ArgumentFlags.php b/src/ArgumentFlags.php new file mode 100644 index 0000000..cb54e69 --- /dev/null +++ b/src/ArgumentFlags.php @@ -0,0 +1,69 @@ + + * @copyright 2016 John Cupitt + * @license https://opensource.org/licenses/MIT MIT + * @link https://github.com/jcupitt/php-vips + */ + +namespace Jcupitt\Vips; + +/** + * The ArgumentFlags enum. + * @category Images + * @package Jcupitt\Vips + * @author John Cupitt + * @copyright 2016 John Cupitt + * @license https://opensource.org/licenses/MIT MIT + * @link https://github.com/jcupitt/php-vips + */ +abstract class ArgumentFlags +{ + const REQUIRED = 1; + const CONSTRUCT = 2; + const SET_ONCE = 4; + const SET_ALWAYS = 8; + const INPUT = 16; + const OUTPUT = 32; + const DEPRECATED = 64; + const MODIFY = 128; + + const NAMES = [ + "REQUIRED" => self::REQUIRED, + "CONSTRUCT" => self::CONSTRUCT, + "SET_ONCE" => self::SET_ONCE, + "SET_ALWAYS" => self::SET_ALWAYS, + "INPUT" => self::INPUT, + "OUTPUT" => self::OUTPUT, + "DEPRECATED" => self::DEPRECATED, + "MODIFY" => self::MODIFY, + ]; +} diff --git a/src/Config.php b/src/Config.php index b546870..ed720df 100644 --- a/src/Config.php +++ b/src/Config.php @@ -60,6 +60,32 @@ class Config */ private static $logger; + /** + * The FFI handle we use for the libvips binary. + * + * @internal + */ + private static \FFI $ffi; + private static $ffi_inited = false; + + /** + * The library version number we detect. + * + * @internal + */ + private static int $library_major; + private static int $library_minor; + private static int $library_micro; + + /** + * Look up these once. + * + * @internal + */ + private static array $ctypes; + private static array $gtypes; + private static array $ftypes; + /** * Sets a logger. This can be handy for debugging. For example: * @@ -96,7 +122,7 @@ public static function getLogger(): ?LoggerInterface */ public static function cacheSetMax(int $value): void { - vips_cache_set_max($value); + Config::ffi()->vips_cache_set_max($value); } /** @@ -110,7 +136,7 @@ public static function cacheSetMax(int $value): void */ public static function cacheSetMaxMem(int $value): void { - vips_cache_set_max_mem($value); + Config::ffi()->vips_cache_set_max_mem($value); } /** @@ -123,7 +149,7 @@ public static function cacheSetMaxMem(int $value): void */ public static function cacheSetMaxFiles(int $value): void { - vips_cache_set_max_files($value); + Config::ffi()->vips_cache_set_max_files($value); } /** @@ -137,7 +163,7 @@ public static function cacheSetMaxFiles(int $value): void */ public static function concurrencySet(int $value): void { - vips_concurrency_set($value); + Config::ffi()->vips_concurrency_set($value); } /** @@ -148,7 +174,660 @@ public static function concurrencySet(int $value): void */ public static function version(): string { - return vips_version(); + Config::ffi(); + + return self::$library_major . "." . + self::$library_minor . ".". + self::$library_micro; + } + + /** + * Handy for debugging. + */ + public static function printAll() + { + Config::ffi()->vips_object_print_all(); + } + + public static function ffi() + { + if (!self::$ffi_inited) { + self::init(); + self::$ffi_inited = true; + } + + return self::$ffi; + } + + public static function ctypes(string $name) + { + Config::ffi(); + + return self::$ctypes[$name]; + } + + public static function gtypes(string $name) + { + Config::ffi(); + + return self::$gtypes[$name]; + } + + public static function ftypes(string $name) + { + Config::ffi(); + + return self::$ftypes[$name]; + } + + /** + * Throw a vips error as an exception. + * + * @throws Exception + * + * @return void + * + * @internal + */ + public static function error(string $message = "") + { + if ($message == "") { + $message = Config::ffi()->vips_error_buffer(); + Config::ffi()->vips_error_clear(); + } + $exception = new Exception($message); + Utils::errorLog($message, $exception); + throw $exception; + } + + /** + * Shut down libvips. Call this just before process exit. + * + * @throws Exception + * + * @return void + * + * @internal + */ + public static function shutDown() + { + Config::ffi()->vips_shutdown(); + } + + public static function filenameGetFilename($name) + { + $pointer = Config::ffi()->vips_filename_get_filename($name); + $filename = \FFI::string($pointer); + Config::ffi()->g_free($pointer); + + return $filename; + } + + public static function filenameGetOptions($name) + { + $pointer = Config::ffi()->vips_filename_get_options($name); + $options = \FFI::string($pointer); + Config::ffi()->g_free($pointer); + + return $options; + } + + public static function atLeast($need_major, $need_minor) + { + return $need_major < self::$library_major || + ($need_major == self::$library_major && + $need_minor <= self::$library_minor); + } + + private static function libraryName($name, $abi) + { + switch (PHP_OS_FAMILY) { + case "Windows": + return "$name-$abi.dll"; + + case "OSX": + return "$name.$abi.dylib"; + + default: + // most *nix + return "$name.so.$abi"; + } + } + + private static function init() + { + $library = self::libraryName("libvips", 42); + + Utils::debugLog("init", ["libray" => $library]); + + /* FIXME ... maybe display a helpful message on failure? This will + * probably be the main point of failure. + */ + $ffi = \FFI::cdef(<<vips_init(""); + if ($result != 0) { + $msg = $ffi->vips_error_buffer(); + throw new Vips\Exception("libvips error: $msg"); + } + Utils::debugLog("init", ["vips_init" => $result]); + + # get the library version number, then we can build the API + self::$library_major = $ffi->vips_version(0); + self::$library_minor = $ffi->vips_version(1); + self::$library_micro = $ffi->vips_version(2); + Utils::debugLog("init", [ + "libvips version" => [ + self::$library_major, + self::$library_minor, + self::$library_micro + ] + ]); + + if (!self::atLeast(8, 7)) { + throw new Vips\Exception("your libvips is too old -- " . + "8.7 or later required"); + } + + if (PHP_INT_SIZE != 8) { + # we could maybe fix this if it's important ... it's mostly + # necessary since GType is the size of a pointer, and there's no + # easy way to discover if php is running on a 32 or 64-bit + # systems (as far as I can see) + throw new Vips\Exception("your php only supports 32-bit ints -- " . + "64 bit ints required"); + } + + # the whole libvips API, mostly adapted from pyvips + $header = <<vips_blend_mode_get_type(); + self::$ffi->vips_interpretation_get_type(); + self::$ffi->vips_operation_flags_get_type(); + self::$ffi->vips_band_format_get_type(); + self::$ffi->vips_token_get_type(); + self::$ffi->vips_saveable_get_type(); + self::$ffi->vips_image_type_get_type(); + + // look these up in advance + self::$ctypes = [ + "GObject" => self::$ffi->type("GObject*"), + "VipsObject" => self::$ffi->type("VipsObject*"), + "VipsOperation" => self::$ffi->type("VipsOperation*"), + "VipsImage" => self::$ffi->type("VipsImage*"), + "VipsInterpolate" => self::$ffi->type("VipsInterpolate*"), + ]; + + self::$gtypes = [ + "gboolean" => self::$ffi->g_type_from_name("gboolean"), + "gint" => self::$ffi->g_type_from_name("gint"), + "gint64" => self::$ffi->g_type_from_name("gint64"), + "guint64" => self::$ffi->g_type_from_name("guint64"), + "gdouble" => self::$ffi->g_type_from_name("gdouble"), + "gchararray" => self::$ffi->g_type_from_name("gchararray"), + "VipsRefString" => self::$ffi->g_type_from_name("VipsRefString"), + + "GEnum" => self::$ffi->g_type_from_name("GEnum"), + "GFlags" => self::$ffi->g_type_from_name("GFlags"), + "VipsBandFormat" => self::$ffi->g_type_from_name("VipsBandFormat"), + "VipsBlendMode" => self::$ffi->g_type_from_name("VipsBlendMode"), + "VipsArrayInt" => self::$ffi->g_type_from_name("VipsArrayInt"), + "VipsArrayDouble" => + self::$ffi->g_type_from_name("VipsArrayDouble"), + "VipsArrayImage" => self::$ffi->g_type_from_name("VipsArrayImage"), + "VipsBlob" => self::$ffi->g_type_from_name("VipsBlob"), + + "GObject" => self::$ffi->g_type_from_name("GObject"), + "VipsImage" => self::$ffi->g_type_from_name("VipsImage"), + ]; + + // map vips format names to c type names + self::$ftypes = [ + "char" => "char", + "uchar" => "unsigned char", + "short" => "short", + "ushort" => "unsigned short", + "int" => "int", + "uint" => "unsigned int", + "float" => "float", + "double" => "double", + "complex" => "float", + "dpcomplex" => "double", + ]; + + Utils::debugLog("init", ["done"]); } } diff --git a/src/GObject.php b/src/GObject.php new file mode 100644 index 0000000..74c35ef --- /dev/null +++ b/src/GObject.php @@ -0,0 +1,107 @@ + + * @copyright 2016 John Cupitt + * @license https://opensource.org/licenses/MIT MIT + * @link https://github.com/libvips/php-vips + */ + +namespace Jcupitt\Vips; + +/** + * This class holds a pointer to a GObject and manages object lifetime. + * + * @category Images + * @package Jcupitt\Vips + * @author John Cupitt + * @copyright 2016 John Cupitt + * @license https://opensource.org/licenses/MIT MIT + * @link https://github.com/libvips/php-vips + */ +abstract class GObject +{ + /** + * A pointer to the underlying GObject. + * + * @internal + */ + private \FFI\CData $pointer; + + /** + * Wrap a GObject around an underlying vips resource. The GObject takes + * ownership of the pointer and will unref it on finalize. + * + * Don't call this yourself, users should stick to (for example) + * Image::newFromFile(). + * + * @param FFI\CData $pointer The underlying pointer that this + * object should wrap. + * + * @internal + */ + public function __construct($pointer) + { + $this->pointer = \FFI::cast(Config::ctypes("GObject"), $pointer); + } + + public function __destruct() + { + $this->unref(); + } + + public function __clone() + { + $this->ref(); + } + + public function ref() + { + Config::ffi()->g_object_ref($this->pointer); + } + + public function unref() + { + Config::ffi()->g_object_unref($this->pointer); + } + + // TODO signal marshalling to go in +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: expandtab sw=4 ts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 + */ diff --git a/src/GValue.php b/src/GValue.php new file mode 100644 index 0000000..c0ce60d --- /dev/null +++ b/src/GValue.php @@ -0,0 +1,342 @@ + + * @copyright 2016 John Cupitt + * @license https://opensource.org/licenses/MIT MIT + * @link https://github.com/libvips/php-vips + */ + +namespace Jcupitt\Vips; + +class GValue +{ + private \FFI\CData $struct; + public \FFI\CData $pointer; + + public function __construct() + { + # allocate a gvalue on the heap, and make it persistent between requests + $this->struct = Config::ffi()->new("GValue", true, true); + $this->pointer = \FFI::addr($this->struct); + + # GValue needs to be inited to all zero + \FFI::memset($this->pointer, 0, \FFI::sizeof($this->struct)); + } + + /* Turn a string into an enum value, if possible + */ + public static function toEnum($gtype, $value) + { + if (is_string($value)) { + $enum_value = Config::ffi()-> + vips_enum_from_nick("php-vips", $gtype, $value); + if ($enum_value < 0) { + echo "gtype = " . $gtype . "\n"; + echo "value = " . $value . "\n"; + Config::error(); + } + } else { + $enum_value = $value; + } + + return $enum_value; + } + + public static function fromEnum($gtype, $value) + { + $result = Config::ffi()->vips_enum_nick($gtype, $value); + if ($result === null) { + Config::error("value not in enum"); + } + + return $result; + } + + public function __destruct() + { + Config::ffi()->g_value_unset($this->pointer); + } + + public function setType(int $gtype) + { + Config::ffi()->g_value_init($this->pointer, $gtype); + } + + public function getType(): int + { + return $this->pointer->g_type; + } + + public function set($value) + { + $gtype = $this->getType(); + + switch ($gtype) { + case Config::gtypes("gboolean"): + Config::ffi()->g_value_set_boolean($this->pointer, $value); + break; + + case Config::gtypes("gint"): + Config::ffi()->g_value_set_int($this->pointer, $value); + break; + + case Config::gtypes("gint64"): + Config::ffi()->g_value_set_int64($this->pointer, $value); + break; + + case Config::gtypes("guint64"): + Config::ffi()->g_value_set_uint64($this->pointer, $value); + break; + + case Config::gtypes("gdouble"): + Config::ffi()->g_value_set_double($this->pointer, $value); + break; + + case Config::gtypes("gchararray"): + Config::ffi()->g_value_set_string($this->pointer, $value); + break; + + case Config::gtypes("VipsRefString"): + Config::ffi()-> + vips_value_set_ref_string($this->pointer, $value); + break; + + case Config::gtypes("VipsArrayInt"): + if (!is_array($value)) { + $value = [$value]; + } + $n = count($value); + $ctype = \FFI::arrayType(\FFI::type("int"), [$n]); + $array = \FFI::new($ctype); + for ($i = 0; $i < $n; $i++) { + $array[$i] = $value[$i]; + } + Config::ffi()-> + vips_value_set_array_int($this->pointer, $array, $n); + break; + + case Config::gtypes("VipsArrayDouble"): + if (!is_array($value)) { + $value = [$value]; + } + $n = count($value); + $ctype = \FFI::arrayType(\FFI::type("double"), [$n]); + $array = \FFI::new($ctype); + for ($i = 0; $i < $n; $i++) { + $array[$i] = $value[$i]; + } + Config::ffi()-> + vips_value_set_array_double($this->pointer, $array, $n); + break; + + case Config::gtypes("VipsArrayImage"): + if (!is_array($value)) { + $value = [$value]; + } + $n = count($value); + Config::ffi()->vips_value_set_array_image($this->pointer, $n); + $array = Config::ffi()-> + vips_value_get_array_image($this->pointer, null); + for ($i = 0; $i < $n; $i++) { + $image = $value[$i]; + $array[$i] = $image->pointer; + $image->ref(); + } + break; + + case Config::gtypes("VipsBlob"): + # we need to set the blob to a copy of the data that vips_lib + # can own and free + $n = strlen($value); + $ctype = \FFI::arrayType(\FFI::type("char"), [$n]); + $memory = \FFI::new($ctype, false, true); + for ($i = 0; $i < $n; $i++) { + $memory[$i] = $value[$i]; + } + Config::ffi()-> + vips_value_set_blob_free($this->pointer, $memory, $n); + break; + + default: + $fundamental = Config::ffi()->g_type_fundamental($gtype); + switch ($fundamental) { + case Config::gtypes("GObject"): + Config::ffi()-> + g_value_set_object($this->pointer, $value->pointer); + break; + + case Config::gtypes("GEnum"): + Config::ffi()->g_value_set_enum( + $this->pointer, + self::toEnum($gtype, $value) + ); + break; + + case Config::gtypes("GFlags"): + /* Just set as int. + */ + Config::ffi()-> + g_value_set_flags($this->pointer, $value); + break; + + default: + $typeName = Config::ffi()->g_type_name($gtype); + throw new \BadMethodCallException( + "gtype $typeName ($gtype) not implemented" + ); + break; + } + } + } + + public function get() + { + $gtype = $this->getType(); + $result = null; + + switch ($gtype) { + case Config::gtypes("gboolean"): + $result = Config::ffi()->g_value_get_boolean($this->pointer); + break; + + case Config::gtypes("gint"): + $result = Config::ffi()->g_value_get_int($this->pointer); + break; + + case Config::gtypes("gint64"): + $result = Config::ffi()->g_value_get_int64($this->pointer); + break; + + case Config::gtypes("guint64"): + $result = Config::ffi()->g_value_get_uint64($this->pointer); + break; + + case Config::gtypes("gdouble"): + $result = Config::ffi()->g_value_get_double($this->pointer); + break; + + case Config::gtypes("gchararray"): + $result = Config::ffi()->g_value_get_string($this->pointer); + break; + + case Config::gtypes("VipsRefString"): + $p_size = Config::ffi()->new("size_t[1]"); + $result = Config::ffi()-> + vips_value_get_ref_string($this->pointer, $p_size); + # $p_size[0] will be the string length, but assume it's null + # terminated + break; + + case Config::gtypes("VipsImage"): + $pointer = Config::ffi()->g_value_get_object($this->pointer); + $result = new Image($pointer); + // get_object does not increment the ref count + $result->ref(); + break; + + case Config::gtypes("VipsArrayInt"): + $p_len = Config::ffi()->new("int[1]"); + $pointer = Config::ffi()-> + vips_value_get_array_int($this->pointer, $p_len); + $result = []; + for ($i = 0; $i < $p_len[0]; $i++) { + $result[] = $pointer[$i]; + } + break; + + case Config::gtypes("VipsArrayDouble"): + $p_len = Config::ffi()->new("int[1]"); + $pointer = Config::ffi()-> + vips_value_get_array_double($this->pointer, $p_len); + $result = []; + for ($i = 0; $i < $p_len[0]; $i++) { + $result[] = $pointer[$i]; + } + break; + + case Config::gtypes("VipsArrayImage"): + $p_len = Config::ffi()->new("int[1]"); + $pointer = Config::ffi()-> + vips_value_get_array_image($this->pointer, $p_len); + $result = []; + for ($i = 0; $i < $p_len[0]; $i++) { + $image = new Image($pointer[$i]); + $image->ref(); + $result[] = $image; + } + break; + + case Config::gtypes("VipsBlob"): + $p_len = Config::ffi()->new("size_t[1]"); + $pointer = Config::ffi()-> + vips_value_get_blob($this->pointer, $p_len); + $result = \FFI::string($pointer, $p_len[0]); + break; + + default: + $fundamental = Config::ffi()->g_type_fundamental($gtype); + switch ($fundamental) { + case Config::gtypes("GEnum"): + $result = Config::ffi()-> + g_value_get_enum($this->pointer); + $result = self::fromEnum($gtype, $result); + break; + + case Config::gtypes("GFlags"): + /* Just get as int. + */ + $result = Config::ffi()-> + g_value_get_flags($this->pointer); + break; + + default: + $typeName = Config::ffi()->g_type_name($gtype); + throw new \BadMethodCallException( + "gtype $typeName ($gtype) not implemented" + ); + break; + } + } + + return $result; + } +} + +/* +* Local variables: +* tab-width: 4 +* c-basic-offset: 4 +* End: +* vim600: expandtab sw=4 ts=4 fdm=marker +* vim<600: expandtab sw=4 ts=4 +*/ diff --git a/src/Image.php b/src/Image.php index 1f364bc..c6de83a 100644 --- a/src/Image.php +++ b/src/Image.php @@ -42,11 +42,8 @@ * This class represents a Vips image object. * * This module provides a binding for the [vips image processing - * library](https://jcupitt.github.io/libvips/). - * - * It needs libvips 8.0 or later to be installed, and it needs the binary - * [`vips` extension](https://github.com/jcupitt/php-vips-ext) to be added to - * your PHP. + * library](https://libvips.org) version 8.7 and later, and required PHP 7.4 + * and later. * * # Example * @@ -213,26 +210,6 @@ * Use `$image->get('ipct-data')` for property names which are not valid under * PHP syntax. * - * # How it works - * - * The binary - * [`vips` extension](https://github.com/jcupitt/php-vips-ext) adds a few extra - * functions to PHP to let you call anything in the libvips library. The API - * it provides is simple, but horrible. - * - * This module is pure PHP and builds on the binary extension to provide a - * convenient interface for programmers. It uses the PHP magic methods - * `__call()`, `__callStatic()`, `__get()` and `__set()` to make vips operators - * appear as methods on the `Image` class, and vips properties as PHP - * properties. - * - * The API you end up with is a object-oriented version of the [VIPS C - * API](https://jcupitt.github.io/libvips/API/current). - * Full documentation - * on the operations and what they do is there, you can use it directly. This - * document explains the extra features of the PHP API and lists the available - * operations very briefly. - * * # Automatic wrapping * * This binding has a `__call()` method and uses @@ -249,8 +226,8 @@ * produces several results. * * For example, `Image::min`, the vips operation that searches an image for - * the minimum value, has a large number of optional arguments. You can use it to - * find the minimum value like this: + * the minimum value, has a large number of optional arguments. You can use it + * to find the minimum value like this: * * ```php * $min_value = $image->min(); @@ -500,102 +477,26 @@ class Image extends ImageAutodoc implements \ArrayAccess { /** - * Map load nicknames to canonical names. Regenerate this table with - * something like: - * - * $ vips -l foreign | grep -i load | awk '{ print $2, $1; }' - * - * Plus a bit of editing. - * - * @internal - */ - private static $nicknameToCanonical = [ - 'csvload' => 'VipsForeignLoadCsv', - 'matrixload' => 'VipsForeignLoadMatrix', - 'rawload' => 'VipsForeignLoadRaw', - 'vipsload' => 'VipsForeignLoadVips', - 'analyzeload' => 'VipsForeignLoadAnalyze', - 'ppmload' => 'VipsForeignLoadPpm', - 'radload' => 'VipsForeignLoadRad', - 'pdfload' => 'VipsForeignLoadPdfFile', - 'pdfload_buffer' => 'VipsForeignLoadPdfBuffer', - 'svgload' => 'VipsForeignLoadSvgFile', - 'svgload_buffer' => 'VipsForeignLoadSvgBuffer', - 'gifload' => 'VipsForeignLoadGifFile', - 'gifload_buffer' => 'VipsForeignLoadGifBuffer', - 'pngload' => 'VipsForeignLoadPng', - 'pngload_buffer' => 'VipsForeignLoadPngBuffer', - 'matload' => 'VipsForeignLoadMat', - 'jpegload' => 'VipsForeignLoadJpegFile', - 'jpegload_buffer' => 'VipsForeignLoadJpegBuffer', - 'webpload' => 'VipsForeignLoadWebpFile', - 'webpload_buffer' => 'VipsForeignLoadWebpBuffer', - 'tiffload' => 'VipsForeignLoadTiffFile', - 'tiffload_buffer' => 'VipsForeignLoadTiffBuffer', - 'magickload' => 'VipsForeignLoadMagickFile', - 'magickload_buffer' => 'VipsForeignLoadMagickBuffer', - 'fitsload' => 'VipsForeignLoadFits', - 'openexrload' => 'VipsForeignLoadOpenexr' - ]; - - /** - * Combine takes an array of blend modes, passed to libvips as an array of - * int. Because libvips does now know they should be enums, we have to do - * the string->int conversion ourselves. We ought to introspect to find the - * mapping, but until we have the machinery for that, we just hardwire the - * mapping here. + * A pointer to the underlying VipsImage. This is the same as the + * GObject, just cast to VipsImage to help FFI. * * @internal */ - private static $blendModeToInt = [ - BlendMode::CLEAR => 0, - BlendMode::SOURCE => 1, - BlendMode::OVER => 2, - BlendMode::IN => 3, - BlendMode::OUT => 4, - BlendMode::ATOP => 5, - BlendMode::DEST => 6, - BlendMode::DEST_OVER => 7, - BlendMode::DEST_IN => 8, - BlendMode::DEST_OUT => 9, - BlendMode::DEST_ATOP => 10, - BlendMode::XOR1 => 11, - BlendMode::ADD => 12, - BlendMode::SATURATE => 13, - BlendMode::MULTIPLY => 14, - BlendMode::SCREEN => 15, - BlendMode::OVERLAY => 16, - BlendMode::DARKEN => 17, - BlendMode::LIGHTEN => 18, - BlendMode::COLOUR_DODGE => 19, - BlendMode::COLOUR_BURN => 20, - BlendMode::HARD_LIGHT => 21, - BlendMode::SOFT_LIGHT => 22, - BlendMode::DIFFERENCE => 23, - BlendMode::EXCLUSION => 24 - ]; - - /** - * The resource for the underlying VipsImage. - * - * @internal - */ - private $image; + public \FFI\CData $pointer; /** - * Wrap a Image around an underlying vips resource. + * Wrap an Image around an underlying CData pointer. * * Don't call this yourself, users should stick to (for example) * Image::newFromFile(). * - * @param resource $image The underlying vips image resource that this - * class should wrap. - * * @internal */ - public function __construct($image) + public function __construct($pointer) { - $this->image = $image; + $this->pointer = Config::ffi()-> + cast(Config::ctypes("VipsImage"), $pointer); + parent::__construct($pointer); } /** @@ -663,16 +564,15 @@ private static function is2D($value): bool * * @internal */ - private static function isImageish($value): bool + public static function isImageish($value): bool { return self::is2D($value) || $value instanceof Image; } /** * Turn a constant (eg. 1, '12', [1, 2, 3], [[1]]) into an image using - * match_image as a guide. + * this as a guide. * - * @param Image $match_image Use this image as a guide. * @param mixed $value Turn this into an image. * * @throws Exception @@ -681,124 +581,14 @@ private static function isImageish($value): bool * * @internal */ - private static function imageize(Image $match_image, $value): Image + public function imageize($value): Image { - if (self::is2D($value)) { - $result = self::newFromArray($value); + if ($value instanceof Image) { + return $value; + } elseif (self::is2D($value)) { + return self::newFromArray($value); } else { - $result = $match_image->newFromImage($value); - } - - return $result; - } - - /** - * Unwrap an array of stuff ready to pass down to the vips_ layer. We - * swap instances of the Image for the plain resource. - * - * @param array $result Unwrap this. - * - * @return array $result unwrapped, ready for vips. - * - * @internal - */ - private static function unwrap(array $result): array - { - array_walk_recursive($result, function (&$value) { - if ($value instanceof Image) { - $value = $value->image; - } - }); - - return $result; - } - - /** - * Is $value a VipsImage. - * - * @param mixed $value The thing to test. - * - * @return bool true if this is a vips image resource. - * - * @internal - */ - private static function isImage($value): bool - { - return is_resource($value) && - get_resource_type($value) === 'GObject'; - } - - /** - * Wrap up the result of a vips_ call ready to return it to PHP. We do - * two things: - * - * - If the array is a singleton, we strip it off. For example, many - * operations return a single result and there's no sense handling - * this as an array of values, so we transform ['out' => x] -> x. - * - * - Any VipsImage resources are rewrapped as instances of Image. - * - * @param mixed $result Wrap this up. - * - * @return mixed $result, but wrapped up as a php class. - * - * @internal - */ - private static function wrapResult($result) - { - if (!is_array($result)) { - $result = ['x' => $result]; - } - - array_walk_recursive($result, function (&$item) { - if (self::isImage($item)) { - $item = new self($item); - } - }); - - if (count($result) === 1) { - $result = array_shift($result); - } - - return $result; - } - - /** - * Throw a vips error as an exception. - * - * @throws Exception - * - * @return void - * - * @internal - */ - private static function errorVips(): void - { - $message = vips_error_buffer(); - $exception = new Exception($message); - Utils::errorLog($message, $exception); - throw $exception; - } - - /** - * Check the result of a vips_ call for an error, and throw an exception - * if we see one. - * - * This won't work for things like __get where a non-array return can be - * a valid return. - * - * @param mixed $result Test this. - * - * @throws Exception - * - * @return void - * - * @internal - */ - private static function errorIsArray($result): void - { - if (!is_array($result)) { - self::errorVips(); + return $this->newFromImage($value); } } @@ -856,32 +646,37 @@ private static function runCmplx(\Closure $func, Image $image): Image } /** - * Create a new Image from a file on disc. + * Handy for things like self::more. Call a 2-ary vips operator like + * 'more', but if the arg is not an image (ie. it's a constant), call + * 'more_const' instead. * - * @param string $filename The file to open. - * @param array $options Any options to pass on to the load operation. + * @param mixed $other The right-hand argument. + * @param string $base The base part of the operation name. + * @param string $op The action to invoke. + * @param array $options An array of options to pass to the operation. * * @throws Exception * - * @return Image A new Image. + * @return mixed The operation result. + * + * @internal */ - public static function newFromFile( - string $filename, + private function callEnum( + $other, + string $base, + string $op, array $options = [] - ): Image { - Utils::debugLog('newFromFile', [ - 'instance' => null, - 'arguments' => [$filename, $options] - ]); - - $options = self::unwrap($options); - $result = vips_image_new_from_file($filename, $options); - self::errorIsArray($result); - $result = self::wrapResult($result); - - Utils::debugLog('newFromFile', ['result' => $result]); - - return $result; + ) { + if (self::isImageish($other)) { + return VipsOperation::call($base, $this, [$other, $op], $options); + } else { + return VipsOperation::call( + $base . '_const', + $this, + [$op, $other], + $options + ); + } } /** @@ -895,61 +690,40 @@ public static function newFromFile( */ public static function findLoad(string $filename): ?string { - Utils::debugLog('findLoad', [ - 'instance' => null, - 'arguments' => [$filename] - ]); - - // added in 1.0.5 of the binary module - if (function_exists('vips_foreign_find_load')) { - $result = vips_foreign_find_load($filename); - } else { - $result = null; - - // fallback: use the vips-loader property ... this can be much slower - try { - $image = self::newFromFile($filename); - // Unfortunately, vips-loader is the operation nickname, rather - // than the canonical name returned by vips_foreign_find_load(). - $loader = $image->get('vips-loader'); - $result = self::$nicknameToCanonical[$loader]; - } catch (Exception $ignored) { - } - } - - Utils::debugLog('findLoad', ['result' => [$result]]); + $result = Config::ffi()->vips_foreign_find_load($filename); return $result; } /** - * Create a new Image from a compressed image held as a string. + * Create a new Image from a file on disc. * - * @param string $buffer The formatted image to open. - * @param string $option_string Any text-style options to pass to the - * selected loader. - * @param array $options Any options to pass on to the load operation. + * @param string $filename The file to open. + * @param array $options Any options to pass on to the load operation. * * @throws Exception * * @return Image A new Image. */ - public static function newFromBuffer( - string $buffer, - string $option_string = '', + public static function newFromFile( + string $name, array $options = [] ): Image { - Utils::debugLog('newFromBuffer', [ - 'instance' => null, - 'arguments' => [$buffer, $option_string, $options] - ]); + $filename = Config::filenameGetFilename($name); + $string_options = Config::filenameGetOptions($name); - $options = self::unwrap($options); - $result = vips_image_new_from_buffer($buffer, $option_string, $options); - self::errorIsArray($result); - $result = self::wrapResult($result); + $loader = self::findLoad($filename); + if ($loader == null) { + Config::error(); + } + + if (strlen($string_options) != 0) { + $options = array_merge([ + "string_options" => $string_options, + ], $options); + } - Utils::debugLog('newFromBuffer', ['result' => $result]); + $result = VipsOperation::call($loader, null, [$filename], $options); return $result; } @@ -965,30 +739,41 @@ public static function newFromBuffer( */ public static function findLoadBuffer(string $buffer): ?string { - Utils::debugLog('findLoadBuffer', [ - 'instance' => null, - 'arguments' => [$buffer] - ]); + $result = Config::ffi()-> + vips_foreign_find_load_buffer($buffer, strlen($buffer)); - // added in 1.0.5 of the binary module - if (function_exists('vips_foreign_find_load_buffer')) { - $result = vips_foreign_find_load_buffer($buffer); - } else { - $result = null; - - // fallback: use the vips-loader property ... this can be much slower - try { - $image = self::newFromBuffer($buffer); - // Unfortunately, vips-loader is the operation nickname, rather - // than the canonical name returned by - // vips_foreign_find_load_buffer(). - $loader = $image->get('vips-loader'); - $result = self::$nicknameToCanonical[$loader]; - } catch (Exception $ignored) { - } + return $result; + } + + /** + * Create a new Image from a compressed image held as a string. + * + * @param string $buffer The formatted image to open. + * @param string $string_options Any text-style options to pass to the + * selected loader. + * @param array $options Options to pass on to the load operation. + * + * @throws Exception + * + * @return Image A new Image. + */ + public static function newFromBuffer( + string $buffer, + string $string_options = '', + array $options = [] + ): Image { + $loader = self::findLoadBuffer($buffer); + if ($loader == null) { + Config::error(); + } + + if (strlen($string_options) != 0) { + $options = array_merge([ + "string_options" => $string_options, + ], $options); } - Utils::debugLog('findLoadBuffer', ['result' => [$result]]); + $result = VipsOperation::call($loader, null, [$buffer], $options); return $result; } @@ -1013,18 +798,31 @@ public static function newFromArray( float $scale = 1.0, float $offset = 0.0 ): Image { - Utils::debugLog('newFromArray', [ - 'instance' => null, - 'arguments' => [$array, $scale, $offset] - ]); + if (!self::is2D($array)) { + $array = [$array]; + } - $result = vips_image_new_from_array($array, $scale, $offset); - if ($result === -1) { - self::errorVips(); + $height = count($array); + $width = count($array[0]); + + $n = $width * $height; + $ctype = \FFI::arrayType(\FFI::type("double"), [$n]); + $a = \FFI::new($ctype, true, true); + for ($y = 0; $y < $height; $y++) { + for ($x = 0; $x < $width; $x++) { + $a[$x + $y * $width] = $array[$y][$x]; + } + } + + $pointer = Config::ffi()-> + vips_image_new_matrix_from_array($width, $height, $a, $n); + if ($pointer == null) { + Config::error(); } - $result = self::wrapResult($result); + $result = new Image($pointer); - Utils::debugLog('newFromArray', ['result' => $result]); + $result->setType(Config::gtypes("gdouble"), 'scale', $scale); + $result->setType(Config::gtypes("gdouble"), 'offset', $offset); return $result; } @@ -1032,7 +830,7 @@ public static function newFromArray( /** * Wraps an Image around an area of memory containing a C-style array. * - * @param string $data C-style array. + * @param mixed $data C-style array. * @param int $width Image width in pixels. * @param int $height Image height in pixels. * @param int $bands Number of bands. @@ -1043,51 +841,41 @@ public static function newFromArray( * @return Image A new Image. */ public static function newFromMemory( - string $data, + mixed $data, int $width, int $height, int $bands, string $format ): Image { - Utils::debugLog('newFromMemory', [ - 'instance' => null, - 'arguments' => [$data, $width, $height, $bands, $format] - ]); - - $result = vips_image_new_from_memory($data, $width, $height, $bands, $format); - if ($result === -1) { - self::errorVips(); + /* Take a copy of the memory area to avoid lifetime issues. + * + * TODO add a references system instead, see pyvips. + */ + $pointer = Config::ffi()->vips_image_new_from_memory_copy( + $data, + strlen($data), + $width, + $height, + $bands, + $format + ); + if ($pointer == null) { + Config::error(); } - $result = self::wrapResult($result); - Utils::debugLog('newFromMemory', ['result' => $result]); + $result = new Image($pointer); return $result; } /** - * Make an interpolator from a name. - * - * @param string $name Name of the interpolator. - * Possible interpolators are: - * - `'nearest'`: Use nearest neighbour interpolation. - * - `'bicubic'`: Use bicubic interpolation. - * - `'bilinear'`: Use bilinear interpolation (the default). - * - `'nohalo'`: Use Nohalo interpolation. - * - `'lbb'`: Use LBB interpolation. - * - `'vsqbs'`: Use the VSQBS interpolation. + * Deprecated thing to make an interpolator. * - * @return resource|null The interpolator, or null on error. + * See Interpolator::newFromName() for the new thing. */ public static function newInterpolator(string $name) { - Utils::debugLog('newInterpolator', [ - 'instance' => null, - 'arguments' => [$name] - ]); - - // added in 1.0.7 of the binary module - return vips_interpolate_new($name); + return Interpolate::newFromName($name); } /** @@ -1108,12 +896,7 @@ public static function newInterpolator(string $name) */ public function newFromImage($value): Image { - Utils::debugLog('newFromImage', [ - 'instance' => $this, - 'arguments' => [$value] - ]); - - $pixel = self::black(1, 1)->add($value)->cast($this->format); + $pixel = static::black(1, 1)->add($value)->cast($this->format); $image = $pixel->embed( 0, 0, @@ -1129,8 +912,6 @@ public function newFromImage($value): Image 'yoffset' => $this->yoffset ]); - Utils::debugLog('newFromImage', ['result' => $image]); - return $image; } @@ -1145,17 +926,26 @@ public function newFromImage($value): Image * * @return void */ - public function writeToFile(string $filename, array $options = []): void + public function writeToFile(string $name, array $options = []): void { - Utils::debugLog('writeToFile', [ - 'instance' => $this, - 'arguments' => [$filename, $options] - ]); + $filename = Config::filenameGetFilename($name); + $string_options = Config::filenameGetOptions($name); + + $saver = Config::ffi()->vips_foreign_find_save($filename); + if ($saver == "") { + Config::error(); + } + + if (strlen($string_options) != 0) { + $options = array_merge([ + "string_options" => $string_options, + ], $options); + } + + $result = VipsOperation::call($saver, $this, [$filename], $options); - $options = self::unwrap($options); - $result = vips_image_write_to_file($this->image, $filename, $options); if ($result === -1) { - self::errorVips(); + Config::error(); } } @@ -1172,19 +962,21 @@ public function writeToFile(string $filename, array $options = []): void */ public function writeToBuffer(string $suffix, array $options = []): string { - Utils::debugLog('writeToBuffer', [ - 'instance' => $this, - 'arguments' => [$suffix, $options] - ]); + $filename = Config::filenameGetFilename($suffix); + $string_options = Config::filenameGetOptions($suffix); - $options = self::unwrap($options); - $result = vips_image_write_to_buffer($this->image, $suffix, $options); - if ($result === -1) { - self::errorVips(); + $saver = Config::ffi()->vips_foreign_find_save_buffer($filename); + if ($saver == "") { + Config::error(); + } + + if (strlen($string_options) != 0) { + $options = array_merge([ + "string_options" => $string_options, + ], $options); } - $result = self::wrapResult($result); - Utils::debugLog('writeToBuffer', ['result' => $result]); + $result = VipsOperation::call($saver, $this, [], $options); return $result; } @@ -1198,17 +990,19 @@ public function writeToBuffer(string $suffix, array $options = []): string */ public function writeToMemory(): string { - Utils::debugLog('writeToMemory', [ - 'instance' => $this, - 'arguments' => [] - ]); + $ctype = \FFI::arrayType(\FFI::type("size_t"), [1]); + $p_size = \FFI::new($ctype); - $result = vips_image_write_to_memory($this->image); - if ($result === -1) { - self::errorVips(); + $pointer = Config::ffi()-> + vips_image_write_to_memory($this->pointer, $p_size); + if ($pointer == null) { + Config::error(); } - Utils::debugLog('writeToMemory', ['result' => $result]); + // string() takes a copy + $result = \FFI::string($pointer, $p_size[0]); + + Config::ffi()->g_free($pointer); return $result; } @@ -1238,17 +1032,29 @@ public function writeToMemory(): string */ public function writeToArray(): array { - Utils::debugLog('writeToArray', [ - 'instance' => $this, - 'arguments' => [] - ]); + $ctype = \FFI::arrayType(\FFI::type("size_t"), [1]); + $p_size = \FFI::new($ctype); - $result = vips_image_write_to_array($this->image); - if ($result === -1) { - self::errorVips(); + $pointer = Config::ffi()-> + vips_image_write_to_memory($this->pointer, $p_size); + if ($pointer == null) { + Config::error(); } - Utils::debugLog('writeToArray', ['result' => $result]); + // wrap pointer up as a C array of the right type + $n = $this->width * $this->height * $this->bands; + $type_name = Config::ftypes($this->format); + $ctype = \FFI::arrayType(\FFI::type($type_name), [$n]); + $array = \FFI::cast($ctype, $pointer); + + // copy to PHP memory as a flat array + $result = []; + for ($i = 0; $i < $n; $i++) { + $result[] = $array[$i]; + } + + // the vips result is not PHP memory, so we must free it + Config::ffi()->g_free($pointer); return $result; } @@ -1269,18 +1075,11 @@ public function writeToArray(): array */ public function copyMemory(): Image { - Utils::debugLog('copyMemory', [ - 'instance' => $this, - 'arguments' => [] - ]); - - $result = vips_image_copy_memory($this->image); - if ($result === -1) { - self::errorVips(); + $pointer = Config::ffi()->vips_image_copy_memory($this->pointer); + if ($pointer == null) { + Config::error(); } - $result = self::wrapResult($result); - - Utils::debugLog('copyMemory', ['result' => $result]); + $result = new Image($pointer); return $result; } @@ -1296,9 +1095,7 @@ public function copyMemory(): Image */ public function __get(string $name) { - $result = vips_image_get($this->image, $name); - self::errorIsArray($result); - return self::wrapResult($result); + return $this->get($name); } /** @@ -1311,7 +1108,7 @@ public function __get(string $name) */ public function __set(string $name, $value): void { - vips_image_set($this->image, $name, $value); + $this->set($name, $value); } /** @@ -1323,7 +1120,7 @@ public function __set(string $name, $value): void */ public function __isset(string $name): bool { - return $this->typeof($name) !== 0; + return $this->getType($name) != 0; } /** @@ -1332,7 +1129,7 @@ public function __isset(string $name): bool * This is handy for fields whose name * does not match PHP's variable naming conventions, like `'exif-data'`. * - * It will throw an exception if $name does not exist. Use Image::typeof() + * It will throw an exception if $name does not exist. Use Image::getType() * to test for the existence of a field. * * @param string $name The property name. @@ -1343,9 +1140,13 @@ public function __isset(string $name): bool */ public function get(string $name) { - $result = vips_image_get($this->image, $name); - self::errorIsArray($result); - return self::wrapResult($result); + $gvalue = new GValue(); + if (Config::ffi()-> + vips_image_get($this->pointer, $name, $gvalue->pointer) != 0) { + Config::error(); + } + + return $gvalue->get(); } /** @@ -1357,9 +1158,21 @@ public function get(string $name) * * @return integer */ - public function typeof(string $name): int + public function getType(string $name): int { - return vips_image_get_typeof($this->image, $name); + return Config::ffi()->vips_image_get_typeof($this->pointer, $name); + } + + /** + * A deprecated synonym for getType(). + * + * @param string $name The property name. + * + * @return integer + */ + public function typeOf(string $name): int + { + return $this->getType($name); } /** @@ -1377,10 +1190,35 @@ public function typeof(string $name): int */ public function set(string $name, $value): void { - $result = vips_image_set($this->image, $name, $value); - if ($result === -1) { - self::errorVips(); + $gvalue = new GValue(); + $gtype = $this->getType($name); + + /* If this is not a known field, guess a sensible type from the value. + */ + if ($gtype == 0) { + if (is_array($value)) { + if (is_int($value[0])) { + $gtype = Config::gtypes("VipsArrayInt"); + } elseif (is_float($value[0])) { + $gtype = Config::gtypes("VipsArrayDouble"); + } else { + $gtype = Config::gtypes("VipsArrayImage"); + } + } elseif (is_int($value)) { + $gtype = Config::gtypes("gint"); + } elseif (is_float($value)) { + $gtype = Config::gtypes("gdouble"); + } elseif (is_string($value)) { + $gtype = Config::gtypes("VipsRefString"); + } else { + $gtype = Config::gtypes("VipsImage"); + } } + + $gvalue->setType($gtype); + $gvalue->set($value); + + Config::ffi()->vips_image_set($this->pointer, $name, $gvalue->pointer); } /** @@ -1402,10 +1240,10 @@ public function set(string $name, $value): void */ public function setType($type, string $name, $value): void { - $result = vips_image_set_type($this->image, $type, $name, $value); - if ($result === -1) { - self::errorVips(); - } + $gvalue = new GValue(); + $gvalue->setType($type); + $gvalue->set($value); + Config::ffi()->vips_image_set($this->pointer, $name, $gvalue->pointer); } /** @@ -1419,9 +1257,8 @@ public function setType($type, string $name, $value): void */ public function remove(string $name): void { - $result = vips_image_remove($this->image, $name); - if ($result === -1) { - self::errorVips(); + if (!Config::ffi()->vips_image_remove($this->pointer, $name)) { + Config::error(); } } @@ -1443,110 +1280,6 @@ public function __toString() return json_encode($array); } - /** - * Call any vips operation. The final element of $arguments can be - * (but doesn't have to be) an array of options to pass to the operation. - * - * We can't have a separate arg for the options since this will be run from - * __call(), which cannot know which args are required and which are - * optional. See call() below for a version with the options broken out. - * - * @param string $name The operation name. - * @param Image|null $instance The instance this operation is being invoked - * from. - * @param array $arguments An array of arguments to pass to the - * operation. - * - * @throws Exception - * - * @return mixed The result(s) of the operation. - */ - public static function callBase( - string $name, - ?Image $instance, - array $arguments - ) { - Utils::debugLog($name, [ - 'instance' => $instance, - 'arguments' => $arguments - ]); - - $arguments = array_merge([$name, $instance], $arguments); - - $arguments = array_values(self::unwrap($arguments)); - $result = vips_call(...$arguments); - self::errorIsArray($result); - $result = self::wrapResult($result); - - Utils::debugLog($name, ['result' => $result]); - - return $result; - } - - /** - * Call any vips operation, with an explicit set of options. This is more - * convenient than callBase() if you have a set of known options. - * - * @param string $name The operation name. - * @param Image|null $instance The instance this operation is being invoked - * from. - * @param array $arguments An array of arguments to pass to the - * operation. - * @param array $options An array of optional arguments to pass to - * the operation. - * - * @throws Exception - * - * @return mixed The result(s) of the operation. - */ - public static function call( - string $name, - ?Image $instance, - array $arguments, - array $options = [] - ) { - /* - echo "call: $name \n"; - echo "instance = \n"; - var_dump($instance); - echo "arguments = \n"; - var_dump($arguments); - echo "options = \n"; - var_dump($options); - */ - - return self::callBase($name, $instance, array_merge($arguments, [$options])); - } - - /** - * Handy for things like self::more. Call a 2-ary vips operator like - * 'more', but if the arg is not an image (ie. it's a constant), call - * 'more_const' instead. - * - * @param mixed $other The right-hand argument. - * @param string $base The base part of the operation name. - * @param string $op The action to invoke. - * @param array $options An array of options to pass to the operation. - * - * @throws Exception - * - * @return mixed The operation result. - * - * @internal - */ - private function callEnum( - $other, - string $base, - string $op, - array $options = [] - ) { - if (self::isImageish($other)) { - return self::call($base, $this, [$other, $op], $options); - } else { - return self::call($base . '_const', $this, [$op, $other], $options); - } - } - /** * Call any vips operation as an instance method. * @@ -1559,7 +1292,7 @@ private function callEnum( */ public function __call(string $name, array $arguments) { - return self::callBase($name, $this, $arguments); + return VipsOperation::callBase($name, $this, $arguments); } /** @@ -1574,7 +1307,7 @@ public function __call(string $name, array $arguments) */ public static function __callStatic(string $name, array $arguments) { - return self::callBase($name, null, $arguments); + return VipsOperation::callBase($name, null, $arguments); } /** @@ -1616,7 +1349,8 @@ public function offsetExists($offset): bool */ public function offsetGet($offset): ?Image { - return $this->offsetExists($offset) ? $this->extract_band($offset) : null; + return $this->offsetExists($offset) ? + $this->extract_band($offset) : null; } /** @@ -1651,7 +1385,8 @@ public function offsetSet($offset, $value): void } if (!is_int($offset)) { - throw new \BadMethodCallException('Image::offsetSet: offset is not integer or null'); + throw new \BadMethodCallException('Image::offsetSet: ' . + 'offset is not integer or null'); } // number of bands to the left and right of $value @@ -1662,7 +1397,7 @@ public function offsetSet($offset, $value): void // if we are setting a constant as the first element, we must expand it // to an image, since bandjoin must have an image as the first argument if ($n_left === 0 && !($value instanceof Image)) { - $value = self::imageize($this, $value); + $value = $this->imageize($value); } $components = []; @@ -1675,7 +1410,14 @@ public function offsetSet($offset, $value): void } $head = array_shift($components); - $this->image = $head->bandjoin($components)->image; + $joined = $head->bandjoin($components); + + /* Overwrite our pointer with the pointer from the new, joined object. + * We have to adjust the refs, yuk! + */ + $joined->ref(); + $this->unref(); + $this->pointer = $joined->pointer; } /** @@ -1693,7 +1435,8 @@ public function offsetUnset($offset): void { if (is_int($offset) && $offset >= 0 && $offset < $this->bands) { if ($this->bands === 1) { - throw new \BadMethodCallException('Image::offsetUnset: cannot delete final band'); + throw new \BadMethodCallException('Image::offsetUnset: ' . + 'cannot delete final band'); } $components = []; @@ -1709,9 +1452,14 @@ public function offsetUnset($offset): void $head = array_shift($components); if (empty($components)) { - $this->image = $head->image; + $head->ref(); + $this->unref(); + $this->pointer = $head->pointer; } else { - $this->image = $head->bandjoin($components)->image; + $new_image = $head->bandjoin($components); + $new_image->ref(); + $this->unref(); + $this->pointer = $new_image->pointer; } } } @@ -1729,7 +1477,7 @@ public function offsetUnset($offset): void public function add($other, array $options = []): Image { if (self::isImageish($other)) { - return self::call('add', $this, [$other], $options); + return VipsOperation::call('add', $this, [$other], $options); } else { return $this->linear(1, $other, $options); } @@ -1748,7 +1496,7 @@ public function add($other, array $options = []): Image public function subtract($other, array $options = []): Image { if (self::isImageish($other)) { - return self::call('subtract', $this, [$other], $options); + return VipsOperation::call('subtract', $this, [$other], $options); } else { $other = self::mapNumeric($other, function ($value) { return -1 * $value; @@ -1770,7 +1518,7 @@ public function subtract($other, array $options = []): Image public function multiply($other, array $options = []): Image { if (self::isImageish($other)) { - return self::call('multiply', $this, [$other], $options); + return VipsOperation::call('multiply', $this, [$other], $options); } else { return $this->linear($other, 0, $options); } @@ -1789,7 +1537,7 @@ public function multiply($other, array $options = []): Image public function divide($other, array $options = []): Image { if (self::isImageish($other)) { - return self::call('divide', $this, [$other], $options); + return VipsOperation::call('divide', $this, [$other], $options); } else { $other = self::mapNumeric($other, function ($value) { return $value ** -1; @@ -1811,9 +1559,14 @@ public function divide($other, array $options = []): Image public function remainder($other, array $options = []): Image { if (self::isImageish($other)) { - return self::call('remainder', $this, [$other], $options); + return VipsOperation::call('remainder', $this, [$other], $options); } else { - return self::call('remainder_const', $this, [$other], $options); + return VipsOperation::call( + 'remainder_const', + $this, + [$other], + $options + ); } } @@ -1829,7 +1582,7 @@ public function remainder($other, array $options = []): Image */ public function pow($other, array $options = []): Image { - return $this->callEnum($other, 'math2', OperationMath2::POW, $options); + return self::callEnum($other, 'math2', OperationMath2::POW, $options); } /** @@ -1844,7 +1597,7 @@ public function pow($other, array $options = []): Image */ public function wop($other, array $options = []): Image { - return $this->callEnum($other, 'math2', OperationMath2::WOP, $options); + return self::callEnum($other, 'math2', OperationMath2::WOP, $options); } /** @@ -1859,7 +1612,7 @@ public function wop($other, array $options = []): Image */ public function lshift($other, array $options = []): Image { - return $this->callEnum($other, 'boolean', OperationBoolean::LSHIFT, $options); + return self::callEnum($other, 'boolean', OperationBoolean::LSHIFT, $options); } /** @@ -1874,7 +1627,12 @@ public function lshift($other, array $options = []): Image */ public function rshift($other, array $options = []): Image { - return $this->callEnum($other, 'boolean', OperationBoolean::RSHIFT, $options); + return self::callEnum( + $other, + 'boolean', + OperationBoolean::RSHIFT, + $options + ); } /** @@ -1891,7 +1649,7 @@ public function rshift($other, array $options = []): Image public function andimage($other, array $options = []): Image { // phpdoc hates OperationBoolean::AND, so use the string form here - return $this->callEnum($other, 'boolean', 'and', $options); + return self::callEnum($other, 'boolean', 'and', $options); } /** @@ -1907,7 +1665,7 @@ public function andimage($other, array $options = []): Image public function orimage($other, array $options = []): Image { // phpdoc hates OperationBoolean::OR, so use the string form here - return $this->callEnum($other, 'boolean', 'or', $options); + return self::callEnum($other, 'boolean', 'or', $options); } /** @@ -1922,7 +1680,12 @@ public function orimage($other, array $options = []): Image */ public function eorimage($other, array $options = []): Image { - return $this->callEnum($other, 'boolean', OperationBoolean::EOR, $options); + return self::callEnum( + $other, + 'boolean', + OperationBoolean::EOR, + $options + ); } /** @@ -1937,7 +1700,12 @@ public function eorimage($other, array $options = []): Image */ public function more($other, array $options = []): Image { - return $this->callEnum($other, 'relational', OperationRelational::MORE, $options); + return self::callEnum( + $other, + 'relational', + OperationRelational::MORE, + $options + ); } /** @@ -1952,7 +1720,12 @@ public function more($other, array $options = []): Image */ public function moreEq($other, array $options = []): Image { - return $this->callEnum($other, 'relational', OperationRelational::MOREEQ, $options); + return self::callEnum( + $other, + 'relational', + OperationRelational::MOREEQ, + $options + ); } /** @@ -1967,7 +1740,12 @@ public function moreEq($other, array $options = []): Image */ public function less($other, array $options = []): Image { - return $this->callEnum($other, 'relational', OperationRelational::LESS, $options); + return self::callEnum( + $other, + 'relational', + OperationRelational::LESS, + $options + ); } /** @@ -1982,7 +1760,12 @@ public function less($other, array $options = []): Image */ public function lessEq($other, array $options = []): Image { - return $this->callEnum($other, 'relational', OperationRelational::LESSEQ, $options); + return self::callEnum( + $other, + 'relational', + OperationRelational::LESSEQ, + $options + ); } /** @@ -1997,7 +1780,12 @@ public function lessEq($other, array $options = []): Image */ public function equal($other, array $options = []): Image { - return $this->callEnum($other, 'relational', OperationRelational::EQUAL, $options); + return self::callEnum( + $other, + 'relational', + OperationRelational::EQUAL, + $options + ); } /** @@ -2012,7 +1800,12 @@ public function equal($other, array $options = []): Image */ public function notEq($other, array $options = []): Image { - return $this->callEnum($other, 'relational', OperationRelational::NOTEQ, $options); + return self::callEnum( + $other, + 'relational', + OperationRelational::NOTEQ, + $options + ); } /** @@ -2048,9 +1841,14 @@ public function bandjoin($other, array $options = []): Image /* We can't use self::bandjoin(), that would just recurse. */ if ($is_const) { - return self::call('bandjoin_const', $this, [$other], $options); + return VipsOperation::call( + 'bandjoin_const', + $this, + [$other], + $options + ); } else { - return self::call( + return VipsOperation::call( 'bandjoin', null, [array_merge([$this], $other)], @@ -2107,7 +1905,7 @@ public function bandrank($other, array $options = []): Image $other = (array) $other; } - return self::call('bandrank', $this, $other, $options); + return VipsOperation::call('bandrank', $this, $other, $options); } /** @@ -2130,16 +1928,18 @@ public function composite($other, $mode, array $options = []): Image } else { $other = (array) $other; } + if (!is_array($mode)) { $mode = [$mode]; } + # composite takes an arrayint, but it's really an array of blend modes + # gvalue doesn't know this, so we must do name -> enum value mapping $mode = array_map(function ($x) { - // Use BlendMode::OVER if a non-existent value is given. - return self::$blendModeToInt[$x] ?? BlendMode::OVER; + return GValue::toEnum(Config::gtypes("VipsBlendMode"), $x); }, $mode); - return self::call( + return VipsOperation::call( 'composite', null, [array_merge([$this], $other), $mode], @@ -2199,7 +1999,6 @@ public function ifthenelse($then, $else, array $options = []): Image * match each other first, and only if they are both constants do we * match to $this. */ - $match_image = null; foreach ([$then, $else, $this] as $item) { if ($item instanceof Image) { @@ -2209,14 +2008,19 @@ public function ifthenelse($then, $else, array $options = []): Image } if (!($then instanceof Image)) { - $then = self::imageize($match_image, $then); + $then = $match_image->imageize($then); } if (!($else instanceof Image)) { - $else = self::imageize($match_image, $else); + $else = $match_image->imageize($else); } - return self::call('ifthenelse', $this, [$then, $else], $options); + return VipsOperation::call( + 'ifthenelse', + $this, + [$then, $else], + $options + ); } /** diff --git a/src/ImageAutodoc.php b/src/ImageAutodoc.php index e917baf..f9537ec 100644 --- a/src/ImageAutodoc.php +++ b/src/ImageAutodoc.php @@ -697,6 +697,6 @@ * @property float $yres Vertical resolution in pixels/mm * @property string $filename Image filename */ -abstract class ImageAutodoc +abstract class ImageAutodoc extends VipsObject { } diff --git a/src/Interpolate.php b/src/Interpolate.php new file mode 100644 index 0000000..c70b327 --- /dev/null +++ b/src/Interpolate.php @@ -0,0 +1,104 @@ + + * @copyright 2016 John Cupitt + * @license https://opensource.org/licenses/MIT MIT + * @link https://github.com/libvips/php-vips + */ + +namespace Jcupitt\Vips; + +/** + * This class holds a pointer to a VipsInterpolate (the libvips + * base class for interpolators) and manages argument introspection and + * operation call. + * + * @category Images + * @package Jcupitt\Vips + * @author John Cupitt + * @copyright 2016 John Cupitt + * @license https://opensource.org/licenses/MIT MIT + * @link https://github.com/libvips/php-vips + */ +class Interpolate extends VipsObject +{ + /** + * A pointer to the underlying Interpolate. This is the same as the + * GObject, just cast to Interpolate to help FFI. + * + * @internal + */ + public \FFI\CData $pointer; + + public function __construct($pointer) + { + $this->pointer = Config::ffi()-> + cast(Config::ctypes("VipsInterpolate"), $pointer); + + parent::__construct($pointer); + } + + /** + * Make an interpolator from a name. + * + * @param string $name Name of the interpolator. + * + * Possible interpolators are: + * - `'nearest'`: Use nearest neighbour interpolation. + * - `'bicubic'`: Use bicubic interpolation. + * - `'bilinear'`: Use bilinear interpolation (the default). + * - `'nohalo'`: Use Nohalo interpolation. + * - `'lbb'`: Use LBB interpolation. + * - `'vsqbs'`: Use the VSQBS interpolation. + * + * @return resource|null The interpolator, or null on error. + */ + public static function newFromName($name) + { + $pointer = Config::ffi()->vips_interpolate_new($name); + if ($pointer == null) { + Config::error(); + } + + return new Interpolate($pointer); + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: expandtab sw=4 ts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 + */ diff --git a/src/Introspect.php b/src/Introspect.php new file mode 100644 index 0000000..b4aca43 --- /dev/null +++ b/src/Introspect.php @@ -0,0 +1,252 @@ + + * @copyright 2016 John Cupitt + * @license https://opensource.org/licenses/MIT MIT + * @link https://github.com/libvips/php-vips + */ + +namespace Jcupitt\Vips; + +/** + * Introspect a VipsOperation and discover everything we can. This is called + * on demand once per operation and the results held in a cache. + * + * @category Images + * @package Jcupitt\Vips + * @author John Cupitt + * @copyright 2016 John Cupitt + * @license https://opensource.org/licenses/MIT MIT + * @link https://github.com/libvips/php-vips + */ +class Introspect +{ + /** + * The operation nickname (eg. "add"). + */ + public string $name; + + /** + * The operation description (eg. "add two images"). + */ + public string $description; + + /** + * The operation flags (eg. SEQUENTIAL | DEPRECATED). + */ + public int $flags; + + /** + * A hash from arg name to a hash of details. + */ + public array $arguments; + + /** + * Arrays of arg names, in order and by category, eg. $this->required_input + * = ["filename"]. + */ + public array $required_input; + public array $optional_input; + public array $required_output; + public array $optional_output; + + /** + * The name of the arg this operation uses as "this". + */ + public string $member_this; + + /** + * And the required input args, without the "this". + */ + public array $method_args; + + public function __construct($name) + { + $this->name = $name; + + $operation = VipsOperation::newFromName($name); + + $this->description = $operation->getDescription(); + $flags = Config::ffi()->vips_operation_get_flags($operation->pointer); + + $p_names = Config::ffi()->new("char**[1]"); + $p_flags = Config::ffi()->new("int*[1]"); + $p_n_args = Config::ffi()->new("int[1]"); + $result = Config::ffi()->vips_object_get_args( + \FFI::cast(Config::ctypes("VipsObject"), $operation->pointer), + $p_names, + $p_flags, + $p_n_args + ); + if ($result != 0) { + error(); + } + $p_names = $p_names[0]; + $p_flags = $p_flags[0]; + $n_args = $p_n_args[0]; + + # make a hash from arg name to flags + $argumentFlags = []; + for ($i = 0; $i < $n_args; $i++) { + if (($p_flags[$i] & ArgumentFlags::CONSTRUCT) != 0) { + # libvips uses '-' to separate parts of arg names, but we + # need '_' for php + $name = \FFI::string($p_names[$i]); + $name = str_replace("-", "_", $name); + $argumentFlags[$name] = $p_flags[$i]; + } + } + + # make a hash from arg name to detailed arg info + $this->arguments = []; + foreach ($argumentFlags as $name => $flags) { + $this->arguments[$name] = [ + "name" => $name, + "flags" => $flags, + "blurb" => $operation->getBlurb($name), + "type" => $operation->getType($name) + ]; + } + + # split args into categories + $this->required_input = []; + $this->optional_input = []; + $this->required_output = []; + $this->optional_output = []; + + foreach ($this->arguments as $name => $details) { + $flags = $details["flags"]; + $blurb = $details["blurb"]; + $type = $details["type"]; + $typeName = Config::ffi()->g_type_name($type); + + if (($flags & ArgumentFlags::INPUT) && + ($flags & ArgumentFlags::REQUIRED) && + !($flags & ArgumentFlags::DEPRECATED)) { + $this->required_input[] = $name; + + # required inputs which we MODIFY are also required outputs + if ($flags & ArgumentFlags::MODIFY) { + $this->required_output[] = $name; + } + } + + if (($flags & ArgumentFlags::OUTPUT) && + ($flags & ArgumentFlags::REQUIRED) && + !($flags & ArgumentFlags::DEPRECATED)) { + $this->required_output[] = $name; + } + + # we let deprecated optional args through, but warn about them + # if they get used, see below + if (($flags & ArgumentFlags::INPUT) && + !($flags & ArgumentFlags::REQUIRED)) { + $this->optional_input[] = $name; + } + + if (($flags & ArgumentFlags::OUTPUT) && + !($flags & ArgumentFlags::REQUIRED)) { + $this->optional_output[] = $name; + } + } + + # find the first required input image arg, if any ... that will be self + $this->member_this = ""; + foreach ($this->required_input as $name) { + $type = $this->arguments[$name]["type"]; + if ($type == Config::gtypes("VipsImage")) { + $this->member_this = $name; + break; + } + } + + # method args are required args, but without the image they are a + # method on + $this->method_args = $this->required_input; + if ($this->member_this != "") { + $index = array_search($this->member_this, $this->method_args); + array_splice($this->method_args, $index); + } + + Utils::debugLog($name, ['introspect' => strval($this)]); + } + + public function __toString() + { + $result = ""; + + $result .= "$this->name:\n"; + + foreach ($this->arguments as $name => $details) { + $flags = $details["flags"]; + $blurb = $details["blurb"]; + $type = $details["type"]; + $typeName = Config::ffi()->g_type_name($type); + + $result .= " $name:\n"; + + $result .= " flags: $flags\n"; + foreach (ArgumentFlags::NAMES as $name => $flag) { + if ($flags & $flag) { + $result .= " $name\n"; + } + } + + $result .= " blurb: $blurb\n"; + $result .= " type: $typeName\n"; + } + + $info = implode(", ", $this->required_input); + $result .= "required input: $info\n"; + $info = implode(", ", $this->required_output); + $result .= "required output: $info\n"; + $info = implode(", ", $this->optional_input); + $result .= "optional input: $info\n"; + $info = implode(", ", $this->optional_output); + $result .= "optional output: $info\n"; + $result .= "member_this: $this->member_this\n"; + $info = implode(", ", $this->method_args); + $result .= "method args: $info\n"; + + return $result; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: expandtab sw=4 ts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 + */ diff --git a/src/Utils.php b/src/Utils.php index 2f4fe53..d5ee068 100644 --- a/src/Utils.php +++ b/src/Utils.php @@ -92,7 +92,7 @@ public static function errorLog(string $message, \Exception $exception): void */ public static function typeFromName(string $name): int { - return vips_type_from_name($name); + return Config::ffi()->g_type_from_name($name); } } diff --git a/src/VipsObject.php b/src/VipsObject.php new file mode 100644 index 0000000..2a027cf --- /dev/null +++ b/src/VipsObject.php @@ -0,0 +1,187 @@ + + * @copyright 2016 John Cupitt + * @license https://opensource.org/licenses/MIT MIT + * @link https://github.com/libvips/php-vips + */ + +namespace Jcupitt\Vips; + +/** + * This class holds a pointer to a VipsObject (the libvips base class) and + * manages properties. + * + * @category Images + * @package Jcupitt\Vips + * @author John Cupitt + * @copyright 2016 John Cupitt + * @license https://opensource.org/licenses/MIT MIT + * @link https://github.com/libvips/php-vips + */ +abstract class VipsObject extends GObject +{ + /** + * A pointer to the underlying VipsObject. + * + * @internal + */ + private \FFI\CData $pointer; + + /** + * A pointer to the underlying GObject. This is the same as the + * VipsObject, just cast. + * + * @internal + */ + private \FFI\CData $gObject; + + public function __construct($pointer) + { + $this->pointer = Config::ffi()-> + cast(Config::ctypes("VipsObject"), $pointer); + $this->gObject = Config::ffi()-> + cast(Config::ctypes("GObject"), $pointer); + + parent::__construct($pointer); + } + + // print a table of all active vipsobjects ... handy for debugging + public static function printAll() + { + Config::ffi()->vips_object_print_all(); + } + + public function getDescription() + { + return Config::ffi()->vips_object_get_description($this->pointer); + } + + // get the pspec for a property + // NULL for no such name + // very slow! avoid if possible + // FIXME add a cache for this thing + public function getPspec(string $name) + { + $pspec = Config::ffi()->new("GParamSpec*[1]"); + $argument_class = Config::ffi()->new("VipsArgumentClass*[1]"); + $argument_instance = Config::ffi()->new("VipsArgumentInstance*[1]"); + $result = Config::ffi()->vips_object_get_argument( + $this->pointer, + $name, + $pspec, + $argument_class, + $argument_instance + ); + + if ($result != 0) { + return null; + } else { + return $pspec[0]; + } + } + + // get the type of a property from a VipsObject + // 0 if no such property + public function getType(string $name) + { + $pspec = $this->getPspec($name); + if (\FFI::isNull($pspec)) { + # need to clear any error, this is horrible + Config::ffi()->vips_error_clear(); + return 0; + } else { + return $pspec->value_type; + } + } + + public function getBlurb(string $name): string + { + $pspec = $this->getPspec($name); + return Config::ffi()->g_param_spec_get_blurb($pspec); + } + + public function getArgumentDescription(string $name): string + { + $pspec = $this->getPspec($name); + return Config::ffi()->g_param_spec_get_description($pspec); + } + + public function get(string $name) + { + $gvalue = new GValue(); + $gvalue->setType($this->getType($name)); + + Config::ffi()-> + g_object_get_property($this->gObject, $name, $gvalue->pointer); + $value = $gvalue->get(); + + Utils::debugLog("get", [$name => var_export($value, true)]); + + return $value; + } + + public function set(string $name, $value) + { + Utils::debugLog("set", [$name => $value]); + + $gvalue = new GValue(); + $gvalue->setType($this->getType($name)); + $gvalue->set($value); + + Config::ffi()-> + g_object_set_property($this->gObject, $name, $gvalue->pointer); + } + + public function setString(string $string_options) + { + $result = Config::ffi()-> + vips_object_set_from_string($this->pointer, $string_options); + + return $result == 0; + } + + public function unrefOutputs() + { + Config::ffi()->vips_object_unref_outputs($this->pointer); + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: expandtab sw=4 ts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 + */ diff --git a/src/VipsOperation.php b/src/VipsOperation.php new file mode 100644 index 0000000..606792d --- /dev/null +++ b/src/VipsOperation.php @@ -0,0 +1,413 @@ + + * @copyright 2016 John Cupitt + * @license https://opensource.org/licenses/MIT MIT + * @link https://github.com/libvips/php-vips + */ + +namespace Jcupitt\Vips; + +/** + * This class holds a pointer to a VipsOperation (the libvips operation base + * class) and manages argument introspection and operation call. + * + * @category Images + * @package Jcupitt\Vips + * @author John Cupitt + * @copyright 2016 John Cupitt + * @license https://opensource.org/licenses/MIT MIT + * @link https://github.com/libvips/php-vips + */ +class VipsOperation extends VipsObject +{ + /** + * A pointer to the underlying VipsOperation. This is the same as the + * GObject, just cast to VipsOperation to help FFI. + * + * @internal + */ + public \FFI\CData $pointer; + + /** + * Introspection data for this operation. + */ + public Introspect $introspect; + + public function __construct($pointer) + { + $this->pointer = Config::ffi()-> + cast(Config::ctypes("VipsOperation"), $pointer); + + parent::__construct($pointer); + } + + public static function newFromName($name) + { + $pointer = Config::ffi()->vips_operation_new($name); + if ($pointer == null) { + Config::error(); + } + + return new VipsOperation($pointer); + } + + public function setMatch($name, $match_image, $value) + { + $flags = $this->introspect->arguments[$name]["flags"]; + $gtype = $this->introspect->arguments[$name]["type"]; + + if ($match_image != null) { + if ($gtype == Config::gtypes("VipsImage")) { + $value = $match_image->imageize($value); + } elseif ($gtype == Config::gtypes("VipsArrayImage") && + is_array($value)) { + $new_value = []; + foreach ($value as $x) { + $new_value[] = $match_image->imageize($x); + } + $value = $new_value; + } + } + + # MODIFY args need to be copied before they are set + if (($flags & ArgumentFlags::MODIFY) != 0) { + # logger.debug('copying MODIFY arg %s', name) + # make sure we have a unique copy + $value = $value->copyMemory(); + } + + parent::set($name, $value); + } + + private static function introspect($name): Introspect + { + static $cache = []; + + if (!array_key_exists($name, $cache)) { + $cache[$name] = new Introspect($name); + } + + return $cache[$name]; + } + + private static function findInside($predicate, $x) + { + if ($predicate($x)) { + return $x; + } + + if (is_array($x)) { + foreach ($x as $y) { + $result = self::findInside($predicate, $y); + + if ($result != null) { + return $result; + } + } + } + + return null; + } + + /** + * Unwrap an array of stuff ready to pass down to the vips_ layer. We + * swap instances of Image for the ffi pointer. + * + * @param array $result Unwrap this. + * + * @return array $result unwrapped, ready for vips. + * + * @internal + */ + private static function unwrap(array $result): array + { + array_walk_recursive($result, function (&$value) { + if ($value instanceof Image) { + $value = $value->image; + } + }); + + return $result; + } + + /** + * Is $value a VipsImage. + * + * @param mixed $value The thing to test. + * + * @return bool true if this is a ffi VipsImage*. + * + * @internal + */ + private static function isImagePointer($value): bool + { + return $value instanceof \FFI\CData && + \FFI::typeof($value) == Config::ctypes("VipsImage"); + } + + /** + * Wrap up the result of a vips_ call ready to return it to PHP. We do + * two things: + * + * - If the array is a singleton, we strip it off. For example, many + * operations return a single result and there's no sense handling + * this as an array of values, so we transform ['out' => x] -> x. + * + * - Any VipsImage resources are rewrapped as instances of Image. + * + * @param mixed $result Wrap this up. + * + * @return mixed $result, but wrapped up as a php class. + * + * @internal + */ + private static function wrapResult($result) + { + if (!is_array($result)) { + $result = ['x' => $result]; + } + + array_walk_recursive($result, function (&$item) { + if (self::isImagePointer($item)) { + $item = new Image($item); + } + }); + + if (count($result) === 1) { + $result = array_shift($result); + } + + return $result; + } + + /** + * Check the result of a vips_ call for an error, and throw an exception + * if we see one. + * + * This won't work for things like __get where a non-array return can be + * a valid return. + * + * @param mixed $result Test this. + * + * @throws Exception + * + * @return void + * + * @internal + */ + private static function errorIsArray($result): void + { + if (!is_array($result)) { + Config::error(); + } + } + + /** + * Call any vips operation. The final element of $arguments can be + * (but doesn't have to be) an array of options to pass to the operation. + * + * We can't have a separate arg for the options since this will be run from + * __call(), which cannot know which args are required and which are + * optional. See call() below for a version with the options broken out. + * + * @param string $operation_name The operation name. + * @param Image|null $instance The instance this operation is being invoked + * from. + * @param array $arguments An array of arguments to pass to the + * operation. + * + * @throws Exception + * + * @return mixed The result(s) of the operation. + */ + public static function callBase( + string $operation_name, + ?Image $instance, + array $arguments + ) { + Utils::debugLog($operation_name, [ + 'instance' => $instance, + 'arguments' => $arguments + ]); + + $operation = self::newFromName($operation_name); + $operation->introspect = self::introspect($operation_name); + + /* the first image argument is the thing we expand constants to + * match ... look inside tables for images, since we may be passing + * an array of images as a single param. + */ + if ($instance != null) { + $match_image = $instance; + } else { + $match_image = self::findInside( + fn($x) => $x instanceof Image, + $arguments + ); + } + + /* Because of the way php callStatic works, we can sometimes be given + * an instance even when no instance was given. + * + * We must loop over the required args and set them from the supplied + * args, using instance if required, and only check the nargs after + * this pass. + */ + $n_required = count($operation->introspect->required_input); + $n_supplied = count($arguments); + $used_instance = false; + $n_used = 0; + foreach ($operation->introspect->required_input as $name) { + if ($name == $operation->introspect->member_this) { + if (!$instance) { + $operation->unrefOutputs(); + Config::error("instance argument not supplied"); + } + $operation->setMatch($name, $match_image, $instance); + $used_instance = true; + } elseif ($n_used < $n_supplied) { + $operation->setMatch($name, $match_image, $arguments[$n_used]); + $n_used += 1; + } else { + $operation->unrefOutputs(); + Config::error("$n_required arguments required, " . + "but $n_supplied supplied"); + } + } + + /* If there's one extra arg and it's an array, use it as our options. + */ + $options = []; + if ($n_supplied == $n_used + 1 && is_array($arguments[$n_used])) { + $options = array_pop($arguments); + $n_supplied -= 1; + } + + if ($n_supplied != $n_used) { + $operation->unrefOutputs(); + Config::error("$n_required arguments required, " . + "but $n_supplied supplied"); + } + + /* Set optional. + */ + foreach ($options as $name => $value) { + if (!in_array($name, $operation->introspect->optional_input) && + !in_array($name, $operation->introspect->optional_output)) { + $operation->unrefOutputs(); + Config::error("optional argument '$name' does not exist"); + } + + $operation->setMatch($name, $match_image, $value); + } + + /* Build the operation + */ + $pointer = Config::ffi()-> + vips_cache_operation_build($operation->pointer); + if ($pointer == null) { + $operation->unrefOutputs(); + Config::error(); + } + $operation = new VipsOperation($pointer); + $operation->introspect = self::introspect($operation_name); + + # TODO .. need to attach input refs to output, see _find_inside in + # pyvips + + /* Fetch required output args (and modified input args). + */ + $result = []; + foreach ($operation->introspect->required_output as $name) { + $result[$name] = $operation->get($name); + } + + /* Any optional output args. + */ + $option_keys = array_keys($options); + foreach ($operation->introspect->optional_output as $name) { + if (in_array($name, $option_keys)) { + $result[$name] = $operation->get($name); + } + } + + /* Free any outputs we've not used. + */ + $operation->unrefOutputs(); + + $result = self::wrapResult($result); + + Utils::debugLog($name, ['result' => var_export($result, true)]); + + return $result; + } + + /** + * Call any vips operation, with an explicit set of options. This is more + * convenient than callBase() if you have a set of known options. + * + * @param string $name The operation name. + * @param Image|null $instance The instance this operation is being invoked + * from. + * @param array $arguments An array of arguments to pass to the + * operation. + * @param array $options An array of optional arguments to pass to + * the operation. + * + * @throws Exception + * + * @return mixed The result(s) of the operation. + */ + public static function call( + string $name, + ?Image $instance, + array $arguments, + array $options = [] + ) { + return self::callBase( + $name, + $instance, + array_merge($arguments, [$options]) + ); + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: expandtab sw=4 ts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 + */ diff --git a/tests/NewTest.php b/tests/NewTest.php index 3a97c6b..bf72426 100644 --- a/tests/NewTest.php +++ b/tests/NewTest.php @@ -57,14 +57,6 @@ public function testVipsNewFromImage() $this->assertEquals($image2->avg(), 2); } - public function testVipsFindLoad() - { - $filename = __DIR__ . '/images/img_0076.jpg'; - $loader = Vips\Image::findLoad($filename); - - $this->assertEquals($loader, 'VipsForeignLoadJpegFile'); - } - public function testVipsNewFromBuffer() { $filename = __DIR__ . '/images/img_0076.jpg'; @@ -76,15 +68,6 @@ public function testVipsNewFromBuffer() $this->assertEquals($image->bands, 3); } - public function testVipsFindLoadBuffer() - { - $filename = __DIR__ . '/images/img_0076.jpg'; - $buffer = file_get_contents($filename); - $loader = Vips\Image::findLoadBuffer($buffer); - - $this->assertEquals($loader, 'VipsForeignLoadJpegBuffer'); - } - public function testVipsCopyMemory() { $filename = __DIR__ . '/images/img_0076.jpg'; diff --git a/tests/ShortcutTest.php b/tests/ShortcutTest.php index da251a2..a4cc80c 100644 --- a/tests/ShortcutTest.php +++ b/tests/ShortcutTest.php @@ -207,6 +207,8 @@ public function testOffsetSet() // replace band with image $test = $image->copy(); $test[1] = $base; + $avg = $test->avg(); + $this->assertTrue(abs($avg - 2.666) < 0.001); $this->assertEquals($test->bands, 3); $this->assertEquals($test[0]->avg(), 2); $this->assertEquals($test[1]->avg(), 2); diff --git a/tests/WriteTest.php b/tests/WriteTest.php index cc27b81..ed592f7 100644 --- a/tests/WriteTest.php +++ b/tests/WriteTest.php @@ -52,7 +52,7 @@ public function testVipsWriteToBuffer() $filename = __DIR__ . '/images/img_0076.jpg'; $image = Vips\Image::newFromFile($filename, ['shrink' => 8]); - $buffer1 = $image->writeToBuffer('.jpg'); + $buffer1 = $image->writeToBuffer('.jpg', ['Q' => 75]); $output_filename = $this->tmp('.jpg'); $image->writeToFile($output_filename); $buffer2 = file_get_contents($output_filename); @@ -68,6 +68,18 @@ public function testVipsWriteToMemory() $this->assertEquals($binaryStr, $memStr); } + + public function testVipsWriteToArray() + { + $filename = __DIR__ . '/images/img_0076.jpg'; + $image = Vips\Image::newFromFile($filename, ['shrink' => 8]); + $array = $image->crop(0, 0, 2, 2)->writeToArray(); + + $this->assertEquals( + $array, + [34, 39, 35, 44, 49, 45, 67, 52, 49, 120, 105, 102] + ); + } } /* From 3a74231a91a38644345fa56ecf6940fdfd5750bd Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Thu, 10 Mar 2022 08:51:08 +0000 Subject: [PATCH 028/123] revise README for 2.x --- README.md | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 592ef6b..1aa3614 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Build Status](https://travis-ci.org/libvips/php-vips.svg?branch=master)](https://travis-ci.org/libvips/php-vips) `php-vips` is a binding for [libvips](https://github.com/libvips/libvips) 8.7 -and later for PHP 7.4 and later. +and later that runs on PHP 7.4 and later. libvips is fast and needs little memory. The [`vips-php-bench`](https://github.com/jcupitt/php-vips-bench) repository @@ -20,7 +20,7 @@ destination in a set of small fragments. ### Install You need to [install the libvips -library](https://libvips.github.io/libvips/install.html). It's in the linux +library](https://www.libvips.org/install.html). It's in the linux package managers, homebrew and MacPorts, and there are Windows binaries on the vips website. For example, on Debian: @@ -34,7 +34,9 @@ Or macOS: brew install vips ``` -Then add vips to your `composer.json`: +You'll need to [enable FFI in your +PHP](https://www.php.net/manual/en/ffi.configuration.php), then add vips +to your `composer.json`: ``` "require": { @@ -71,6 +73,21 @@ $ ./try1.php ~/pics/k2.jpg x.tif See `examples/`. We have a [complete set of formatted API docs](https://libvips.github.io/php-vips/docs/classes/Jcupitt-Vips-Image.html). + +### How it works + +php-vips uses [php-ffi](https://www.php.net/manual/en/book.ffi.php) to +call directly into the libvips binary. It introspects the library binary +and presents the methods it finds as members of the `Image` class. + +This means that the API you see depends on the version of libvips that +php-vips finds at runtime, and not on php-vips. php-vips documentation assumes +you are using the latest stable version of the libvips library. + +The previous php-vips version that relied on a binary extension +and not on php-ffi is still available and supported in [the 1.x +branch](https://github.com/libvips/php-vips/tree/1.x). + ### Introduction to the API Almost all methods return a new image as the result, so you can chain them. @@ -130,19 +147,7 @@ And look in `docs/`. There are around 300 operations in the library, see the vips docs for an introduction: -https://libvips.github.io/libvips/API/current - -### TODO after merge - -- Support preloading, see https://www.php.net/manual/en/class.ffi.php - -- Rewrite the enum and doc generator in php. - -- Add source/target API - -- Add progress callbacks etc. - -- Add mutable. +https://libvips.org/API/current ### Test and install From b3f4813609f53c9a57b9805f1fab5b6cac319e65 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Thu, 10 Mar 2022 08:56:09 +0000 Subject: [PATCH 029/123] minor doc revision --- src/Image.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Image.php b/src/Image.php index c6de83a..5a77d99 100644 --- a/src/Image.php +++ b/src/Image.php @@ -42,7 +42,7 @@ * This class represents a Vips image object. * * This module provides a binding for the [vips image processing - * library](https://libvips.org) version 8.7 and later, and required PHP 7.4 + * library](https://libvips.org) version 8.7 and later, and requires PHP 7.4 * and later. * * # Example @@ -66,7 +66,7 @@ * * ``` * "require": { - * "jcupitt/vips" : "@dev" + * "jcupitt/vips" : "2.0.0" * } * ``` * From fb68c96893fdbc56d9c5e6b8f327db3786519295 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 11 Mar 2022 10:51:44 +0000 Subject: [PATCH 030/123] remove dbg from an example --- examples/watermark-image.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/watermark-image.php b/examples/watermark-image.php index 195343f..4e1ee83 100755 --- a/examples/watermark-image.php +++ b/examples/watermark-image.php @@ -4,7 +4,7 @@ require __DIR__ . '/vendor/autoload.php'; use Jcupitt\Vips; -Vips\Config::setLogger(new Vips\DebugLogger()); +#Vips\Config::setLogger(new Vips\DebugLogger()); if (count($argv) != 4) { echo("usage: ./watermark.php input-image output-image watermark-image\n"); From 16451e8f4d4f042279296eea2ffa68dd990fbb2a Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 19 Mar 2022 10:38:15 +0000 Subject: [PATCH 031/123] make watermark-text work for still images --- examples/watermark-text.php | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/examples/watermark-text.php b/examples/watermark-text.php index 654cada..9b57f26 100755 --- a/examples/watermark-text.php +++ b/examples/watermark-text.php @@ -11,18 +11,25 @@ exit(1); } -// we can stream the main image, and we want all frames $image = Vips\Image::newFromFile($argv[1], [ 'access' => 'sequential', - 'n' => -1 ]); +$page_height = $image->height; + +// is this an animated image? open all pages +if ($image->getType("n-pages") != 0) { + $image = Vips\Image::newFromFile($argv[1], [ + 'access' => 'sequential', + 'n' => -1 + ]); + + // the size of each frame + $page_height = $image->get('page-height'); +} $output_filename = $argv[2]; $text = $argv[3]; -// the size of each frame -$page_height = $image->get('page-height'); - $text_mask = Vips\Image::text($text, [ 'width' => $image->width, 'dpi' => 150 From d517e8526e0cc033ffab3b1fe3325a587b517236 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Thu, 24 Mar 2022 21:24:44 +0000 Subject: [PATCH 032/123] update links --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1aa3614..908cebf 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ $ ./try1.php ~/pics/k2.jpg x.tif ``` See `examples/`. We have a [complete set of formatted API -docs](https://libvips.github.io/php-vips/docs/classes/Jcupitt-Vips-Image.html). +docs](https://libvips.github.io/php-vips/classes/Jcupitt-Vips-Image.html). ### How it works @@ -136,7 +136,9 @@ For example: $image->writeToFile("fred.jpg", ["Q" => 90]); ``` -`php-vips` comes [with full API docs](https://libvips.github.io/php-vips/docs/classes/Jcupitt.Vips.Image.html). To regenerate these from your sources, type: +`php-vips` comes [with full API +docs](https://libvips.github.io/php-vips/classes/Jcupitt-Vips-Image.html). +To regenerate these from your sources, type: ``` $ vendor/bin/phpdoc From ee6e1cf8b5acf1245c4acf5bd970bc8ae2d003da Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Mon, 28 Mar 2022 18:06:03 +0100 Subject: [PATCH 033/123] fix libname on macos The OS name was not right for macOS. Released as 2.0.1. Thanks andrefelipe. See https://github.com/libvips/php-vips/issues/140 --- CHANGELOG.md | 4 ++++ src/Config.php | 1 + 2 files changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9139f34..d990973 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to `:vips` will be documented in this file. +## 2.0.1 - 2022-1-20 + +- Fix library name on macOS [andrefelipe] + ## 2.0.0 - 2022-1-20 Rewritten to use PHP FFI to call into the libvips library rather than a binary diff --git a/src/Config.php b/src/Config.php index ed720df..b523038 100644 --- a/src/Config.php +++ b/src/Config.php @@ -286,6 +286,7 @@ private static function libraryName($name, $abi) return "$name-$abi.dll"; case "OSX": + case "Darwin": return "$name.$abi.dylib"; default: From 446c0b58a53de654bac5aaad6b62afac5ae21a00 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 6 Apr 2022 17:59:29 +0100 Subject: [PATCH 034/123] note --no-install-recommends for debian See https://github.com/libvips/php-vips/issues/141 --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 908cebf..f3a4d64 100644 --- a/README.md +++ b/README.md @@ -25,9 +25,11 @@ package managers, homebrew and MacPorts, and there are Windows binaries on the vips website. For example, on Debian: ``` -sudo apt-get install libvips-dev +sudo apt-get install --no-install-recommends libvips42 ``` +(`--no-install-recommends` stops Debian installing a *lot* of extra packages) + Or macOS: ``` From bc3061bee8549cf232220e251596048bb8ad87da Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Thu, 14 Apr 2022 11:19:54 +0100 Subject: [PATCH 035/123] fix string_options code Ooops, it was only half-implemented. Fixes eg. `newFromFile("x.tif[page=2]");`. --- CHANGELOG.md | 4 ++++ src/VipsOperation.php | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d990973..3a3a719 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to `:vips` will be documented in this file. +## 2.0.2 - 2022-4-14 + +- Fix extra optional string args on file open + ## 2.0.1 - 2022-1-20 - Fix library name on macOS [andrefelipe] diff --git a/src/VipsOperation.php b/src/VipsOperation.php index 606792d..5148e42 100644 --- a/src/VipsOperation.php +++ b/src/VipsOperation.php @@ -320,6 +320,15 @@ public static function callBase( "but $n_supplied supplied"); } + /* set any string options before any args so they can't be + * overridden. + */ + if (array_key_exists("string_options", $options)) { + $string_options = $options["string_options"]; + unset($options["string_options"]); + $operation->setString($string_options); + } + /* Set optional. */ foreach ($options as $name => $value) { From 3c178b30521736136e0368d2858848bf4e6e5f01 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Thu, 21 Apr 2022 09:25:07 +0100 Subject: [PATCH 036/123] note preload and ffi implications in README thanks @talisto see https://github.com/libvips/php-vips/issues/123 --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index f3a4d64..6ada06b 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,13 @@ to your `composer.json`: } ``` +php-vips does not yet support preloading, so you need to enable FFI globally. +This has some security implications, since anyone who can run php on your +server can use it to make calls off into any native library they can find. + +Of course if attackers are running their own PHP code on your webserver you +are probably already toast, unfortunately. + ### Example ```php From d8d0d6336a9b7c2de4d7b379eef440c9c918cc90 Mon Sep 17 00:00:00 2001 From: Kleis Auke Wolthuizen Date: Mon, 4 Jul 2022 16:50:23 +0200 Subject: [PATCH 037/123] Fix Windows and 32-bit support (#146) * Fix Windows and 32-bit support - Move FFI functions to a separate class. - Split the GLib and GObject symbols into a separate string. - Ensure `g_*` functions are called via `libglib-2.0-0.dll` or `libgobject-2.0-0.dll` on Windows. - Fix 32-bit support, use `PHP_INT_SIZE` to determine `GType`. - Remove debug code in `GValue::toEnum`. - Prefer to call `cast` on the static `\FFI` class. - Remove `Config::error()` in favor of `throw new Exception()`, move error buffer logic to `Vips\Exception`. - Remove `GsfOutputCsvQuotingMode` class. - Remove `Config::printAll`, already exists as `VipsObject::printAll`. - Move `Config::filenameGetFilename` and `Config::filenameGetOptions` to Utils class. * Cleanup * Fix `FFI::libraryName` * Add missing type declarations * Revert "Move FFI functions to a separate class" To make reviewing easier. * Ensure exception is thrown with the error buffer --- README.md | 1 - composer.json | 9 +- examples/generate_phpdoc.py | 2 +- phpdoc.xml | 6 +- src/Config.php | 458 ++++++++++++++++---------------- src/Exception.php | 11 + src/GObject.php | 10 +- src/GValue.php | 124 +++++---- src/GsfOutputCsvQuotingMode.php | 54 ---- src/Image.php | 112 ++++---- src/ImageAutodoc.php | 8 +- src/Interpolate.php | 14 +- src/Introspect.php | 41 ++- src/Utils.php | 28 +- src/VipsObject.php | 54 ++-- src/VipsOperation.php | 74 ++---- 16 files changed, 478 insertions(+), 528 deletions(-) delete mode 100644 src/GsfOutputCsvQuotingMode.php diff --git a/README.md b/README.md index 6ada06b..93ecce9 100644 --- a/README.md +++ b/README.md @@ -174,4 +174,3 @@ $ vendor/bin/phpdoc $ cd src $ ../examples/generate_phpdoc.py ``` - diff --git a/composer.json b/composer.json index 2c61251..f007871 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,7 @@ "php-parallel-lint/php-parallel-lint": "^1.3", "phpdocumentor/shim": "^3.3", "phpunit/phpunit": "^9.5", - "squizlabs/php_codesniffer": "^3.6" + "squizlabs/php_codesniffer": "^3.7" }, "autoload": { "psr-4": { @@ -50,5 +50,10 @@ ] }, "minimum-stability": "dev", - "prefer-stable": true + "prefer-stable": true, + "config": { + "allow-plugins": { + "phpdocumentor/shim": true + } + } } diff --git a/examples/generate_phpdoc.py b/examples/generate_phpdoc.py index 7ad9eda..9676c8e 100755 --- a/examples/generate_phpdoc.py +++ b/examples/generate_phpdoc.py @@ -263,7 +263,7 @@ def add_nickname(gtype, a, b): f.write(' * @see {0} for possible values\n'.format(remove_prefix(type_name(gtype)))) f.write(' */\n') - f.write('abstract class ImageAutodoc\n') + f.write('abstract class ImageAutodoc extends VipsObject\n') f.write('{\n') f.write('}\n') diff --git a/phpdoc.xml b/phpdoc.xml index a37402d..131e964 100644 --- a/phpdoc.xml +++ b/phpdoc.xml @@ -1,16 +1,14 @@ - + php-vips + php-vips docs - docs - src - diff --git a/src/Config.php b/src/Config.php index b523038..5a9294f 100644 --- a/src/Config.php +++ b/src/Config.php @@ -55,18 +55,37 @@ class Config /** * The logger instance. + */ + private static ?LoggerInterface $logger = null; + + /** + * The FFI handle we use for the glib binary. * - * @var LoggerInterface + * @internal */ - private static $logger; + private static \FFI $glib; + + + /** + * The FFI handle we use for the gobject binary. + * + * @internal + */ + private static \FFI $gobject; /** * The FFI handle we use for the libvips binary. * * @internal */ - private static \FFI $ffi; - private static $ffi_inited = false; + private static \FFI $vips; + + /** + * Are the above FFI handles initialized? + * + * @internal + */ + private static bool $ffi_inited = false; /** * The library version number we detect. @@ -122,7 +141,7 @@ public static function getLogger(): ?LoggerInterface */ public static function cacheSetMax(int $value): void { - Config::ffi()->vips_cache_set_max($value); + self::vips()->vips_cache_set_max($value); } /** @@ -136,7 +155,7 @@ public static function cacheSetMax(int $value): void */ public static function cacheSetMaxMem(int $value): void { - Config::ffi()->vips_cache_set_max_mem($value); + self::vips()->vips_cache_set_max_mem($value); } /** @@ -149,7 +168,7 @@ public static function cacheSetMaxMem(int $value): void */ public static function cacheSetMaxFiles(int $value): void { - Config::ffi()->vips_cache_set_max_files($value); + self::vips()->vips_cache_set_max_files($value); } /** @@ -163,123 +182,91 @@ public static function cacheSetMaxFiles(int $value): void */ public static function concurrencySet(int $value): void { - Config::ffi()->vips_concurrency_set($value); + self::vips()->vips_concurrency_set($value); } - /** - * Gets the libvips version number as a string of the form - * MAJOR.MINOR.MICRO, for example "8.6.1". - * - * @return string - */ - public static function version(): string + public static function glib(): \FFI { - Config::ffi(); + self::init(); - return self::$library_major . "." . - self::$library_minor . ".". - self::$library_micro; + return self::$glib; } - /** - * Handy for debugging. - */ - public static function printAll() + public static function gobject(): \FFI { - Config::ffi()->vips_object_print_all(); + self::init(); + + return self::$gobject; } - public static function ffi() + public static function vips(): \FFI { - if (!self::$ffi_inited) { - self::init(); - self::$ffi_inited = true; - } + self::init(); - return self::$ffi; + return self::$vips; } - public static function ctypes(string $name) + public static function ctypes(string $name): \FFI\CType { - Config::ffi(); + self::init(); return self::$ctypes[$name]; } - public static function gtypes(string $name) + public static function gtypes(string $name): int { - Config::ffi(); + self::init(); return self::$gtypes[$name]; } - public static function ftypes(string $name) + public static function ftypes(string $name): string { - Config::ffi(); + self::init(); return self::$ftypes[$name]; } /** - * Throw a vips error as an exception. - * - * @throws Exception - * - * @return void + * Gets the libvips version number as a string of the form + * MAJOR.MINOR.MICRO, for example "8.6.1". * - * @internal + * @return string */ - public static function error(string $message = "") + public static function version(): string { - if ($message == "") { - $message = Config::ffi()->vips_error_buffer(); - Config::ffi()->vips_error_clear(); - } - $exception = new Exception($message); - Utils::errorLog($message, $exception); - throw $exception; + self::init(); + + return self::$library_major . "." . + self::$library_minor . "." . + self::$library_micro; } /** * Shut down libvips. Call this just before process exit. * - * @throws Exception - * * @return void - * - * @internal */ - public static function shutDown() + public static function shutDown(): void { - Config::ffi()->vips_shutdown(); + self::vips()->vips_shutdown(); } - public static function filenameGetFilename($name) - { - $pointer = Config::ffi()->vips_filename_get_filename($name); - $filename = \FFI::string($pointer); - Config::ffi()->g_free($pointer); - - return $filename; - } - - public static function filenameGetOptions($name) - { - $pointer = Config::ffi()->vips_filename_get_options($name); - $options = \FFI::string($pointer); - Config::ffi()->g_free($pointer); - - return $options; - } - - public static function atLeast($need_major, $need_minor) + /** + * Is this at least libvips major.minor[.patch]? + * @param int $x Major component. + * @param int $y Minor component. + * @param int $z Patch component. + * @return bool `true` if at least libvips major.minor[.patch]; otherwise, `false`. + */ + public static function atLeast(int $x, int $y, int $z = 0): bool { - return $need_major < self::$library_major || - ($need_major == self::$library_major && - $need_minor <= self::$library_minor); + return self::$library_major > $x || + self::$library_major == $x && self::$library_minor > $y || + self::$library_major == $x && self::$library_minor == $y && self::$library_micro >= $z; } - private static function libraryName($name, $abi) + private static function libraryName(string $name, int $abi): string { switch (PHP_OS_FAMILY) { case "Windows": @@ -295,32 +282,43 @@ private static function libraryName($name, $abi) } } - private static function init() + private static function init(): void { - $library = self::libraryName("libvips", 42); + // Already initialized. + if (self::$ffi_inited) { + return; + } + + $vips_libname = self::libraryName("libvips", 42); + if (PHP_OS_FAMILY === "Windows") { + $glib_libname = self::libraryName("libglib-2.0", 0); + $gobject_libname = self::libraryName("libgobject-2.0", 0); + } else { + $glib_libname = $vips_libname; + $gobject_libname = $vips_libname; + } - Utils::debugLog("init", ["libray" => $library]); + Utils::debugLog("init", ["library" => $vips_libname]); /* FIXME ... maybe display a helpful message on failure? This will * probably be the main point of failure. */ - $ffi = \FFI::cdef(<<vips_init(""); + $result = $vips->vips_init(""); if ($result != 0) { - $msg = $ffi->vips_error_buffer(); - throw new Vips\Exception("libvips error: $msg"); + throw new Exception("libvips error: " . $vips->vips_error_buffer()); } Utils::debugLog("init", ["vips_init" => $result]); # get the library version number, then we can build the API - self::$library_major = $ffi->vips_version(0); - self::$library_minor = $ffi->vips_version(1); - self::$library_micro = $ffi->vips_version(2); + self::$library_major = $vips->vips_version(0); + self::$library_minor = $vips->vips_version(1); + self::$library_micro = $vips->vips_version(2); Utils::debugLog("init", [ "libvips version" => [ self::$library_major, @@ -330,73 +328,77 @@ private static function init() ]); if (!self::atLeast(8, 7)) { - throw new Vips\Exception("your libvips is too old -- " . + throw new Exception("your libvips is too old -- " . "8.7 or later required"); } - if (PHP_INT_SIZE != 8) { - # we could maybe fix this if it's important ... it's mostly - # necessary since GType is the size of a pointer, and there's no - # easy way to discover if php is running on a 32 or 64-bit - # systems (as far as I can see) - throw new Vips\Exception("your php only supports 32-bit ints -- " . - "64 bit ints required"); - } + $is_64bits = PHP_INT_SIZE === 8; - # the whole libvips API, mostly adapted from pyvips - $header = <<vips_blend_mode_get_type(); - self::$ffi->vips_interpretation_get_type(); - self::$ffi->vips_operation_flags_get_type(); - self::$ffi->vips_band_format_get_type(); - self::$ffi->vips_token_get_type(); - self::$ffi->vips_saveable_get_type(); - self::$ffi->vips_image_type_get_type(); + self::$vips->vips_blend_mode_get_type(); + self::$vips->vips_interpretation_get_type(); + self::$vips->vips_operation_flags_get_type(); + self::$vips->vips_band_format_get_type(); + self::$vips->vips_token_get_type(); + self::$vips->vips_saveable_get_type(); + self::$vips->vips_image_type_get_type(); // look these up in advance self::$ctypes = [ - "GObject" => self::$ffi->type("GObject*"), - "VipsObject" => self::$ffi->type("VipsObject*"), - "VipsOperation" => self::$ffi->type("VipsOperation*"), - "VipsImage" => self::$ffi->type("VipsImage*"), - "VipsInterpolate" => self::$ffi->type("VipsInterpolate*"), + "GObject" => self::$gobject->type("GObject*"), + "GParamSpec" => self::$gobject->type("GParamSpec*"), + "VipsObject" => self::$vips->type("VipsObject*"), + "VipsOperation" => self::$vips->type("VipsOperation*"), + "VipsImage" => self::$vips->type("VipsImage*"), + "VipsInterpolate" => self::$vips->type("VipsInterpolate*"), ]; self::$gtypes = [ - "gboolean" => self::$ffi->g_type_from_name("gboolean"), - "gint" => self::$ffi->g_type_from_name("gint"), - "gint64" => self::$ffi->g_type_from_name("gint64"), - "guint64" => self::$ffi->g_type_from_name("guint64"), - "gdouble" => self::$ffi->g_type_from_name("gdouble"), - "gchararray" => self::$ffi->g_type_from_name("gchararray"), - "VipsRefString" => self::$ffi->g_type_from_name("VipsRefString"), - - "GEnum" => self::$ffi->g_type_from_name("GEnum"), - "GFlags" => self::$ffi->g_type_from_name("GFlags"), - "VipsBandFormat" => self::$ffi->g_type_from_name("VipsBandFormat"), - "VipsBlendMode" => self::$ffi->g_type_from_name("VipsBlendMode"), - "VipsArrayInt" => self::$ffi->g_type_from_name("VipsArrayInt"), + "gboolean" => self::$gobject->g_type_from_name("gboolean"), + "gint" => self::$gobject->g_type_from_name("gint"), + "gint64" => self::$gobject->g_type_from_name("gint64"), + "guint64" => self::$gobject->g_type_from_name("guint64"), + "gdouble" => self::$gobject->g_type_from_name("gdouble"), + "gchararray" => self::$gobject->g_type_from_name("gchararray"), + "VipsRefString" => self::$gobject->g_type_from_name("VipsRefString"), + + "GEnum" => self::$gobject->g_type_from_name("GEnum"), + "GFlags" => self::$gobject->g_type_from_name("GFlags"), + "VipsBandFormat" => self::$gobject->g_type_from_name("VipsBandFormat"), + "VipsBlendMode" => self::$gobject->g_type_from_name("VipsBlendMode"), + "VipsArrayInt" => self::$gobject->g_type_from_name("VipsArrayInt"), "VipsArrayDouble" => - self::$ffi->g_type_from_name("VipsArrayDouble"), - "VipsArrayImage" => self::$ffi->g_type_from_name("VipsArrayImage"), - "VipsBlob" => self::$ffi->g_type_from_name("VipsBlob"), + self::$gobject->g_type_from_name("VipsArrayDouble"), + "VipsArrayImage" => self::$gobject->g_type_from_name("VipsArrayImage"), + "VipsBlob" => self::$gobject->g_type_from_name("VipsBlob"), - "GObject" => self::$ffi->g_type_from_name("GObject"), - "VipsImage" => self::$ffi->g_type_from_name("VipsImage"), + "GObject" => self::$gobject->g_type_from_name("GObject"), + "VipsImage" => self::$gobject->g_type_from_name("VipsImage"), ]; // map vips format names to c type names @@ -829,6 +838,7 @@ private static function init() ]; Utils::debugLog("init", ["done"]); + self::$ffi_inited = true; } } diff --git a/src/Exception.php b/src/Exception.php index d4c4672..f8279b0 100644 --- a/src/Exception.php +++ b/src/Exception.php @@ -51,6 +51,17 @@ */ class Exception extends \Exception { + public function __construct($message = "", $code = 0, \Throwable $previous = null) + { + if ($message == "") { + $message = "libvips error: " . Config::vips()->vips_error_buffer(); + Config::vips()->vips_error_clear(); + } + + Utils::errorLog($message); + + parent::__construct($message, $code, $previous); + } } /* diff --git a/src/GObject.php b/src/GObject.php index 74c35ef..46620b3 100644 --- a/src/GObject.php +++ b/src/GObject.php @@ -69,7 +69,7 @@ abstract class GObject * * @internal */ - public function __construct($pointer) + public function __construct(\FFI\CData $pointer) { $this->pointer = \FFI::cast(Config::ctypes("GObject"), $pointer); } @@ -84,14 +84,14 @@ public function __clone() $this->ref(); } - public function ref() + public function ref(): void { - Config::ffi()->g_object_ref($this->pointer); + Config::gobject()->g_object_ref($this->pointer); } - public function unref() + public function unref(): void { - Config::ffi()->g_object_unref($this->pointer); + Config::gobject()->g_object_unref($this->pointer); } // TODO signal marshalling to go in diff --git a/src/GValue.php b/src/GValue.php index c0ce60d..32d28d1 100644 --- a/src/GValue.php +++ b/src/GValue.php @@ -46,24 +46,24 @@ class GValue public function __construct() { # allocate a gvalue on the heap, and make it persistent between requests - $this->struct = Config::ffi()->new("GValue", true, true); + $this->struct = Config::gobject()->new("GValue", true, true); $this->pointer = \FFI::addr($this->struct); # GValue needs to be inited to all zero \FFI::memset($this->pointer, 0, \FFI::sizeof($this->struct)); } - /* Turn a string into an enum value, if possible + /** + * Turn a string into an enum value, if possible + * @throws Exception */ - public static function toEnum($gtype, $value) + public static function toEnum(int $gtype, $value): int { if (is_string($value)) { - $enum_value = Config::ffi()-> + $enum_value = Config::vips()-> vips_enum_from_nick("php-vips", $gtype, $value); if ($enum_value < 0) { - echo "gtype = " . $gtype . "\n"; - echo "value = " . $value . "\n"; - Config::error(); + throw new Exception(); } } else { $enum_value = $value; @@ -72,11 +72,15 @@ public static function toEnum($gtype, $value) return $enum_value; } - public static function fromEnum($gtype, $value) + /** + * Turn an enum into a string, if possible + * @throws Exception + */ + public static function fromEnum(int $gtype, int $value): string { - $result = Config::ffi()->vips_enum_nick($gtype, $value); + $result = Config::vips()->vips_enum_nick($gtype, $value); if ($result === null) { - Config::error("value not in enum"); + throw new Exception("value not in enum"); } return $result; @@ -84,12 +88,12 @@ public static function fromEnum($gtype, $value) public function __destruct() { - Config::ffi()->g_value_unset($this->pointer); + Config::gobject()->g_value_unset($this->pointer); } - public function setType(int $gtype) + public function setType(int $gtype): void { - Config::ffi()->g_value_init($this->pointer, $gtype); + Config::gobject()->g_value_init($this->pointer, $gtype); } public function getType(): int @@ -97,37 +101,44 @@ public function getType(): int return $this->pointer->g_type; } - public function set($value) + /** + * Set a GValue. + * + * @param mixed $value Value to be set. + * + * @throws Exception + */ + public function set($value): void { $gtype = $this->getType(); switch ($gtype) { case Config::gtypes("gboolean"): - Config::ffi()->g_value_set_boolean($this->pointer, $value); + Config::gobject()->g_value_set_boolean($this->pointer, $value); break; case Config::gtypes("gint"): - Config::ffi()->g_value_set_int($this->pointer, $value); + Config::gobject()->g_value_set_int($this->pointer, $value); break; case Config::gtypes("gint64"): - Config::ffi()->g_value_set_int64($this->pointer, $value); + Config::gobject()->g_value_set_int64($this->pointer, $value); break; case Config::gtypes("guint64"): - Config::ffi()->g_value_set_uint64($this->pointer, $value); + Config::gobject()->g_value_set_uint64($this->pointer, $value); break; case Config::gtypes("gdouble"): - Config::ffi()->g_value_set_double($this->pointer, $value); + Config::gobject()->g_value_set_double($this->pointer, $value); break; case Config::gtypes("gchararray"): - Config::ffi()->g_value_set_string($this->pointer, $value); + Config::gobject()->g_value_set_string($this->pointer, $value); break; case Config::gtypes("VipsRefString"): - Config::ffi()-> + Config::vips()-> vips_value_set_ref_string($this->pointer, $value); break; @@ -141,7 +152,7 @@ public function set($value) for ($i = 0; $i < $n; $i++) { $array[$i] = $value[$i]; } - Config::ffi()-> + Config::vips()-> vips_value_set_array_int($this->pointer, $array, $n); break; @@ -155,7 +166,7 @@ public function set($value) for ($i = 0; $i < $n; $i++) { $array[$i] = $value[$i]; } - Config::ffi()-> + Config::vips()-> vips_value_set_array_double($this->pointer, $array, $n); break; @@ -164,8 +175,8 @@ public function set($value) $value = [$value]; } $n = count($value); - Config::ffi()->vips_value_set_array_image($this->pointer, $n); - $array = Config::ffi()-> + Config::vips()->vips_value_set_array_image($this->pointer, $n); + $array = Config::vips()-> vips_value_get_array_image($this->pointer, null); for ($i = 0; $i < $n; $i++) { $image = $value[$i]; @@ -183,20 +194,20 @@ public function set($value) for ($i = 0; $i < $n; $i++) { $memory[$i] = $value[$i]; } - Config::ffi()-> + Config::vips()-> vips_value_set_blob_free($this->pointer, $memory, $n); break; default: - $fundamental = Config::ffi()->g_type_fundamental($gtype); + $fundamental = Config::gobject()->g_type_fundamental($gtype); switch ($fundamental) { case Config::gtypes("GObject"): - Config::ffi()-> + Config::gobject()-> g_value_set_object($this->pointer, $value->pointer); break; case Config::gtypes("GEnum"): - Config::ffi()->g_value_set_enum( + Config::gobject()->g_value_set_enum( $this->pointer, self::toEnum($gtype, $value) ); @@ -205,20 +216,26 @@ public function set($value) case Config::gtypes("GFlags"): /* Just set as int. */ - Config::ffi()-> + Config::gobject()-> g_value_set_flags($this->pointer, $value); break; default: - $typeName = Config::ffi()->g_type_name($gtype); + $typeName = Config::gobject()->g_type_name($gtype); throw new \BadMethodCallException( "gtype $typeName ($gtype) not implemented" ); - break; } } } + /** + * Get the contents of a GValue. + * + * @return mixed The contents of this GValue. + * + * @throws Exception + */ public function get() { $gtype = $this->getType(); @@ -226,47 +243,47 @@ public function get() switch ($gtype) { case Config::gtypes("gboolean"): - $result = Config::ffi()->g_value_get_boolean($this->pointer); + $result = Config::gobject()->g_value_get_boolean($this->pointer); break; case Config::gtypes("gint"): - $result = Config::ffi()->g_value_get_int($this->pointer); + $result = Config::gobject()->g_value_get_int($this->pointer); break; case Config::gtypes("gint64"): - $result = Config::ffi()->g_value_get_int64($this->pointer); + $result = Config::gobject()->g_value_get_int64($this->pointer); break; case Config::gtypes("guint64"): - $result = Config::ffi()->g_value_get_uint64($this->pointer); + $result = Config::gobject()->g_value_get_uint64($this->pointer); break; case Config::gtypes("gdouble"): - $result = Config::ffi()->g_value_get_double($this->pointer); + $result = Config::gobject()->g_value_get_double($this->pointer); break; case Config::gtypes("gchararray"): - $result = Config::ffi()->g_value_get_string($this->pointer); + $result = Config::gobject()->g_value_get_string($this->pointer); break; case Config::gtypes("VipsRefString"): - $p_size = Config::ffi()->new("size_t[1]"); - $result = Config::ffi()-> + $p_size = Config::vips()->new("size_t[1]"); + $result = Config::vips()-> vips_value_get_ref_string($this->pointer, $p_size); # $p_size[0] will be the string length, but assume it's null # terminated break; case Config::gtypes("VipsImage"): - $pointer = Config::ffi()->g_value_get_object($this->pointer); + $pointer = Config::gobject()->g_value_get_object($this->pointer); $result = new Image($pointer); // get_object does not increment the ref count $result->ref(); break; case Config::gtypes("VipsArrayInt"): - $p_len = Config::ffi()->new("int[1]"); - $pointer = Config::ffi()-> + $p_len = Config::vips()->new("int[1]"); + $pointer = Config::vips()-> vips_value_get_array_int($this->pointer, $p_len); $result = []; for ($i = 0; $i < $p_len[0]; $i++) { @@ -275,8 +292,8 @@ public function get() break; case Config::gtypes("VipsArrayDouble"): - $p_len = Config::ffi()->new("int[1]"); - $pointer = Config::ffi()-> + $p_len = Config::vips()->new("int[1]"); + $pointer = Config::vips()-> vips_value_get_array_double($this->pointer, $p_len); $result = []; for ($i = 0; $i < $p_len[0]; $i++) { @@ -285,8 +302,8 @@ public function get() break; case Config::gtypes("VipsArrayImage"): - $p_len = Config::ffi()->new("int[1]"); - $pointer = Config::ffi()-> + $p_len = Config::vips()->new("int[1]"); + $pointer = Config::vips()-> vips_value_get_array_image($this->pointer, $p_len); $result = []; for ($i = 0; $i < $p_len[0]; $i++) { @@ -297,17 +314,17 @@ public function get() break; case Config::gtypes("VipsBlob"): - $p_len = Config::ffi()->new("size_t[1]"); - $pointer = Config::ffi()-> + $p_len = Config::vips()->new("size_t[1]"); + $pointer = Config::vips()-> vips_value_get_blob($this->pointer, $p_len); $result = \FFI::string($pointer, $p_len[0]); break; default: - $fundamental = Config::ffi()->g_type_fundamental($gtype); + $fundamental = Config::gobject()->g_type_fundamental($gtype); switch ($fundamental) { case Config::gtypes("GEnum"): - $result = Config::ffi()-> + $result = Config::gobject()-> g_value_get_enum($this->pointer); $result = self::fromEnum($gtype, $result); break; @@ -315,16 +332,15 @@ public function get() case Config::gtypes("GFlags"): /* Just get as int. */ - $result = Config::ffi()-> + $result = Config::gobject()-> g_value_get_flags($this->pointer); break; default: - $typeName = Config::ffi()->g_type_name($gtype); + $typeName = Config::gobject()->g_type_name($gtype); throw new \BadMethodCallException( "gtype $typeName ($gtype) not implemented" ); - break; } } diff --git a/src/GsfOutputCsvQuotingMode.php b/src/GsfOutputCsvQuotingMode.php deleted file mode 100644 index a8a08f0..0000000 --- a/src/GsfOutputCsvQuotingMode.php +++ /dev/null @@ -1,54 +0,0 @@ - - * @copyright 2016 John Cupitt - * @license https://opensource.org/licenses/MIT MIT - * @link https://github.com/jcupitt/php-vips - */ - -namespace Jcupitt\Vips; - -/** - * The GsfOutputCsvQuotingMode enum. - * @category Images - * @package Jcupitt\Vips - * @author John Cupitt - * @copyright 2016 John Cupitt - * @license https://opensource.org/licenses/MIT MIT - * @link https://github.com/jcupitt/php-vips - */ -abstract class GsfOutputCsvQuotingMode -{ - const NEVER = 'never'; - const AUTO = 'auto'; -} diff --git a/src/Image.php b/src/Image.php index 5a77d99..414198f 100644 --- a/src/Image.php +++ b/src/Image.php @@ -492,10 +492,9 @@ class Image extends ImageAutodoc implements \ArrayAccess * * @internal */ - public function __construct($pointer) + public function __construct(\FFI\CData $pointer) { - $this->pointer = Config::ffi()-> - cast(Config::ctypes("VipsImage"), $pointer); + $this->pointer = \FFI::cast(Config::ctypes("VipsImage"), $pointer); parent::__construct($pointer); } @@ -647,7 +646,7 @@ private static function runCmplx(\Closure $func, Image $image): Image /** * Handy for things like self::more. Call a 2-ary vips operator like - * 'more', but if the arg is not an image (ie. it's a constant), call + * 'more', but if the arg is not an image (i.e. it's a constant), call * 'more_const' instead. * * @param mixed $other The right-hand argument. @@ -690,15 +689,13 @@ private function callEnum( */ public static function findLoad(string $filename): ?string { - $result = Config::ffi()->vips_foreign_find_load($filename); - - return $result; + return Config::vips()->vips_foreign_find_load($filename); } /** * Create a new Image from a file on disc. * - * @param string $filename The file to open. + * @param string $name The file to open. * @param array $options Any options to pass on to the load operation. * * @throws Exception @@ -709,12 +706,12 @@ public static function newFromFile( string $name, array $options = [] ): Image { - $filename = Config::filenameGetFilename($name); - $string_options = Config::filenameGetOptions($name); + $filename = Utils::filenameGetFilename($name); + $string_options = Utils::filenameGetOptions($name); $loader = self::findLoad($filename); if ($loader == null) { - Config::error(); + throw new Exception(); } if (strlen($string_options) != 0) { @@ -723,9 +720,7 @@ public static function newFromFile( ], $options); } - $result = VipsOperation::call($loader, null, [$filename], $options); - - return $result; + return VipsOperation::call($loader, null, [$filename], $options); } /** @@ -739,10 +734,8 @@ public static function newFromFile( */ public static function findLoadBuffer(string $buffer): ?string { - $result = Config::ffi()-> + return Config::vips()-> vips_foreign_find_load_buffer($buffer, strlen($buffer)); - - return $result; } /** @@ -764,7 +757,7 @@ public static function newFromBuffer( ): Image { $loader = self::findLoadBuffer($buffer); if ($loader == null) { - Config::error(); + throw new Exception(); } if (strlen($string_options) != 0) { @@ -773,9 +766,7 @@ public static function newFromBuffer( ], $options); } - $result = VipsOperation::call($loader, null, [$buffer], $options); - - return $result; + return VipsOperation::call($loader, null, [$buffer], $options); } /** @@ -814,10 +805,10 @@ public static function newFromArray( } } - $pointer = Config::ffi()-> + $pointer = Config::vips()-> vips_image_new_matrix_from_array($width, $height, $a, $n); if ($pointer == null) { - Config::error(); + throw new Exception(); } $result = new Image($pointer); @@ -851,7 +842,7 @@ public static function newFromMemory( * * TODO add a references system instead, see pyvips. */ - $pointer = Config::ffi()->vips_image_new_from_memory_copy( + $pointer = Config::vips()->vips_image_new_from_memory_copy( $data, strlen($data), $width, @@ -860,12 +851,10 @@ public static function newFromMemory( $format ); if ($pointer == null) { - Config::error(); + throw new Exception(); } - $result = new Image($pointer); - - return $result; + return new Image($pointer); } /** @@ -873,7 +862,7 @@ public static function newFromMemory( * * See Interpolator::newFromName() for the new thing. */ - public static function newInterpolator(string $name) + public static function newInterpolator(string $name): Interpolate { return Interpolate::newFromName($name); } @@ -904,21 +893,19 @@ public function newFromImage($value): Image $this->height, ['extend' => Extend::COPY] ); - $image = $image->copy([ + return $image->copy([ 'interpretation' => $this->interpretation, 'xres' => $this->xres, 'yres' => $this->yres, 'xoffset' => $this->xoffset, 'yoffset' => $this->yoffset ]); - - return $image; } /** * Write an image to a file. * - * @param string $filename The file to write the image to. + * @param string $name The file to write the image to. * @param array $options Any options to pass on to the selected save * operation. * @@ -928,12 +915,12 @@ public function newFromImage($value): Image */ public function writeToFile(string $name, array $options = []): void { - $filename = Config::filenameGetFilename($name); - $string_options = Config::filenameGetOptions($name); + $filename = Utils::filenameGetFilename($name); + $string_options = Utils::filenameGetOptions($name); - $saver = Config::ffi()->vips_foreign_find_save($filename); + $saver = Config::vips()->vips_foreign_find_save($filename); if ($saver == "") { - Config::error(); + throw new Exception(); } if (strlen($string_options) != 0) { @@ -945,7 +932,7 @@ public function writeToFile(string $name, array $options = []): void $result = VipsOperation::call($saver, $this, [$filename], $options); if ($result === -1) { - Config::error(); + throw new Exception(); } } @@ -962,12 +949,12 @@ public function writeToFile(string $name, array $options = []): void */ public function writeToBuffer(string $suffix, array $options = []): string { - $filename = Config::filenameGetFilename($suffix); - $string_options = Config::filenameGetOptions($suffix); + $filename = Utils::filenameGetFilename($suffix); + $string_options = Utils::filenameGetOptions($suffix); - $saver = Config::ffi()->vips_foreign_find_save_buffer($filename); + $saver = Config::vips()->vips_foreign_find_save_buffer($filename); if ($saver == "") { - Config::error(); + throw new Exception(); } if (strlen($string_options) != 0) { @@ -976,9 +963,7 @@ public function writeToBuffer(string $suffix, array $options = []): string ], $options); } - $result = VipsOperation::call($saver, $this, [], $options); - - return $result; + return VipsOperation::call($saver, $this, [], $options); } /** @@ -993,16 +978,16 @@ public function writeToMemory(): string $ctype = \FFI::arrayType(\FFI::type("size_t"), [1]); $p_size = \FFI::new($ctype); - $pointer = Config::ffi()-> + $pointer = Config::vips()-> vips_image_write_to_memory($this->pointer, $p_size); if ($pointer == null) { - Config::error(); + throw new Exception(); } // string() takes a copy $result = \FFI::string($pointer, $p_size[0]); - Config::ffi()->g_free($pointer); + Config::glib()->g_free($pointer); return $result; } @@ -1035,10 +1020,10 @@ public function writeToArray(): array $ctype = \FFI::arrayType(\FFI::type("size_t"), [1]); $p_size = \FFI::new($ctype); - $pointer = Config::ffi()-> + $pointer = Config::vips()-> vips_image_write_to_memory($this->pointer, $p_size); if ($pointer == null) { - Config::error(); + throw new Exception(); } // wrap pointer up as a C array of the right type @@ -1054,7 +1039,7 @@ public function writeToArray(): array } // the vips result is not PHP memory, so we must free it - Config::ffi()->g_free($pointer); + Config::glib()->g_free($pointer); return $result; } @@ -1075,13 +1060,11 @@ public function writeToArray(): array */ public function copyMemory(): Image { - $pointer = Config::ffi()->vips_image_copy_memory($this->pointer); + $pointer = Config::vips()->vips_image_copy_memory($this->pointer); if ($pointer == null) { - Config::error(); + throw new Exception(); } - $result = new Image($pointer); - - return $result; + return new Image($pointer); } /** @@ -1105,6 +1088,7 @@ public function __get(string $name) * @param mixed $value The value to set for this property. * * @return void + * @throws Exception */ public function __set(string $name, $value): void { @@ -1141,9 +1125,9 @@ public function __isset(string $name): bool public function get(string $name) { $gvalue = new GValue(); - if (Config::ffi()-> + if (Config::vips()-> vips_image_get($this->pointer, $name, $gvalue->pointer) != 0) { - Config::error(); + throw new Exception(); } return $gvalue->get(); @@ -1160,7 +1144,7 @@ public function get(string $name) */ public function getType(string $name): int { - return Config::ffi()->vips_image_get_typeof($this->pointer, $name); + return Config::vips()->vips_image_get_typeof($this->pointer, $name); } /** @@ -1218,7 +1202,7 @@ public function set(string $name, $value): void $gvalue->setType($gtype); $gvalue->set($value); - Config::ffi()->vips_image_set($this->pointer, $name, $gvalue->pointer); + Config::vips()->vips_image_set($this->pointer, $name, $gvalue->pointer); } /** @@ -1243,7 +1227,7 @@ public function setType($type, string $name, $value): void $gvalue = new GValue(); $gvalue->setType($type); $gvalue->set($value); - Config::ffi()->vips_image_set($this->pointer, $name, $gvalue->pointer); + Config::vips()->vips_image_set($this->pointer, $name, $gvalue->pointer); } /** @@ -1257,8 +1241,8 @@ public function setType($type, string $name, $value): void */ public function remove(string $name): void { - if (!Config::ffi()->vips_image_remove($this->pointer, $name)) { - Config::error(); + if (!Config::vips()->vips_image_remove($this->pointer, $name)) { + throw new Exception(); } } @@ -1267,7 +1251,7 @@ public function remove(string $name): void * * @return string */ - public function __toString() + public function __toString(): string { $array = [ 'width' => $this->width, diff --git a/src/ImageAutodoc.php b/src/ImageAutodoc.php index f9537ec..531084a 100644 --- a/src/ImageAutodoc.php +++ b/src/ImageAutodoc.php @@ -203,6 +203,8 @@ * @throws Exception * @method string dzsave_buffer(array $options = []) Save image to dz buffer. * @throws Exception + * @method void dzsave_target(string $target, array $options = []) Save image to deepzoom target. + * @throws Exception * @method Image embed(integer $x, integer $y, integer $width, integer $height, array $options = []) Embed an image in a larger image. * @throws Exception * @method Image extract_area(integer $left, integer $top, integer $width, integer $height, array $options = []) Extract an area from an image. @@ -485,9 +487,9 @@ * @throws Exception * @method static Image pngload_source(string $source, array $options = []) Load png from source. * @throws Exception - * @method void pngsave(string $filename, array $options = []) Save image to png file. + * @method void pngsave(string $filename, array $options = []) Save image to file as PNG. * @throws Exception - * @method string pngsave_buffer(array $options = []) Save image to png buffer. + * @method string pngsave_buffer(array $options = []) Save image to buffer as PNG. * @throws Exception * @method void pngsave_target(string $target, array $options = []) Save image to target as PNG. * @throws Exception @@ -643,6 +645,8 @@ * @throws Exception * @method string tiffsave_buffer(array $options = []) Save image to tiff buffer. * @throws Exception + * @method void tiffsave_target(string $target, array $options = []) Save image to tiff target. + * @throws Exception * @method Image tilecache(array $options = []) Cache an image as a set of tiles. * @throws Exception * @method static Image tonelut(array $options = []) Build a look-up table. diff --git a/src/Interpolate.php b/src/Interpolate.php index c70b327..9276b90 100644 --- a/src/Interpolate.php +++ b/src/Interpolate.php @@ -60,10 +60,9 @@ class Interpolate extends VipsObject */ public \FFI\CData $pointer; - public function __construct($pointer) + public function __construct(\FFI\CData $pointer) { - $this->pointer = Config::ffi()-> - cast(Config::ctypes("VipsInterpolate"), $pointer); + $this->pointer = \FFI::cast(Config::ctypes("VipsInterpolate"), $pointer); parent::__construct($pointer); } @@ -81,13 +80,14 @@ public function __construct($pointer) * - `'lbb'`: Use LBB interpolation. * - `'vsqbs'`: Use the VSQBS interpolation. * - * @return resource|null The interpolator, or null on error. + * @return Interpolate The interpolator. + * @throws Exception If unable to make a new interpolator from $name. */ - public static function newFromName($name) + public static function newFromName(string $name): Interpolate { - $pointer = Config::ffi()->vips_interpolate_new($name); + $pointer = Config::vips()->vips_interpolate_new($name); if ($pointer == null) { - Config::error(); + throw new Exception(); } return new Interpolate($pointer); diff --git a/src/Introspect.php b/src/Introspect.php index b4aca43..61a711d 100644 --- a/src/Introspect.php +++ b/src/Introspect.php @@ -57,12 +57,12 @@ class Introspect public string $name; /** - * The operation description (eg. "add two images"). + * The operation description (e.g. "add two images"). */ public string $description; /** - * The operation flags (eg. SEQUENTIAL | DEPRECATED). + * The operation flags (e.g. SEQUENTIAL | DEPRECATED). */ public int $flags; @@ -90,26 +90,28 @@ class Introspect */ public array $method_args; - public function __construct($name) + /** + * @throws Exception + */ + public function __construct($operation_name) { - $this->name = $name; + $this->name = $operation_name; - $operation = VipsOperation::newFromName($name); + $operation = VipsOperation::newFromName($operation_name); $this->description = $operation->getDescription(); - $flags = Config::ffi()->vips_operation_get_flags($operation->pointer); - $p_names = Config::ffi()->new("char**[1]"); - $p_flags = Config::ffi()->new("int*[1]"); - $p_n_args = Config::ffi()->new("int[1]"); - $result = Config::ffi()->vips_object_get_args( + $p_names = Config::vips()->new("char**[1]"); + $p_flags = Config::vips()->new("int*[1]"); + $p_n_args = Config::vips()->new("int[1]"); + $result = Config::vips()->vips_object_get_args( \FFI::cast(Config::ctypes("VipsObject"), $operation->pointer), $p_names, $p_flags, $p_n_args ); if ($result != 0) { - error(); + throw new Exception(); } $p_names = $p_names[0]; $p_flags = $p_flags[0]; @@ -146,9 +148,6 @@ public function __construct($name) foreach ($this->arguments as $name => $details) { $flags = $details["flags"]; - $blurb = $details["blurb"]; - $type = $details["type"]; - $typeName = Config::ffi()->g_type_name($type); if (($flags & ArgumentFlags::INPUT) && ($flags & ArgumentFlags::REQUIRED) && @@ -198,27 +197,25 @@ public function __construct($name) array_splice($this->method_args, $index); } - Utils::debugLog($name, ['introspect' => strval($this)]); + Utils::debugLog($operation_name, ['introspect' => strval($this)]); } - public function __toString() + public function __toString(): string { - $result = ""; - - $result .= "$this->name:\n"; + $result = "$this->name:\n"; foreach ($this->arguments as $name => $details) { $flags = $details["flags"]; $blurb = $details["blurb"]; $type = $details["type"]; - $typeName = Config::ffi()->g_type_name($type); + $typeName = Config::gobject()->g_type_name($type); $result .= " $name:\n"; $result .= " flags: $flags\n"; - foreach (ArgumentFlags::NAMES as $name => $flag) { + foreach (ArgumentFlags::NAMES as $flag_name => $flag) { if ($flags & $flag) { - $result .= " $name\n"; + $result .= " $flag_name\n"; } } diff --git a/src/Utils.php b/src/Utils.php index d5ee068..808af39 100644 --- a/src/Utils.php +++ b/src/Utils.php @@ -69,16 +69,16 @@ public static function debugLog(string $name, array $arguments): void /** * Log an error message. * - * @param string $message The error message. - * @param \Exception $exception The exception. + * @param string $message The error message. + * @param \Exception|null $exception The exception, if any. * * @return void */ - public static function errorLog(string $message, \Exception $exception): void + public static function errorLog(string $message, ?\Exception $exception = null): void { $logger = Config::getLogger(); if ($logger) { - $logger->error($message, ['exception' => $exception]); + $logger->error($message, $exception == null ? [] : ['exception' => $exception]); } } @@ -92,7 +92,25 @@ public static function errorLog(string $message, \Exception $exception): void */ public static function typeFromName(string $name): int { - return Config::ffi()->g_type_from_name($name); + return Config::gobject()->g_type_from_name($name); + } + + public static function filenameGetFilename(string $name): string + { + $pointer = Config::vips()->vips_filename_get_filename($name); + $filename = \FFI::string($pointer); + Config::glib()->g_free($pointer); + + return $filename; + } + + public static function filenameGetOptions(string $name): string + { + $pointer = Config::vips()->vips_filename_get_options($name); + $options = \FFI::string($pointer); + Config::glib()->g_free($pointer); + + return $options; } } diff --git a/src/VipsObject.php b/src/VipsObject.php index 2a027cf..22f5c3b 100644 --- a/src/VipsObject.php +++ b/src/VipsObject.php @@ -66,37 +66,35 @@ abstract class VipsObject extends GObject */ private \FFI\CData $gObject; - public function __construct($pointer) + public function __construct(\FFI\CData $pointer) { - $this->pointer = Config::ffi()-> - cast(Config::ctypes("VipsObject"), $pointer); - $this->gObject = Config::ffi()-> - cast(Config::ctypes("GObject"), $pointer); + $this->pointer = \FFI::cast(Config::ctypes("VipsObject"), $pointer); + $this->gObject = \FFI::cast(Config::ctypes("GObject"), $pointer); parent::__construct($pointer); } // print a table of all active vipsobjects ... handy for debugging - public static function printAll() + public static function printAll(): void { - Config::ffi()->vips_object_print_all(); + Config::vips()->vips_object_print_all(); } - public function getDescription() + public function getDescription(): string { - return Config::ffi()->vips_object_get_description($this->pointer); + return Config::vips()->vips_object_get_description($this->pointer); } // get the pspec for a property // NULL for no such name // very slow! avoid if possible // FIXME add a cache for this thing - public function getPspec(string $name) + public function getPspec(string $name): ?\FFI\CData { - $pspec = Config::ffi()->new("GParamSpec*[1]"); - $argument_class = Config::ffi()->new("VipsArgumentClass*[1]"); - $argument_instance = Config::ffi()->new("VipsArgumentInstance*[1]"); - $result = Config::ffi()->vips_object_get_argument( + $pspec = Config::gobject()->new("GParamSpec*[1]"); + $argument_class = Config::vips()->new("VipsArgumentClass*[1]"); + $argument_instance = Config::vips()->new("VipsArgumentInstance*[1]"); + $result = Config::vips()->vips_object_get_argument( $this->pointer, $name, $pspec, @@ -113,12 +111,12 @@ public function getPspec(string $name) // get the type of a property from a VipsObject // 0 if no such property - public function getType(string $name) + public function getType(string $name): int { $pspec = $this->getPspec($name); if (\FFI::isNull($pspec)) { # need to clear any error, this is horrible - Config::ffi()->vips_error_clear(); + Config::vips()->vips_error_clear(); return 0; } else { return $pspec->value_type; @@ -128,21 +126,24 @@ public function getType(string $name) public function getBlurb(string $name): string { $pspec = $this->getPspec($name); - return Config::ffi()->g_param_spec_get_blurb($pspec); + return Config::gobject()->g_param_spec_get_blurb($pspec); } public function getArgumentDescription(string $name): string { $pspec = $this->getPspec($name); - return Config::ffi()->g_param_spec_get_description($pspec); + return Config::gobject()->g_param_spec_get_description($pspec); } + /** + * @throws Exception + */ public function get(string $name) { $gvalue = new GValue(); $gvalue->setType($this->getType($name)); - Config::ffi()-> + Config::gobject()-> g_object_get_property($this->gObject, $name, $gvalue->pointer); $value = $gvalue->get(); @@ -151,7 +152,10 @@ public function get(string $name) return $value; } - public function set(string $name, $value) + /** + * @throws Exception + */ + public function set(string $name, $value): void { Utils::debugLog("set", [$name => $value]); @@ -159,21 +163,21 @@ public function set(string $name, $value) $gvalue->setType($this->getType($name)); $gvalue->set($value); - Config::ffi()-> + Config::gobject()-> g_object_set_property($this->gObject, $name, $gvalue->pointer); } - public function setString(string $string_options) + public function setString(string $string_options): bool { - $result = Config::ffi()-> + $result = Config::vips()-> vips_object_set_from_string($this->pointer, $string_options); return $result == 0; } - public function unrefOutputs() + public function unrefOutputs(): void { - Config::ffi()->vips_object_unref_outputs($this->pointer); + Config::vips()->vips_object_unref_outputs($this->pointer); } } diff --git a/src/VipsOperation.php b/src/VipsOperation.php index 5148e42..67266e9 100644 --- a/src/VipsOperation.php +++ b/src/VipsOperation.php @@ -64,25 +64,28 @@ class VipsOperation extends VipsObject */ public Introspect $introspect; - public function __construct($pointer) + public function __construct(\FFI\CData $pointer) { - $this->pointer = Config::ffi()-> + $this->pointer = Config::vips()-> cast(Config::ctypes("VipsOperation"), $pointer); parent::__construct($pointer); } - public static function newFromName($name) + /** + * @throws Exception + */ + public static function newFromName($name): VipsOperation { - $pointer = Config::ffi()->vips_operation_new($name); + $pointer = Config::vips()->vips_operation_new($name); if ($pointer == null) { - Config::error(); + throw new Exception(); } return new VipsOperation($pointer); } - public function setMatch($name, $match_image, $value) + public function setMatch($name, $match_image, $value): void { $flags = $this->introspect->arguments[$name]["flags"]; $gtype = $this->introspect->arguments[$name]["type"]; @@ -140,27 +143,6 @@ private static function findInside($predicate, $x) return null; } - /** - * Unwrap an array of stuff ready to pass down to the vips_ layer. We - * swap instances of Image for the ffi pointer. - * - * @param array $result Unwrap this. - * - * @return array $result unwrapped, ready for vips. - * - * @internal - */ - private static function unwrap(array $result): array - { - array_walk_recursive($result, function (&$value) { - if ($value instanceof Image) { - $value = $value->image; - } - }); - - return $result; - } - /** * Is $value a VipsImage. * @@ -211,28 +193,6 @@ private static function wrapResult($result) return $result; } - /** - * Check the result of a vips_ call for an error, and throw an exception - * if we see one. - * - * This won't work for things like __get where a non-array return can be - * a valid return. - * - * @param mixed $result Test this. - * - * @throws Exception - * - * @return void - * - * @internal - */ - private static function errorIsArray($result): void - { - if (!is_array($result)) { - Config::error(); - } - } - /** * Call any vips operation. The final element of $arguments can be * (but doesn't have to be) an array of options to pass to the operation. @@ -286,22 +246,20 @@ public static function callBase( */ $n_required = count($operation->introspect->required_input); $n_supplied = count($arguments); - $used_instance = false; $n_used = 0; foreach ($operation->introspect->required_input as $name) { if ($name == $operation->introspect->member_this) { if (!$instance) { $operation->unrefOutputs(); - Config::error("instance argument not supplied"); + throw new Exception("instance argument not supplied"); } $operation->setMatch($name, $match_image, $instance); - $used_instance = true; } elseif ($n_used < $n_supplied) { $operation->setMatch($name, $match_image, $arguments[$n_used]); $n_used += 1; } else { $operation->unrefOutputs(); - Config::error("$n_required arguments required, " . + throw new Exception("$n_required arguments required, " . "but $n_supplied supplied"); } } @@ -316,7 +274,7 @@ public static function callBase( if ($n_supplied != $n_used) { $operation->unrefOutputs(); - Config::error("$n_required arguments required, " . + throw new Exception("$n_required arguments required, " . "but $n_supplied supplied"); } @@ -335,7 +293,7 @@ public static function callBase( if (!in_array($name, $operation->introspect->optional_input) && !in_array($name, $operation->introspect->optional_output)) { $operation->unrefOutputs(); - Config::error("optional argument '$name' does not exist"); + throw new Exception("optional argument '$name' does not exist"); } $operation->setMatch($name, $match_image, $value); @@ -343,11 +301,11 @@ public static function callBase( /* Build the operation */ - $pointer = Config::ffi()-> + $pointer = Config::vips()-> vips_cache_operation_build($operation->pointer); if ($pointer == null) { $operation->unrefOutputs(); - Config::error(); + throw new Exception(); } $operation = new VipsOperation($pointer); $operation->introspect = self::introspect($operation_name); @@ -377,7 +335,7 @@ public static function callBase( $result = self::wrapResult($result); - Utils::debugLog($name, ['result' => var_export($result, true)]); + Utils::debugLog($operation_name, ['result' => var_export($result, true)]); return $result; } From e2874ce26b88d62ad1917b55226796895ab1ea22 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Mon, 4 Jul 2022 15:56:54 +0100 Subject: [PATCH 038/123] note changes for 2.0.3 release --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a3a719..866a153 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to `:vips` will be documented in this file. +## 2.0.3 - 2022-07-04 + +- Fix on Windows [kleisauke] +- Fix 32-bit support [kleisauke] +- Code cleanups [kleisauke] + ## 2.0.2 - 2022-4-14 - Fix extra optional string args on file open From be4a59d3bb710d7eecdb2725819ff6bdebf44660 Mon Sep 17 00:00:00 2001 From: Kleis Auke Wolthuizen Date: Tue, 19 Jul 2022 19:54:09 +0200 Subject: [PATCH 039/123] Explicitly unset images during `tearDown()` (#150) To facilitate debugging of possible refleaks. --- src/Config.php | 3 +++ tests/ConvenienceTest.php | 6 ++++++ tests/ExceptionTest.php | 11 +++++------ tests/MetaTest.php | 6 ++++++ tests/ShortcutTest.php | 6 ++++++ 5 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/Config.php b/src/Config.php index 5a9294f..91c19ce 100644 --- a/src/Config.php +++ b/src/Config.php @@ -781,6 +781,9 @@ private static function init(): void self::$gobject = \FFI::cdef($gobject_decls, $gobject_libname); self::$vips = \FFI::cdef($vips_decls, $vips_libname); + # Useful for debugging + # self::$vips->vips_leak_set(1); + # force the creation of some types we need self::$vips->vips_blend_mode_get_type(); self::$vips->vips_interpretation_get_type(); diff --git a/tests/ConvenienceTest.php b/tests/ConvenienceTest.php index 34fa4f9..48c7bf7 100644 --- a/tests/ConvenienceTest.php +++ b/tests/ConvenienceTest.php @@ -24,6 +24,12 @@ protected function setUp(): void $this->pixel = $this->image->getpoint(0, 0); } + protected function tearDown(): void + { + unset($this->image); + unset($this->pixel); + } + public function testVipsBandjoin() { $image = Vips\Image::newFromArray([[1, 2, 3], [4, 5, 6]]); diff --git a/tests/ExceptionTest.php b/tests/ExceptionTest.php index 262e519..343b521 100644 --- a/tests/ExceptionTest.php +++ b/tests/ExceptionTest.php @@ -12,16 +12,15 @@ class ExceptionTest extends TestCase */ private $image; - /** - * The original value of pixel (0, 0). - */ - private $pixel; - protected function setUp(): void { $filename = __DIR__ . '/images/img_0076.jpg'; $this->image = Vips\Image::newFromFile($filename); - $this->pixel = $this->image->getpoint(0, 0); + } + + protected function tearDown(): void + { + unset($this->image); } public function testVipsNewFromFileException() diff --git a/tests/MetaTest.php b/tests/MetaTest.php index 9447fe3..8b8bb06 100644 --- a/tests/MetaTest.php +++ b/tests/MetaTest.php @@ -26,6 +26,12 @@ protected function setUp(): void $this->png_image = Vips\Image::newFromFile($png_filename); } + protected function tearDown(): void + { + unset($this->image); + unset($this->png_image); + } + public function testVipsSetGet() { $this->image->poop = 'banana'; diff --git a/tests/ShortcutTest.php b/tests/ShortcutTest.php index a4cc80c..4be1a46 100644 --- a/tests/ShortcutTest.php +++ b/tests/ShortcutTest.php @@ -37,6 +37,12 @@ protected function setUp(): void $this->pixel = $this->image->getpoint(0, 0); } + protected function tearDown(): void + { + unset($this->image); + unset($this->pixel); + } + public function testVipsPow() { $real = self::mapNumeric($this->pixel, function ($value) { From 2abe71c6c1381685cf8e2c235c970e31252fa5d4 Mon Sep 17 00:00:00 2001 From: Kleis Auke Wolthuizen Date: Wed, 20 Jul 2022 11:22:46 +0200 Subject: [PATCH 040/123] Fix `var_dump()` of instances (#151) --- src/Config.php | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/Config.php b/src/Config.php index 91c19ce..629915b 100644 --- a/src/Config.php +++ b/src/Config.php @@ -346,6 +346,20 @@ private static function init(): void typedef int64_t gint64; typedef $gtype GType; + +typedef struct _GData GData; + +typedef struct _GTypeClass GTypeClass; + +typedef struct _GTypeInstance { + GTypeClass *g_class; +} GTypeInstance; + +typedef struct _GObject { + GTypeInstance g_type_instance; + unsigned int ref_count; + GData *qdata; +} GObject; EOS; // GLib declarations @@ -361,14 +375,6 @@ private static function init(): void guint64 data[2]; } GValue; -typedef struct _GData GData; - -typedef struct _GTypeClass GTypeClass; - -typedef struct _GTypeInstance { - GTypeClass *g_class; -} GTypeInstance; - typedef struct _GParamSpec { GTypeInstance g_type_instance; @@ -386,12 +392,6 @@ private static function init(): void unsigned int param_id; } GParamSpec; -typedef struct _GObject { - GTypeInstance g_type_instance; - unsigned int ref_count; - GData *qdata; -} GObject; - const char* g_type_name (GType gtype); GType g_type_from_name (const char* name); @@ -478,8 +478,7 @@ private static function init(): void typedef struct _VipsImage VipsImage; typedef struct _VipsProgress VipsProgress; -// Defined in GObject, just typedef to void* -typedef void* GObject; +// Defined in GObject, just typedef to void typedef void GParamSpec; typedef void GValue; From fad7f0a34ffc1bfac05d9250ecaef4b35fa75159 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Thu, 11 Aug 2022 09:34:04 +0100 Subject: [PATCH 041/123] support use of "-" in arg names php-vips 1.x used to allow "-" as a component separator in arg names, but we forgot to implement this in v2. Thanks andrews05 See https://github.com/libvips/php-vips/issues/153 --- CHANGELOG.md | 4 ++++ install-vips.sh | 25 ------------------------- src/Introspect.php | 4 ++-- src/VipsObject.php | 3 +++ src/VipsOperation.php | 6 +++++- tests/CallTest.php | 16 +++++++++++++++- 6 files changed, 29 insertions(+), 29 deletions(-) delete mode 100755 install-vips.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 866a153..bff6018 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to `:vips` will be documented in this file. +## master + +- allow "-" as a name component separator [andrews05] + ## 2.0.3 - 2022-07-04 - Fix on Windows [kleisauke] diff --git a/install-vips.sh b/install-vips.sh deleted file mode 100755 index db7c4b6..0000000 --- a/install-vips.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash - -version=$VIPS_VERSION -vips_tarball=https://github.com/libvips/libvips/releases/download/v$version/vips-$version.tar.gz - -set -e - -# do we already have the correct vips built? early exit if yes -# we could check the configure params as well I guess -if [ -d "$HOME/vips/bin" ]; then - installed_version=$($HOME/vips/bin/vips --version | awk -F- '{print $2}') - echo "Need vips $version" - echo "Found vips $installed_version" - - if [ "$installed_version" == "$version" ]; then - echo "Using cached vips directory" - exit 0 - fi -fi - -rm -rf $HOME/vips -curl -Ls $vips_tarball | tar xz -cd vips-$version -CXXFLAGS=-D_GLIBCXX_USE_CXX11_ABI=0 ./configure --prefix=$HOME/vips "$@" -make -j`nproc` && make install diff --git a/src/Introspect.php b/src/Introspect.php index 61a711d..f252bc6 100644 --- a/src/Introspect.php +++ b/src/Introspect.php @@ -121,8 +121,8 @@ public function __construct($operation_name) $argumentFlags = []; for ($i = 0; $i < $n_args; $i++) { if (($p_flags[$i] & ArgumentFlags::CONSTRUCT) != 0) { - # libvips uses '-' to separate parts of arg names, but we - # need '_' for php + # make sure we're using "_" to separate arg components, though + # I think libvips is "_" everywhere now $name = \FFI::string($p_names[$i]); $name = str_replace("-", "_", $name); $argumentFlags[$name] = $p_flags[$i]; diff --git a/src/VipsObject.php b/src/VipsObject.php index 22f5c3b..cf7db76 100644 --- a/src/VipsObject.php +++ b/src/VipsObject.php @@ -91,6 +91,7 @@ public function getDescription(): string // FIXME add a cache for this thing public function getPspec(string $name): ?\FFI\CData { + $name = str_replace("-", "_", $name); $pspec = Config::gobject()->new("GParamSpec*[1]"); $argument_class = Config::vips()->new("VipsArgumentClass*[1]"); $argument_instance = Config::vips()->new("VipsArgumentInstance*[1]"); @@ -140,6 +141,7 @@ public function getArgumentDescription(string $name): string */ public function get(string $name) { + $name = str_replace("-", "_", $name); $gvalue = new GValue(); $gvalue->setType($this->getType($name)); @@ -159,6 +161,7 @@ public function set(string $name, $value): void { Utils::debugLog("set", [$name => $value]); + $name = str_replace("-", "_", $name); $gvalue = new GValue(); $gvalue->setType($this->getType($name)); $gvalue->set($value); diff --git a/src/VipsOperation.php b/src/VipsOperation.php index 67266e9..b3f546b 100644 --- a/src/VipsOperation.php +++ b/src/VipsOperation.php @@ -290,6 +290,7 @@ public static function callBase( /* Set optional. */ foreach ($options as $name => $value) { + $name = str_replace("-", "_", $name); if (!in_array($name, $operation->introspect->optional_input) && !in_array($name, $operation->introspect->optional_output)) { $operation->unrefOutputs(); @@ -324,6 +325,7 @@ public static function callBase( */ $option_keys = array_keys($options); foreach ($operation->introspect->optional_output as $name) { + $name = str_replace("-", "_", $name); if (in_array($name, $option_keys)) { $result[$name] = $operation->get($name); } @@ -335,7 +337,9 @@ public static function callBase( $result = self::wrapResult($result); - Utils::debugLog($operation_name, ['result' => var_export($result, true)]); + Utils::debugLog($operation_name, [ + 'result' => var_export($result, true) + ]); return $result; } diff --git a/tests/CallTest.php b/tests/CallTest.php index 1406ec9..99cf97f 100644 --- a/tests/CallTest.php +++ b/tests/CallTest.php @@ -10,13 +10,27 @@ class CallTest extends TestCase public function testVipsCall() { $image = Vips\Image::newFromArray([1, 2, 3]); - $image = $image->embed(10, 20, 3000, 2000, ['extend' => Vips\Extend::COPY]); + $image = $image->embed(10, 20, 3000, 2000, [ + 'extend' => Vips\Extend::COPY + ]); $this->assertEquals($image->width, 3000); $this->assertEquals($image->height, 2000); $this->assertEquals($image->bands, 1); } + public function testVipsCallHyphen() + { + # should work with "-" as well as "_" + $image = Vips\Image::worley(64, 64, [ + "cell-size" => 8 + ]); + + $this->assertEquals($image->width, 64); + $this->assertEquals($image->height, 64); + $this->assertEquals($image->bands, 1); + } + public function testVipsCallStatic() { $image = Vips\Image::black(1, 4, ['bands' => 3]); From e736e51340e69123152d6fd72fb7d1066979b98d Mon Sep 17 00:00:00 2001 From: Kleis Auke Wolthuizen Date: Tue, 11 Oct 2022 10:37:38 +0200 Subject: [PATCH 042/123] Move FFI functions to a separate class (#147) * Move FFI functions to a separate class * Ensure libvips can be found on Apple Silicon Allow users to set the `VIPSHOME` env when libvips is installed in a non-standard path. On macOS, always search in `/opt/homebrew/lib/` as a fallback. In most cases, either the `LD_LIBRARY_PATH` or `DYLD_LIBRARY_PATH` env is preferred. On macOS, however, that no longer works since Apple introduced System Integrity Protection (SIP). See: https://github.com/libvips/php-vips/issues/140. * Prefer the strict equality operator --- src/Config.php | 694 +----------------------------------- src/Exception.php | 4 +- src/FFI.php | 804 ++++++++++++++++++++++++++++++++++++++++++ src/GObject.php | 6 +- src/GValue.php | 142 ++++---- src/Image.php | 56 +-- src/Interpolate.php | 4 +- src/Introspect.php | 14 +- src/Utils.php | 10 +- src/VipsObject.php | 30 +- src/VipsOperation.php | 14 +- 11 files changed, 950 insertions(+), 828 deletions(-) create mode 100644 src/FFI.php diff --git a/src/Config.php b/src/Config.php index 629915b..f7824d7 100644 --- a/src/Config.php +++ b/src/Config.php @@ -58,53 +58,6 @@ class Config */ private static ?LoggerInterface $logger = null; - /** - * The FFI handle we use for the glib binary. - * - * @internal - */ - private static \FFI $glib; - - - /** - * The FFI handle we use for the gobject binary. - * - * @internal - */ - private static \FFI $gobject; - - /** - * The FFI handle we use for the libvips binary. - * - * @internal - */ - private static \FFI $vips; - - /** - * Are the above FFI handles initialized? - * - * @internal - */ - private static bool $ffi_inited = false; - - /** - * The library version number we detect. - * - * @internal - */ - private static int $library_major; - private static int $library_minor; - private static int $library_micro; - - /** - * Look up these once. - * - * @internal - */ - private static array $ctypes; - private static array $gtypes; - private static array $ftypes; - /** * Sets a logger. This can be handy for debugging. For example: * @@ -141,7 +94,7 @@ public static function getLogger(): ?LoggerInterface */ public static function cacheSetMax(int $value): void { - self::vips()->vips_cache_set_max($value); + FFI::vips()->vips_cache_set_max($value); } /** @@ -155,7 +108,7 @@ public static function cacheSetMax(int $value): void */ public static function cacheSetMaxMem(int $value): void { - self::vips()->vips_cache_set_max_mem($value); + FFI::vips()->vips_cache_set_max_mem($value); } /** @@ -168,7 +121,7 @@ public static function cacheSetMaxMem(int $value): void */ public static function cacheSetMaxFiles(int $value): void { - self::vips()->vips_cache_set_max_files($value); + FFI::vips()->vips_cache_set_max_files($value); } /** @@ -182,49 +135,7 @@ public static function cacheSetMaxFiles(int $value): void */ public static function concurrencySet(int $value): void { - self::vips()->vips_concurrency_set($value); - } - - public static function glib(): \FFI - { - self::init(); - - return self::$glib; - } - - public static function gobject(): \FFI - { - self::init(); - - return self::$gobject; - } - - public static function vips(): \FFI - { - self::init(); - - return self::$vips; - } - - public static function ctypes(string $name): \FFI\CType - { - self::init(); - - return self::$ctypes[$name]; - } - - public static function gtypes(string $name): int - { - self::init(); - - return self::$gtypes[$name]; - } - - public static function ftypes(string $name): string - { - self::init(); - - return self::$ftypes[$name]; + FFI::vips()->vips_concurrency_set($value); } /** @@ -235,21 +146,7 @@ public static function ftypes(string $name): string */ public static function version(): string { - self::init(); - - return self::$library_major . "." . - self::$library_minor . "." . - self::$library_micro; - } - - /** - * Shut down libvips. Call this just before process exit. - * - * @return void - */ - public static function shutDown(): void - { - self::vips()->vips_shutdown(); + return FFI::version(); } /** @@ -261,586 +158,7 @@ public static function shutDown(): void */ public static function atLeast(int $x, int $y, int $z = 0): bool { - return self::$library_major > $x || - self::$library_major == $x && self::$library_minor > $y || - self::$library_major == $x && self::$library_minor == $y && self::$library_micro >= $z; - } - - private static function libraryName(string $name, int $abi): string - { - switch (PHP_OS_FAMILY) { - case "Windows": - return "$name-$abi.dll"; - - case "OSX": - case "Darwin": - return "$name.$abi.dylib"; - - default: - // most *nix - return "$name.so.$abi"; - } - } - - private static function init(): void - { - // Already initialized. - if (self::$ffi_inited) { - return; - } - - $vips_libname = self::libraryName("libvips", 42); - if (PHP_OS_FAMILY === "Windows") { - $glib_libname = self::libraryName("libglib-2.0", 0); - $gobject_libname = self::libraryName("libgobject-2.0", 0); - } else { - $glib_libname = $vips_libname; - $gobject_libname = $vips_libname; - } - - Utils::debugLog("init", ["library" => $vips_libname]); - - /* FIXME ... maybe display a helpful message on failure? This will - * probably be the main point of failure. - */ - $vips = \FFI::cdef(<<vips_init(""); - if ($result != 0) { - throw new Exception("libvips error: " . $vips->vips_error_buffer()); - } - Utils::debugLog("init", ["vips_init" => $result]); - - # get the library version number, then we can build the API - self::$library_major = $vips->vips_version(0); - self::$library_minor = $vips->vips_version(1); - self::$library_micro = $vips->vips_version(2); - Utils::debugLog("init", [ - "libvips version" => [ - self::$library_major, - self::$library_minor, - self::$library_micro - ] - ]); - - if (!self::atLeast(8, 7)) { - throw new Exception("your libvips is too old -- " . - "8.7 or later required"); - } - - $is_64bits = PHP_INT_SIZE === 8; - - // GType is the size of a pointer - $gtype = $is_64bits ? "guint64" : "guint32"; - - // Typedefs shared across the libvips, GLib and GObject declarations - $typedefs = <<vips_leak_set(1); - - # force the creation of some types we need - self::$vips->vips_blend_mode_get_type(); - self::$vips->vips_interpretation_get_type(); - self::$vips->vips_operation_flags_get_type(); - self::$vips->vips_band_format_get_type(); - self::$vips->vips_token_get_type(); - self::$vips->vips_saveable_get_type(); - self::$vips->vips_image_type_get_type(); - - // look these up in advance - self::$ctypes = [ - "GObject" => self::$gobject->type("GObject*"), - "GParamSpec" => self::$gobject->type("GParamSpec*"), - "VipsObject" => self::$vips->type("VipsObject*"), - "VipsOperation" => self::$vips->type("VipsOperation*"), - "VipsImage" => self::$vips->type("VipsImage*"), - "VipsInterpolate" => self::$vips->type("VipsInterpolate*"), - ]; - - self::$gtypes = [ - "gboolean" => self::$gobject->g_type_from_name("gboolean"), - "gint" => self::$gobject->g_type_from_name("gint"), - "gint64" => self::$gobject->g_type_from_name("gint64"), - "guint64" => self::$gobject->g_type_from_name("guint64"), - "gdouble" => self::$gobject->g_type_from_name("gdouble"), - "gchararray" => self::$gobject->g_type_from_name("gchararray"), - "VipsRefString" => self::$gobject->g_type_from_name("VipsRefString"), - - "GEnum" => self::$gobject->g_type_from_name("GEnum"), - "GFlags" => self::$gobject->g_type_from_name("GFlags"), - "VipsBandFormat" => self::$gobject->g_type_from_name("VipsBandFormat"), - "VipsBlendMode" => self::$gobject->g_type_from_name("VipsBlendMode"), - "VipsArrayInt" => self::$gobject->g_type_from_name("VipsArrayInt"), - "VipsArrayDouble" => - self::$gobject->g_type_from_name("VipsArrayDouble"), - "VipsArrayImage" => self::$gobject->g_type_from_name("VipsArrayImage"), - "VipsBlob" => self::$gobject->g_type_from_name("VipsBlob"), - - "GObject" => self::$gobject->g_type_from_name("GObject"), - "VipsImage" => self::$gobject->g_type_from_name("VipsImage"), - ]; - - // map vips format names to c type names - self::$ftypes = [ - "char" => "char", - "uchar" => "unsigned char", - "short" => "short", - "ushort" => "unsigned short", - "int" => "int", - "uint" => "unsigned int", - "float" => "float", - "double" => "double", - "complex" => "float", - "dpcomplex" => "double", - ]; - - Utils::debugLog("init", ["done"]); - self::$ffi_inited = true; + return FFI::atLeast($x, $y, $z); } } diff --git a/src/Exception.php b/src/Exception.php index f8279b0..ce5df1d 100644 --- a/src/Exception.php +++ b/src/Exception.php @@ -54,8 +54,8 @@ class Exception extends \Exception public function __construct($message = "", $code = 0, \Throwable $previous = null) { if ($message == "") { - $message = "libvips error: " . Config::vips()->vips_error_buffer(); - Config::vips()->vips_error_clear(); + $message = "libvips error: " . FFI::vips()->vips_error_buffer(); + FFI::vips()->vips_error_clear(); } Utils::errorLog($message); diff --git a/src/FFI.php b/src/FFI.php new file mode 100644 index 0000000..6db84ff --- /dev/null +++ b/src/FFI.php @@ -0,0 +1,804 @@ + + * @copyright 2016 John Cupitt + * @license https://opensource.org/licenses/MIT MIT + * @link https://github.com/jcupitt/php-vips + */ + +namespace Jcupitt\Vips; + +/** + * This class contains the libvips FFI methods. + * + * @category Images + * @package Jcupitt\Vips + * @author John Cupitt + * @copyright 2016 John Cupitt + * @license https://opensource.org/licenses/MIT MIT + * @link https://github.com/jcupitt/php-vips + */ +class FFI +{ + + /** + * The FFI handle we use for the glib binary. + * + * @internal + */ + private static \FFI $glib; + + /** + * The FFI handle we use for the gobject binary. + * + * @internal + */ + private static \FFI $gobject; + + /** + * The FFI handle we use for the libvips binary. + * + * @internal + */ + private static \FFI $vips; + + /** + * Are the above FFI handles initialized? + * + * @internal + */ + private static bool $ffi_inited = false; + + /** + * Look up these once. + * + * @internal + */ + private static array $ctypes; + private static array $gtypes; + private static array $ftypes; + + /** + * The library version number we detect. + * + * @internal + */ + private static int $library_major; + private static int $library_minor; + private static int $library_micro; + + public static function glib(): \FFI + { + self::init(); + + return self::$glib; + } + + public static function gobject(): \FFI + { + self::init(); + + return self::$gobject; + } + + public static function vips(): \FFI + { + self::init(); + + return self::$vips; + } + + public static function ctypes(string $name): \FFI\CType + { + self::init(); + + return self::$ctypes[$name]; + } + + public static function gtypes(string $name): int + { + self::init(); + + return self::$gtypes[$name]; + } + + public static function ftypes(string $name): string + { + self::init(); + + return self::$ftypes[$name]; + } + + /** + * Gets the libvips version number as a string of the form + * MAJOR.MINOR.MICRO, for example "8.6.1". + * + * @return string + */ + public static function version(): string + { + self::init(); + + return self::$library_major . "." . + self::$library_minor . "." . + self::$library_micro; + } + + /** + * Is this at least libvips major.minor[.patch]? + * @param int $x Major component. + * @param int $y Minor component. + * @param int $z Patch component. + * @return bool `true` if at least libvips major.minor[.patch]; otherwise, `false`. + */ + public static function atLeast(int $x, int $y, int $z = 0): bool + { + return self::$library_major > $x || + self::$library_major == $x && self::$library_minor > $y || + self::$library_major == $x && self::$library_minor == $y && self::$library_micro >= $z; + } + + /** + * Shut down libvips. Call this just before process exit. + * + * @return void + */ + public static function shutDown(): void + { + self::vips()->vips_shutdown(); + } + + private static function libraryName(string $name, int $abi): string + { + switch (PHP_OS_FAMILY) { + case "Windows": + return "$name-$abi.dll"; + + case "OSX": + case "Darwin": + return "$name.$abi.dylib"; + + default: + // most *nix + return "$name.so.$abi"; + } + } + + private static function init(): void + { + // Already initialized. + if (self::$ffi_inited) { + return; + } + + $vips_libname = self::libraryName("libvips", 42); + if (PHP_OS_FAMILY === "Windows") { + $glib_libname = self::libraryName("libglib-2.0", 0); + $gobject_libname = self::libraryName("libgobject-2.0", 0); + } else { + $glib_libname = $vips_libname; + $gobject_libname = $vips_libname; + } + + Utils::debugLog("init", ["library" => $vips_libname]); + + $is_64bits = PHP_INT_SIZE === 8; + + $libraryPaths = [ + "" // system library + ]; + + $vipshome = getenv("VIPSHOME"); + if ($vipshome) { + // lib/ predicates lib/ + $libraryPaths[] = $vipshome . ($is_64bits ? "/lib64/" : "/lib32/"); + // lib/ is always searched + $libraryPaths[] = $vipshome . "/lib/"; + } + + if (PHP_OS_FAMILY === "OSX" || + PHP_OS_FAMILY === "Darwin") { + $libraryPaths[] = "/opt/homebrew/lib/"; // Homebrew on Apple Silicon + } + + // attempt to open libraries using the system library search method + // (no prefix) and a couple of fallback paths, if any + $vips = null; + foreach ($libraryPaths as $path) { + Utils::debugLog("init", ["path" => $path]); + + try { + $vips = \FFI::cdef(<< "library load failed", "exception" => $e]); + } + } + + if ($vips === null) { + array_shift($libraryPaths); + + $msg = "Unable to find library '$vips_libname'"; + if (!empty($libraryPaths)) { + $msg .= " in any of ['" . implode("', '", $libraryPaths) . "']"; + } + $msg .= ". Make sure that you've installed libvips and that '$vips_libname'"; + $msg .= " is on your system's library search path."; + throw new Exception($msg); + } + + $result = $vips->vips_init(""); + if ($result != 0) { + throw new Exception("libvips error: " . $vips->vips_error_buffer()); + } + Utils::debugLog("init", ["vips_init" => $result]); + + # get the library version number, then we can build the API + self::$library_major = $vips->vips_version(0); + self::$library_minor = $vips->vips_version(1); + self::$library_micro = $vips->vips_version(2); + Utils::debugLog("init", [ + "libvips version" => [ + self::$library_major, + self::$library_minor, + self::$library_micro + ] + ]); + + if (!self::atLeast(8, 7)) { + throw new Exception("your libvips is too old -- " . + "8.7 or later required"); + } + + // GType is the size of a pointer + $gtype = $is_64bits ? "guint64" : "guint32"; + + // Typedefs shared across the libvips, GLib and GObject declarations + $typedefs = <<vips_leak_set(1); + + # force the creation of some types we need + self::$vips->vips_blend_mode_get_type(); + self::$vips->vips_interpretation_get_type(); + self::$vips->vips_operation_flags_get_type(); + self::$vips->vips_band_format_get_type(); + self::$vips->vips_token_get_type(); + self::$vips->vips_saveable_get_type(); + self::$vips->vips_image_type_get_type(); + + // look these up in advance + self::$ctypes = [ + "GObject" => self::$gobject->type("GObject*"), + "GParamSpec" => self::$gobject->type("GParamSpec*"), + "VipsObject" => self::$vips->type("VipsObject*"), + "VipsOperation" => self::$vips->type("VipsOperation*"), + "VipsImage" => self::$vips->type("VipsImage*"), + "VipsInterpolate" => self::$vips->type("VipsInterpolate*"), + ]; + + self::$gtypes = [ + "gboolean" => self::$gobject->g_type_from_name("gboolean"), + "gint" => self::$gobject->g_type_from_name("gint"), + "gint64" => self::$gobject->g_type_from_name("gint64"), + "guint64" => self::$gobject->g_type_from_name("guint64"), + "gdouble" => self::$gobject->g_type_from_name("gdouble"), + "gchararray" => self::$gobject->g_type_from_name("gchararray"), + "VipsRefString" => self::$gobject->g_type_from_name("VipsRefString"), + + "GEnum" => self::$gobject->g_type_from_name("GEnum"), + "GFlags" => self::$gobject->g_type_from_name("GFlags"), + "VipsBandFormat" => self::$gobject->g_type_from_name("VipsBandFormat"), + "VipsBlendMode" => self::$gobject->g_type_from_name("VipsBlendMode"), + "VipsArrayInt" => self::$gobject->g_type_from_name("VipsArrayInt"), + "VipsArrayDouble" => + self::$gobject->g_type_from_name("VipsArrayDouble"), + "VipsArrayImage" => self::$gobject->g_type_from_name("VipsArrayImage"), + "VipsBlob" => self::$gobject->g_type_from_name("VipsBlob"), + + "GObject" => self::$gobject->g_type_from_name("GObject"), + "VipsImage" => self::$gobject->g_type_from_name("VipsImage"), + ]; + + // map vips format names to c type names + self::$ftypes = [ + "char" => "char", + "uchar" => "unsigned char", + "short" => "short", + "ushort" => "unsigned short", + "int" => "int", + "uint" => "unsigned int", + "float" => "float", + "double" => "double", + "complex" => "float", + "dpcomplex" => "double", + ]; + + Utils::debugLog("init", ["done"]); + self::$ffi_inited = true; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: expandtab sw=4 ts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 + */ diff --git a/src/GObject.php b/src/GObject.php index 46620b3..4ebc500 100644 --- a/src/GObject.php +++ b/src/GObject.php @@ -71,7 +71,7 @@ abstract class GObject */ public function __construct(\FFI\CData $pointer) { - $this->pointer = \FFI::cast(Config::ctypes("GObject"), $pointer); + $this->pointer = \FFI::cast(FFI::ctypes("GObject"), $pointer); } public function __destruct() @@ -86,12 +86,12 @@ public function __clone() public function ref(): void { - Config::gobject()->g_object_ref($this->pointer); + FFI::gobject()->g_object_ref($this->pointer); } public function unref(): void { - Config::gobject()->g_object_unref($this->pointer); + FFI::gobject()->g_object_unref($this->pointer); } // TODO signal marshalling to go in diff --git a/src/GValue.php b/src/GValue.php index 32d28d1..8560b90 100644 --- a/src/GValue.php +++ b/src/GValue.php @@ -46,7 +46,7 @@ class GValue public function __construct() { # allocate a gvalue on the heap, and make it persistent between requests - $this->struct = Config::gobject()->new("GValue", true, true); + $this->struct = FFI::gobject()->new("GValue", true, true); $this->pointer = \FFI::addr($this->struct); # GValue needs to be inited to all zero @@ -60,7 +60,7 @@ public function __construct() public static function toEnum(int $gtype, $value): int { if (is_string($value)) { - $enum_value = Config::vips()-> + $enum_value = FFI::vips()-> vips_enum_from_nick("php-vips", $gtype, $value); if ($enum_value < 0) { throw new Exception(); @@ -78,7 +78,7 @@ public static function toEnum(int $gtype, $value): int */ public static function fromEnum(int $gtype, int $value): string { - $result = Config::vips()->vips_enum_nick($gtype, $value); + $result = FFI::vips()->vips_enum_nick($gtype, $value); if ($result === null) { throw new Exception("value not in enum"); } @@ -88,12 +88,12 @@ public static function fromEnum(int $gtype, int $value): string public function __destruct() { - Config::gobject()->g_value_unset($this->pointer); + FFI::gobject()->g_value_unset($this->pointer); } public function setType(int $gtype): void { - Config::gobject()->g_value_init($this->pointer, $gtype); + FFI::gobject()->g_value_init($this->pointer, $gtype); } public function getType(): int @@ -113,36 +113,36 @@ public function set($value): void $gtype = $this->getType(); switch ($gtype) { - case Config::gtypes("gboolean"): - Config::gobject()->g_value_set_boolean($this->pointer, $value); + case FFI::gtypes("gboolean"): + FFI::gobject()->g_value_set_boolean($this->pointer, $value); break; - case Config::gtypes("gint"): - Config::gobject()->g_value_set_int($this->pointer, $value); + case FFI::gtypes("gint"): + FFI::gobject()->g_value_set_int($this->pointer, $value); break; - case Config::gtypes("gint64"): - Config::gobject()->g_value_set_int64($this->pointer, $value); + case FFI::gtypes("gint64"): + FFI::gobject()->g_value_set_int64($this->pointer, $value); break; - case Config::gtypes("guint64"): - Config::gobject()->g_value_set_uint64($this->pointer, $value); + case FFI::gtypes("guint64"): + FFI::gobject()->g_value_set_uint64($this->pointer, $value); break; - case Config::gtypes("gdouble"): - Config::gobject()->g_value_set_double($this->pointer, $value); + case FFI::gtypes("gdouble"): + FFI::gobject()->g_value_set_double($this->pointer, $value); break; - case Config::gtypes("gchararray"): - Config::gobject()->g_value_set_string($this->pointer, $value); + case FFI::gtypes("gchararray"): + FFI::gobject()->g_value_set_string($this->pointer, $value); break; - case Config::gtypes("VipsRefString"): - Config::vips()-> + case FFI::gtypes("VipsRefString"): + FFI::vips()-> vips_value_set_ref_string($this->pointer, $value); break; - case Config::gtypes("VipsArrayInt"): + case FFI::gtypes("VipsArrayInt"): if (!is_array($value)) { $value = [$value]; } @@ -152,11 +152,11 @@ public function set($value): void for ($i = 0; $i < $n; $i++) { $array[$i] = $value[$i]; } - Config::vips()-> + FFI::vips()-> vips_value_set_array_int($this->pointer, $array, $n); break; - case Config::gtypes("VipsArrayDouble"): + case FFI::gtypes("VipsArrayDouble"): if (!is_array($value)) { $value = [$value]; } @@ -166,17 +166,17 @@ public function set($value): void for ($i = 0; $i < $n; $i++) { $array[$i] = $value[$i]; } - Config::vips()-> + FFI::vips()-> vips_value_set_array_double($this->pointer, $array, $n); break; - case Config::gtypes("VipsArrayImage"): + case FFI::gtypes("VipsArrayImage"): if (!is_array($value)) { $value = [$value]; } $n = count($value); - Config::vips()->vips_value_set_array_image($this->pointer, $n); - $array = Config::vips()-> + FFI::vips()->vips_value_set_array_image($this->pointer, $n); + $array = FFI::vips()-> vips_value_get_array_image($this->pointer, null); for ($i = 0; $i < $n; $i++) { $image = $value[$i]; @@ -185,7 +185,7 @@ public function set($value): void } break; - case Config::gtypes("VipsBlob"): + case FFI::gtypes("VipsBlob"): # we need to set the blob to a copy of the data that vips_lib # can own and free $n = strlen($value); @@ -194,34 +194,34 @@ public function set($value): void for ($i = 0; $i < $n; $i++) { $memory[$i] = $value[$i]; } - Config::vips()-> + FFI::vips()-> vips_value_set_blob_free($this->pointer, $memory, $n); break; default: - $fundamental = Config::gobject()->g_type_fundamental($gtype); + $fundamental = FFI::gobject()->g_type_fundamental($gtype); switch ($fundamental) { - case Config::gtypes("GObject"): - Config::gobject()-> + case FFI::gtypes("GObject"): + FFI::gobject()-> g_value_set_object($this->pointer, $value->pointer); break; - case Config::gtypes("GEnum"): - Config::gobject()->g_value_set_enum( + case FFI::gtypes("GEnum"): + FFI::gobject()->g_value_set_enum( $this->pointer, self::toEnum($gtype, $value) ); break; - case Config::gtypes("GFlags"): + case FFI::gtypes("GFlags"): /* Just set as int. */ - Config::gobject()-> + FFI::gobject()-> g_value_set_flags($this->pointer, $value); break; default: - $typeName = Config::gobject()->g_type_name($gtype); + $typeName = FFI::gobject()->g_type_name($gtype); throw new \BadMethodCallException( "gtype $typeName ($gtype) not implemented" ); @@ -242,48 +242,48 @@ public function get() $result = null; switch ($gtype) { - case Config::gtypes("gboolean"): - $result = Config::gobject()->g_value_get_boolean($this->pointer); + case FFI::gtypes("gboolean"): + $result = FFI::gobject()->g_value_get_boolean($this->pointer); break; - case Config::gtypes("gint"): - $result = Config::gobject()->g_value_get_int($this->pointer); + case FFI::gtypes("gint"): + $result = FFI::gobject()->g_value_get_int($this->pointer); break; - case Config::gtypes("gint64"): - $result = Config::gobject()->g_value_get_int64($this->pointer); + case FFI::gtypes("gint64"): + $result = FFI::gobject()->g_value_get_int64($this->pointer); break; - case Config::gtypes("guint64"): - $result = Config::gobject()->g_value_get_uint64($this->pointer); + case FFI::gtypes("guint64"): + $result = FFI::gobject()->g_value_get_uint64($this->pointer); break; - case Config::gtypes("gdouble"): - $result = Config::gobject()->g_value_get_double($this->pointer); + case FFI::gtypes("gdouble"): + $result = FFI::gobject()->g_value_get_double($this->pointer); break; - case Config::gtypes("gchararray"): - $result = Config::gobject()->g_value_get_string($this->pointer); + case FFI::gtypes("gchararray"): + $result = FFI::gobject()->g_value_get_string($this->pointer); break; - case Config::gtypes("VipsRefString"): - $p_size = Config::vips()->new("size_t[1]"); - $result = Config::vips()-> + case FFI::gtypes("VipsRefString"): + $p_size = FFI::vips()->new("size_t[1]"); + $result = FFI::vips()-> vips_value_get_ref_string($this->pointer, $p_size); # $p_size[0] will be the string length, but assume it's null # terminated break; - case Config::gtypes("VipsImage"): - $pointer = Config::gobject()->g_value_get_object($this->pointer); + case FFI::gtypes("VipsImage"): + $pointer = FFI::gobject()->g_value_get_object($this->pointer); $result = new Image($pointer); // get_object does not increment the ref count $result->ref(); break; - case Config::gtypes("VipsArrayInt"): - $p_len = Config::vips()->new("int[1]"); - $pointer = Config::vips()-> + case FFI::gtypes("VipsArrayInt"): + $p_len = FFI::vips()->new("int[1]"); + $pointer = FFI::vips()-> vips_value_get_array_int($this->pointer, $p_len); $result = []; for ($i = 0; $i < $p_len[0]; $i++) { @@ -291,9 +291,9 @@ public function get() } break; - case Config::gtypes("VipsArrayDouble"): - $p_len = Config::vips()->new("int[1]"); - $pointer = Config::vips()-> + case FFI::gtypes("VipsArrayDouble"): + $p_len = FFI::vips()->new("int[1]"); + $pointer = FFI::vips()-> vips_value_get_array_double($this->pointer, $p_len); $result = []; for ($i = 0; $i < $p_len[0]; $i++) { @@ -301,9 +301,9 @@ public function get() } break; - case Config::gtypes("VipsArrayImage"): - $p_len = Config::vips()->new("int[1]"); - $pointer = Config::vips()-> + case FFI::gtypes("VipsArrayImage"): + $p_len = FFI::vips()->new("int[1]"); + $pointer = FFI::vips()-> vips_value_get_array_image($this->pointer, $p_len); $result = []; for ($i = 0; $i < $p_len[0]; $i++) { @@ -313,31 +313,31 @@ public function get() } break; - case Config::gtypes("VipsBlob"): - $p_len = Config::vips()->new("size_t[1]"); - $pointer = Config::vips()-> + case FFI::gtypes("VipsBlob"): + $p_len = FFI::vips()->new("size_t[1]"); + $pointer = FFI::vips()-> vips_value_get_blob($this->pointer, $p_len); $result = \FFI::string($pointer, $p_len[0]); break; default: - $fundamental = Config::gobject()->g_type_fundamental($gtype); + $fundamental = FFI::gobject()->g_type_fundamental($gtype); switch ($fundamental) { - case Config::gtypes("GEnum"): - $result = Config::gobject()-> + case FFI::gtypes("GEnum"): + $result = FFI::gobject()-> g_value_get_enum($this->pointer); $result = self::fromEnum($gtype, $result); break; - case Config::gtypes("GFlags"): + case FFI::gtypes("GFlags"): /* Just get as int. */ - $result = Config::gobject()-> + $result = FFI::gobject()-> g_value_get_flags($this->pointer); break; default: - $typeName = Config::gobject()->g_type_name($gtype); + $typeName = FFI::gobject()->g_type_name($gtype); throw new \BadMethodCallException( "gtype $typeName ($gtype) not implemented" ); diff --git a/src/Image.php b/src/Image.php index 414198f..5516f15 100644 --- a/src/Image.php +++ b/src/Image.php @@ -494,7 +494,7 @@ class Image extends ImageAutodoc implements \ArrayAccess */ public function __construct(\FFI\CData $pointer) { - $this->pointer = \FFI::cast(Config::ctypes("VipsImage"), $pointer); + $this->pointer = \FFI::cast(FFI::ctypes("VipsImage"), $pointer); parent::__construct($pointer); } @@ -689,7 +689,7 @@ private function callEnum( */ public static function findLoad(string $filename): ?string { - return Config::vips()->vips_foreign_find_load($filename); + return FFI::vips()->vips_foreign_find_load($filename); } /** @@ -734,7 +734,7 @@ public static function newFromFile( */ public static function findLoadBuffer(string $buffer): ?string { - return Config::vips()-> + return FFI::vips()-> vips_foreign_find_load_buffer($buffer, strlen($buffer)); } @@ -805,15 +805,15 @@ public static function newFromArray( } } - $pointer = Config::vips()-> + $pointer = FFI::vips()-> vips_image_new_matrix_from_array($width, $height, $a, $n); if ($pointer == null) { throw new Exception(); } $result = new Image($pointer); - $result->setType(Config::gtypes("gdouble"), 'scale', $scale); - $result->setType(Config::gtypes("gdouble"), 'offset', $offset); + $result->setType(FFI::gtypes("gdouble"), 'scale', $scale); + $result->setType(FFI::gtypes("gdouble"), 'offset', $offset); return $result; } @@ -842,7 +842,7 @@ public static function newFromMemory( * * TODO add a references system instead, see pyvips. */ - $pointer = Config::vips()->vips_image_new_from_memory_copy( + $pointer = FFI::vips()->vips_image_new_from_memory_copy( $data, strlen($data), $width, @@ -918,7 +918,7 @@ public function writeToFile(string $name, array $options = []): void $filename = Utils::filenameGetFilename($name); $string_options = Utils::filenameGetOptions($name); - $saver = Config::vips()->vips_foreign_find_save($filename); + $saver = FFI::vips()->vips_foreign_find_save($filename); if ($saver == "") { throw new Exception(); } @@ -952,7 +952,7 @@ public function writeToBuffer(string $suffix, array $options = []): string $filename = Utils::filenameGetFilename($suffix); $string_options = Utils::filenameGetOptions($suffix); - $saver = Config::vips()->vips_foreign_find_save_buffer($filename); + $saver = FFI::vips()->vips_foreign_find_save_buffer($filename); if ($saver == "") { throw new Exception(); } @@ -978,7 +978,7 @@ public function writeToMemory(): string $ctype = \FFI::arrayType(\FFI::type("size_t"), [1]); $p_size = \FFI::new($ctype); - $pointer = Config::vips()-> + $pointer = FFI::vips()-> vips_image_write_to_memory($this->pointer, $p_size); if ($pointer == null) { throw new Exception(); @@ -987,7 +987,7 @@ public function writeToMemory(): string // string() takes a copy $result = \FFI::string($pointer, $p_size[0]); - Config::glib()->g_free($pointer); + FFI::glib()->g_free($pointer); return $result; } @@ -1020,7 +1020,7 @@ public function writeToArray(): array $ctype = \FFI::arrayType(\FFI::type("size_t"), [1]); $p_size = \FFI::new($ctype); - $pointer = Config::vips()-> + $pointer = FFI::vips()-> vips_image_write_to_memory($this->pointer, $p_size); if ($pointer == null) { throw new Exception(); @@ -1028,7 +1028,7 @@ public function writeToArray(): array // wrap pointer up as a C array of the right type $n = $this->width * $this->height * $this->bands; - $type_name = Config::ftypes($this->format); + $type_name = FFI::ftypes($this->format); $ctype = \FFI::arrayType(\FFI::type($type_name), [$n]); $array = \FFI::cast($ctype, $pointer); @@ -1039,7 +1039,7 @@ public function writeToArray(): array } // the vips result is not PHP memory, so we must free it - Config::glib()->g_free($pointer); + FFI::glib()->g_free($pointer); return $result; } @@ -1060,7 +1060,7 @@ public function writeToArray(): array */ public function copyMemory(): Image { - $pointer = Config::vips()->vips_image_copy_memory($this->pointer); + $pointer = FFI::vips()->vips_image_copy_memory($this->pointer); if ($pointer == null) { throw new Exception(); } @@ -1125,7 +1125,7 @@ public function __isset(string $name): bool public function get(string $name) { $gvalue = new GValue(); - if (Config::vips()-> + if (FFI::vips()-> vips_image_get($this->pointer, $name, $gvalue->pointer) != 0) { throw new Exception(); } @@ -1144,7 +1144,7 @@ public function get(string $name) */ public function getType(string $name): int { - return Config::vips()->vips_image_get_typeof($this->pointer, $name); + return FFI::vips()->vips_image_get_typeof($this->pointer, $name); } /** @@ -1182,27 +1182,27 @@ public function set(string $name, $value): void if ($gtype == 0) { if (is_array($value)) { if (is_int($value[0])) { - $gtype = Config::gtypes("VipsArrayInt"); + $gtype = FFI::gtypes("VipsArrayInt"); } elseif (is_float($value[0])) { - $gtype = Config::gtypes("VipsArrayDouble"); + $gtype = FFI::gtypes("VipsArrayDouble"); } else { - $gtype = Config::gtypes("VipsArrayImage"); + $gtype = FFI::gtypes("VipsArrayImage"); } } elseif (is_int($value)) { - $gtype = Config::gtypes("gint"); + $gtype = FFI::gtypes("gint"); } elseif (is_float($value)) { - $gtype = Config::gtypes("gdouble"); + $gtype = FFI::gtypes("gdouble"); } elseif (is_string($value)) { - $gtype = Config::gtypes("VipsRefString"); + $gtype = FFI::gtypes("VipsRefString"); } else { - $gtype = Config::gtypes("VipsImage"); + $gtype = FFI::gtypes("VipsImage"); } } $gvalue->setType($gtype); $gvalue->set($value); - Config::vips()->vips_image_set($this->pointer, $name, $gvalue->pointer); + FFI::vips()->vips_image_set($this->pointer, $name, $gvalue->pointer); } /** @@ -1227,7 +1227,7 @@ public function setType($type, string $name, $value): void $gvalue = new GValue(); $gvalue->setType($type); $gvalue->set($value); - Config::vips()->vips_image_set($this->pointer, $name, $gvalue->pointer); + FFI::vips()->vips_image_set($this->pointer, $name, $gvalue->pointer); } /** @@ -1241,7 +1241,7 @@ public function setType($type, string $name, $value): void */ public function remove(string $name): void { - if (!Config::vips()->vips_image_remove($this->pointer, $name)) { + if (!FFI::vips()->vips_image_remove($this->pointer, $name)) { throw new Exception(); } } @@ -1920,7 +1920,7 @@ public function composite($other, $mode, array $options = []): Image # composite takes an arrayint, but it's really an array of blend modes # gvalue doesn't know this, so we must do name -> enum value mapping $mode = array_map(function ($x) { - return GValue::toEnum(Config::gtypes("VipsBlendMode"), $x); + return GValue::toEnum(FFI::gtypes("VipsBlendMode"), $x); }, $mode); return VipsOperation::call( diff --git a/src/Interpolate.php b/src/Interpolate.php index 9276b90..cb5eee5 100644 --- a/src/Interpolate.php +++ b/src/Interpolate.php @@ -62,7 +62,7 @@ class Interpolate extends VipsObject public function __construct(\FFI\CData $pointer) { - $this->pointer = \FFI::cast(Config::ctypes("VipsInterpolate"), $pointer); + $this->pointer = \FFI::cast(FFI::ctypes("VipsInterpolate"), $pointer); parent::__construct($pointer); } @@ -85,7 +85,7 @@ public function __construct(\FFI\CData $pointer) */ public static function newFromName(string $name): Interpolate { - $pointer = Config::vips()->vips_interpolate_new($name); + $pointer = FFI::vips()->vips_interpolate_new($name); if ($pointer == null) { throw new Exception(); } diff --git a/src/Introspect.php b/src/Introspect.php index f252bc6..ce3208c 100644 --- a/src/Introspect.php +++ b/src/Introspect.php @@ -101,11 +101,11 @@ public function __construct($operation_name) $this->description = $operation->getDescription(); - $p_names = Config::vips()->new("char**[1]"); - $p_flags = Config::vips()->new("int*[1]"); - $p_n_args = Config::vips()->new("int[1]"); - $result = Config::vips()->vips_object_get_args( - \FFI::cast(Config::ctypes("VipsObject"), $operation->pointer), + $p_names = FFI::vips()->new("char**[1]"); + $p_flags = FFI::vips()->new("int*[1]"); + $p_n_args = FFI::vips()->new("int[1]"); + $result = FFI::vips()->vips_object_get_args( + \FFI::cast(FFI::ctypes("VipsObject"), $operation->pointer), $p_names, $p_flags, $p_n_args @@ -183,7 +183,7 @@ public function __construct($operation_name) $this->member_this = ""; foreach ($this->required_input as $name) { $type = $this->arguments[$name]["type"]; - if ($type == Config::gtypes("VipsImage")) { + if ($type == FFI::gtypes("VipsImage")) { $this->member_this = $name; break; } @@ -208,7 +208,7 @@ public function __toString(): string $flags = $details["flags"]; $blurb = $details["blurb"]; $type = $details["type"]; - $typeName = Config::gobject()->g_type_name($type); + $typeName = FFI::gobject()->g_type_name($type); $result .= " $name:\n"; diff --git a/src/Utils.php b/src/Utils.php index 808af39..31e26fc 100644 --- a/src/Utils.php +++ b/src/Utils.php @@ -92,23 +92,23 @@ public static function errorLog(string $message, ?\Exception $exception = null): */ public static function typeFromName(string $name): int { - return Config::gobject()->g_type_from_name($name); + return FFI::gobject()->g_type_from_name($name); } public static function filenameGetFilename(string $name): string { - $pointer = Config::vips()->vips_filename_get_filename($name); + $pointer = FFI::vips()->vips_filename_get_filename($name); $filename = \FFI::string($pointer); - Config::glib()->g_free($pointer); + FFI::glib()->g_free($pointer); return $filename; } public static function filenameGetOptions(string $name): string { - $pointer = Config::vips()->vips_filename_get_options($name); + $pointer = FFI::vips()->vips_filename_get_options($name); $options = \FFI::string($pointer); - Config::glib()->g_free($pointer); + FFI::glib()->g_free($pointer); return $options; } diff --git a/src/VipsObject.php b/src/VipsObject.php index cf7db76..05abdc6 100644 --- a/src/VipsObject.php +++ b/src/VipsObject.php @@ -68,8 +68,8 @@ abstract class VipsObject extends GObject public function __construct(\FFI\CData $pointer) { - $this->pointer = \FFI::cast(Config::ctypes("VipsObject"), $pointer); - $this->gObject = \FFI::cast(Config::ctypes("GObject"), $pointer); + $this->pointer = \FFI::cast(FFI::ctypes("VipsObject"), $pointer); + $this->gObject = \FFI::cast(FFI::ctypes("GObject"), $pointer); parent::__construct($pointer); } @@ -77,12 +77,12 @@ public function __construct(\FFI\CData $pointer) // print a table of all active vipsobjects ... handy for debugging public static function printAll(): void { - Config::vips()->vips_object_print_all(); + FFI::vips()->vips_object_print_all(); } public function getDescription(): string { - return Config::vips()->vips_object_get_description($this->pointer); + return FFI::vips()->vips_object_get_description($this->pointer); } // get the pspec for a property @@ -92,10 +92,10 @@ public function getDescription(): string public function getPspec(string $name): ?\FFI\CData { $name = str_replace("-", "_", $name); - $pspec = Config::gobject()->new("GParamSpec*[1]"); - $argument_class = Config::vips()->new("VipsArgumentClass*[1]"); - $argument_instance = Config::vips()->new("VipsArgumentInstance*[1]"); - $result = Config::vips()->vips_object_get_argument( + $pspec = FFI::gobject()->new("GParamSpec*[1]"); + $argument_class = FFI::vips()->new("VipsArgumentClass*[1]"); + $argument_instance = FFI::vips()->new("VipsArgumentInstance*[1]"); + $result = FFI::vips()->vips_object_get_argument( $this->pointer, $name, $pspec, @@ -117,7 +117,7 @@ public function getType(string $name): int $pspec = $this->getPspec($name); if (\FFI::isNull($pspec)) { # need to clear any error, this is horrible - Config::vips()->vips_error_clear(); + FFI::vips()->vips_error_clear(); return 0; } else { return $pspec->value_type; @@ -127,13 +127,13 @@ public function getType(string $name): int public function getBlurb(string $name): string { $pspec = $this->getPspec($name); - return Config::gobject()->g_param_spec_get_blurb($pspec); + return FFI::gobject()->g_param_spec_get_blurb($pspec); } public function getArgumentDescription(string $name): string { $pspec = $this->getPspec($name); - return Config::gobject()->g_param_spec_get_description($pspec); + return FFI::gobject()->g_param_spec_get_description($pspec); } /** @@ -145,7 +145,7 @@ public function get(string $name) $gvalue = new GValue(); $gvalue->setType($this->getType($name)); - Config::gobject()-> + FFI::gobject()-> g_object_get_property($this->gObject, $name, $gvalue->pointer); $value = $gvalue->get(); @@ -166,13 +166,13 @@ public function set(string $name, $value): void $gvalue->setType($this->getType($name)); $gvalue->set($value); - Config::gobject()-> + FFI::gobject()-> g_object_set_property($this->gObject, $name, $gvalue->pointer); } public function setString(string $string_options): bool { - $result = Config::vips()-> + $result = FFI::vips()-> vips_object_set_from_string($this->pointer, $string_options); return $result == 0; @@ -180,7 +180,7 @@ public function setString(string $string_options): bool public function unrefOutputs(): void { - Config::vips()->vips_object_unref_outputs($this->pointer); + FFI::vips()->vips_object_unref_outputs($this->pointer); } } diff --git a/src/VipsOperation.php b/src/VipsOperation.php index b3f546b..b1eb86e 100644 --- a/src/VipsOperation.php +++ b/src/VipsOperation.php @@ -66,8 +66,8 @@ class VipsOperation extends VipsObject public function __construct(\FFI\CData $pointer) { - $this->pointer = Config::vips()-> - cast(Config::ctypes("VipsOperation"), $pointer); + $this->pointer = FFI::vips()-> + cast(FFI::ctypes("VipsOperation"), $pointer); parent::__construct($pointer); } @@ -77,7 +77,7 @@ public function __construct(\FFI\CData $pointer) */ public static function newFromName($name): VipsOperation { - $pointer = Config::vips()->vips_operation_new($name); + $pointer = FFI::vips()->vips_operation_new($name); if ($pointer == null) { throw new Exception(); } @@ -91,9 +91,9 @@ public function setMatch($name, $match_image, $value): void $gtype = $this->introspect->arguments[$name]["type"]; if ($match_image != null) { - if ($gtype == Config::gtypes("VipsImage")) { + if ($gtype == FFI::gtypes("VipsImage")) { $value = $match_image->imageize($value); - } elseif ($gtype == Config::gtypes("VipsArrayImage") && + } elseif ($gtype == FFI::gtypes("VipsArrayImage") && is_array($value)) { $new_value = []; foreach ($value as $x) { @@ -155,7 +155,7 @@ private static function findInside($predicate, $x) private static function isImagePointer($value): bool { return $value instanceof \FFI\CData && - \FFI::typeof($value) == Config::ctypes("VipsImage"); + \FFI::typeof($value) == FFI::ctypes("VipsImage"); } /** @@ -302,7 +302,7 @@ public static function callBase( /* Build the operation */ - $pointer = Config::vips()-> + $pointer = FFI::vips()-> vips_cache_operation_build($operation->pointer); if ($pointer == null) { $operation->unrefOutputs(); From c46db5539768b132a58f18c88790541b5ebbe440 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 11 Oct 2022 09:45:25 +0100 Subject: [PATCH 043/123] update for 2.1 --- CHANGELOG.md | 4 +++- README.md | 13 +++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bff6018..bfe47c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,11 @@ All notable changes to `:vips` will be documented in this file. -## master +## 2.1.0 - 2022-10-11 - allow "-" as a name component separator [andrews05] +- split FFI into a separate class [kleisauke] +- improve finding of library binary [jcupitt] ## 2.0.3 - 2022-07-04 diff --git a/README.md b/README.md index 93ecce9..a091cf7 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ to your `composer.json`: ``` "require": { - "jcupitt/vips" : "2.0.0" + "jcupitt/vips" : "2.1.0" } ``` @@ -112,7 +112,7 @@ either 255 or the original image. Note that libvips operators always make new images, they don't modify existing images, so after the line above, `$image` is unchanged. -You use long, double, array and image as parameters. For example: +You can use long, double, array and image as parameters. For example: ```php $image = $image->add(2); @@ -136,7 +136,7 @@ to add two images. Or: $image = $image->add([[1, 2, 3], [4, 5, 6]]); ``` -To make a 2 x 3 image from the array, then add that image to the original. +To make a 3 x 2 image from the array, then add that image to the original. Almost all methods can take an extra final argument: an array of options. For example: @@ -145,7 +145,7 @@ For example: $image->writeToFile("fred.jpg", ["Q" => 90]); ``` -`php-vips` comes [with full API +`php-vips` comes [with API docs](https://libvips.github.io/php-vips/classes/Jcupitt-Vips-Image.html). To regenerate these from your sources, type: @@ -155,8 +155,9 @@ $ vendor/bin/phpdoc And look in `docs/`. -There are around 300 operations in the library, see the vips docs for an -introduction: +Unfortunatly, due to php-doc limitations, these do not list every option +to every operation. For a full API description you need to see the main +libvips documentation: https://libvips.org/API/current From 7a832fded0ea51a19027e334c0bca8a80fefeafe Mon Sep 17 00:00:00 2001 From: Christian Stocker Date: Thu, 13 Oct 2022 10:46:46 +0200 Subject: [PATCH 044/123] Remove mixed here, not supported by PHP 7.4 (#161) --- src/Image.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Image.php b/src/Image.php index 5516f15..d599757 100644 --- a/src/Image.php +++ b/src/Image.php @@ -832,7 +832,7 @@ public static function newFromArray( * @return Image A new Image. */ public static function newFromMemory( - mixed $data, + $data, int $width, int $height, int $bands, From 247ab4f2d4c70f03ed7d98ee8c62a1e80e12e1c7 Mon Sep 17 00:00:00 2001 From: Christian Stocker Date: Thu, 13 Oct 2022 10:48:04 +0200 Subject: [PATCH 045/123] =?UTF-8?q?Avoid=20=E2=80=9CDeprecate=20dynamic=20?= =?UTF-8?q?properties=E2=80=9D=20warning=20in=20PHP=208.2=20(#159)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/generate_phpdoc.py | 2 ++ src/ImageAutodoc.php | 2 ++ 2 files changed, 4 insertions(+) diff --git a/examples/generate_phpdoc.py b/examples/generate_phpdoc.py index 9676c8e..0f2c60f 100755 --- a/examples/generate_phpdoc.py +++ b/examples/generate_phpdoc.py @@ -265,6 +265,8 @@ def add_nickname(gtype, a, b): f.write(' */\n') f.write('abstract class ImageAutodoc extends VipsObject\n') f.write('{\n') + f.write(' abstract public function __set(string $name, $value);\n') + f.write(' abstract public function __get(string $name);\n') f.write('}\n') diff --git a/src/ImageAutodoc.php b/src/ImageAutodoc.php index 531084a..529c834 100644 --- a/src/ImageAutodoc.php +++ b/src/ImageAutodoc.php @@ -703,4 +703,6 @@ */ abstract class ImageAutodoc extends VipsObject { + abstract public function __set(string $name, $value); + abstract public function __get(string $name); } From 9496dd2a36e64dc6a04249200e4e795b22231bde Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sun, 13 Nov 2022 12:37:06 +0000 Subject: [PATCH 046/123] WIP -- work around php-ffi memory leaks (#171) * work around a php-ffi memleak Due to a bug (I think?) in php-ffi we leaked c. 100 bytes for every call to getPspec(). This could really add up in long running programs. Thanks @levmv! See https://github.com/libvips/php-vips/issues/167 * credit levmv in changelog * work around a php-ffi leak in arrayType php-ffi seems to leak if you use arrayType ... this commit switches to string arguments instead * tests pass * better test for disabled ffi see https://github.com/libvips/php-vips/issues/172 --- CHANGELOG.md | 7 +++++++ src/FFI.php | 13 ++++++++++--- src/GValue.php | 9 +++------ src/Image.php | 14 +++++--------- src/VipsObject.php | 31 ++++++++++++++++++++++--------- src/VipsOperation.php | 20 +++++++++++--------- 6 files changed, 58 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bfe47c7..8b215c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to `:vips` will be documented in this file. +## master + +- refactor callBase() for maintainability +- work around a php-ffi memory leak in getPspec() [levmv] +- work around a php-ffi memory leak in arrayType() +- better test for disabled ffi + ## 2.1.0 - 2022-10-11 - allow "-" as a name component separator [andrews05] diff --git a/src/FFI.php b/src/FFI.php index 6db84ff..f7a8277 100644 --- a/src/FFI.php +++ b/src/FFI.php @@ -201,6 +201,13 @@ private static function init(): void return; } + // try experimentally binding a bit of stdio ... if this fails, FFI + // has probably not been installed or enabled, and php will throw a + // useful error message + $stdio = \FFI::cdef(<< vips_image_write_to_memory($this->pointer, $p_size); @@ -1017,8 +1015,7 @@ public function writeToMemory(): string */ public function writeToArray(): array { - $ctype = \FFI::arrayType(\FFI::type("size_t"), [1]); - $p_size = \FFI::new($ctype); + $p_size = \FFI::new("size_t[1]"); $pointer = FFI::vips()-> vips_image_write_to_memory($this->pointer, $p_size); @@ -1027,10 +1024,9 @@ public function writeToArray(): array } // wrap pointer up as a C array of the right type - $n = $this->width * $this->height * $this->bands; $type_name = FFI::ftypes($this->format); - $ctype = \FFI::arrayType(\FFI::type($type_name), [$n]); - $array = \FFI::cast($ctype, $pointer); + $n = $this->width * $this->height * $this->bands; + $array = \FFI::cast("{$type_name}[$n]", $pointer); // copy to PHP memory as a flat array $result = []; diff --git a/src/VipsObject.php b/src/VipsObject.php index 05abdc6..74bb83b 100644 --- a/src/VipsObject.php +++ b/src/VipsObject.php @@ -88,26 +88,39 @@ public function getDescription(): string // get the pspec for a property // NULL for no such name // very slow! avoid if possible - // FIXME add a cache for this thing + // FIXME add a cache for this thing, see code in pyvips public function getPspec(string $name): ?\FFI\CData { $name = str_replace("-", "_", $name); - $pspec = FFI::gobject()->new("GParamSpec*[1]"); - $argument_class = FFI::vips()->new("VipsArgumentClass*[1]"); - $argument_instance = FFI::vips()->new("VipsArgumentInstance*[1]"); + $pspec_array = FFI::gobject()->new("GParamSpec*[1]"); + $argument_class_array = FFI::vips()->new("VipsArgumentClass*[1]"); + $argument_instance_array = FFI::vips()->new("VipsArgumentInstance*[1]"); $result = FFI::vips()->vips_object_get_argument( $this->pointer, $name, - $pspec, - $argument_class, - $argument_instance + $pspec_array, + $argument_class_array, + $argument_instance_array ); if ($result != 0) { - return null; + $pspec = null; } else { - return $pspec[0]; + /* php-ffi seems to leak if we do the obvious $pspec_array[0] to + * get the return result ... instead, we must make a new pointer + * object and copy the value ourselves + * + * the returns values from vips_object_get_argument() are static, + * so this is safe + */ + $pspec = FFI::gobject()->new("GParamSpec*"); + $to_pointer = \FFI::addr($pspec); + $from_pointer = \FFI::addr($pspec_array); + $size = \FFI::sizeof($from_pointer); + \FFI::memcpy($to_pointer, $from_pointer, $size); } + + return $pspec; } // get the type of a property from a VipsObject diff --git a/src/VipsOperation.php b/src/VipsOperation.php index b1eb86e..cf212af 100644 --- a/src/VipsOperation.php +++ b/src/VipsOperation.php @@ -81,8 +81,9 @@ public static function newFromName($name): VipsOperation if ($pointer == null) { throw new Exception(); } + $operation = new VipsOperation($pointer); - return new VipsOperation($pointer); + return $operation; } public function setMatch($name, $match_image, $value): void @@ -223,6 +224,7 @@ public static function callBase( $operation = self::newFromName($operation_name); $operation->introspect = self::introspect($operation_name); + $introspect = $operation->introspect; /* the first image argument is the thing we expand constants to * match ... look inside tables for images, since we may be passing @@ -244,11 +246,11 @@ public static function callBase( * args, using instance if required, and only check the nargs after * this pass. */ - $n_required = count($operation->introspect->required_input); + $n_required = count($introspect->required_input); $n_supplied = count($arguments); $n_used = 0; - foreach ($operation->introspect->required_input as $name) { - if ($name == $operation->introspect->member_this) { + foreach ($introspect->required_input as $name) { + if ($name == $introspect->member_this) { if (!$instance) { $operation->unrefOutputs(); throw new Exception("instance argument not supplied"); @@ -291,8 +293,8 @@ public static function callBase( */ foreach ($options as $name => $value) { $name = str_replace("-", "_", $name); - if (!in_array($name, $operation->introspect->optional_input) && - !in_array($name, $operation->introspect->optional_output)) { + if (!in_array($name, $introspect->optional_input) && + !in_array($name, $introspect->optional_output)) { $operation->unrefOutputs(); throw new Exception("optional argument '$name' does not exist"); } @@ -309,7 +311,7 @@ public static function callBase( throw new Exception(); } $operation = new VipsOperation($pointer); - $operation->introspect = self::introspect($operation_name); + $operation->introspect = $introspect; # TODO .. need to attach input refs to output, see _find_inside in # pyvips @@ -317,14 +319,14 @@ public static function callBase( /* Fetch required output args (and modified input args). */ $result = []; - foreach ($operation->introspect->required_output as $name) { + foreach ($introspect->required_output as $name) { $result[$name] = $operation->get($name); } /* Any optional output args. */ $option_keys = array_keys($options); - foreach ($operation->introspect->optional_output as $name) { + foreach ($introspect->optional_output as $name) { $name = str_replace("-", "_", $name); if (in_array($name, $option_keys)) { $result[$name] = $operation->get($name); From 57983ad1b96185713974d7cffaa87147f3a7dc93 Mon Sep 17 00:00:00 2001 From: Christian Stocker Date: Sun, 13 Nov 2022 13:41:33 +0100 Subject: [PATCH 047/123] Add tests with GitHub actions (#160) * add github tests * Remove Travis tests * incorporate feedback from PR * Rename tests to CI * --no-install-recommends --- .github/workflows/ci.yml | 37 +++++++++++++++++++++++++ .travis.yml | 60 ---------------------------------------- 2 files changed, 37 insertions(+), 60 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..5b7dd18 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,37 @@ +name: CI + +on: [ push, pull_request ] + +jobs: + CI: + name: ${{ matrix.php }} + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + include: + - php: '7.4' + - php: '8.0' + - php: '8.1' + - php: '8.2' + + steps: + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + tools: composer:v2 + coverage: none + + - name: Checkout code + uses: actions/checkout@v3 + + - name: Install vips + run: sudo apt install -y libvips --no-install-recommends + + - name: Install composer dependencies + run: | + composer update --prefer-dist --no-interaction --no-progress --no-ansi + + - name: PHPUnit + run: composer test diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index fb20029..0000000 --- a/.travis.yml +++ /dev/null @@ -1,60 +0,0 @@ -language: php - -dist: bionic - -php: - - 7.3 - - 7.4 - -env: - global: - - VIPS_VERSION=8.10.0 - - PATH=$HOME/vips/bin:$PATH - - LD_LIBRARY_PATH=$HOME/vips/lib:$LD_LIBRARY_PATH - - PKG_CONFIG_PATH=$HOME/vips/lib/pkgconfig:$PKG_CONFIG_PATH - -cache: - apt: true - directories: - - $HOME/.composer/cache - - $HOME/vips - -addons: - apt: - packages: - # main dependencies - - libcfitsio-dev - - libexif-dev - - libexpat1-dev - - libfftw3-dev - - libgif-dev - - libgsf-1-dev - - libgsl-dev - - liblcms2-dev - - libmagickwand-dev - - libmatio-dev - - libnifti-dev - - libopenexr-dev - - libopenslide-dev - - liborc-0.4-dev - - libpango1.0-dev - - libpng-dev - - libpoppler-glib-dev - - librsvg2-dev - - libtiff5-dev - - libwebp-dev - # needed for building libvips from source - - gtk-doc-tools - - gobject-introspection - -before_install: - - bash install-vips.sh - --disable-dependency-tracking - --disable-introspection - --disable-gtk-doc-html - --disable-gtk-doc - - yes '' | pecl install vips - -install: composer install --prefer-dist - -script: composer test From c388e68219d1165fec8d242b8d07779d016477ac Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sun, 13 Nov 2022 12:43:17 +0000 Subject: [PATCH 048/123] remove error_buffer_copy (#163) We had a stray declaration of vips_error_buffer_copy() in FFI.php. This would prevent php-vips starting with libvips before 8.9. Thanks Waschnick. see https://github.com/libvips/php-vips/issues/162 --- CHANGELOG.md | 2 ++ README.md | 2 +- src/FFI.php | 1 - 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b215c0..6184fab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ All notable changes to `:vips` will be documented in this file. ## master +- remove unused vips_error_buffer_copy() declaration to fix compatibility with + libvips before 8.9 [Waschnick] - refactor callBase() for maintainability - work around a php-ffi memory leak in getPspec() [levmv] - work around a php-ffi memory leak in arrayType() diff --git a/README.md b/README.md index a091cf7..4089894 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ to your `composer.json`: php-vips does not yet support preloading, so you need to enable FFI globally. This has some security implications, since anyone who can run php on your -server can use it to make calls off into any native library they can find. +server can use it to call any native library they have access to. Of course if attackers are running their own PHP code on your webserver you are probably already toast, unfortunately. diff --git a/src/FFI.php b/src/FFI.php index f7a8277..0da75ca 100644 --- a/src/FFI.php +++ b/src/FFI.php @@ -443,7 +443,6 @@ private static function init(): void int vips_shutdown (void); const char *vips_error_buffer (void); -char *vips_error_buffer_copy (void); void vips_error_clear (void); void vips_error_freeze (void); void vips_error_thaw (void); From 87e6c974af52ca69a030c64ba2e9477d3a1cb4c2 Mon Sep 17 00:00:00 2001 From: Kleis Auke Wolthuizen Date: Sun, 13 Nov 2022 14:56:05 +0100 Subject: [PATCH 049/123] Update CI badge in README.md (#173) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4089894..4747982 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # PHP binding for libvips -[![Build Status](https://travis-ci.org/libvips/php-vips.svg?branch=master)](https://travis-ci.org/libvips/php-vips) +[![CI](https://github.com/libvips/php-vips/workflows/CI/badge.svg)](https://github.com/libvips/php-vips/actions) `php-vips` is a binding for [libvips](https://github.com/libvips/libvips) 8.7 and later that runs on PHP 7.4 and later. From ff7cb36fde54887ca61647b3ff76598caa69bd25 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sun, 13 Nov 2022 16:59:57 +0000 Subject: [PATCH 050/123] tag as v2.1.1 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6184fab..d55e196 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ All notable changes to `:vips` will be documented in this file. -## master +## 2.1.1 - 2022-11-13 - remove unused vips_error_buffer_copy() declaration to fix compatibility with libvips before 8.9 [Waschnick] From cfe001ceaeb76a9427f69642f34334050c504853 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 24 Jan 2023 15:41:45 +0000 Subject: [PATCH 051/123] fix startup on windows Binding printf() won't work on windows. We should find a better way to test if ffi is enabled. Thanks West14. See https://github.com/libvips/php-vips/issues/183 --- CHANGELOG.md | 4 ++++ examples/addconst.php | 2 +- examples/bench.php | 2 +- examples/class.php | 2 +- examples/sig.php | 2 +- examples/vips-magick.php | 2 +- src/FFI.php | 12 +++++++++--- 7 files changed, 18 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d55e196..acc5eae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to `:vips` will be documented in this file. +## master + +- fix startup on windows [West14] + ## 2.1.1 - 2022-11-13 - remove unused vips_error_buffer_copy() declaration to fix compatibility with diff --git a/examples/addconst.php b/examples/addconst.php index 9722a89..971d757 100755 --- a/examples/addconst.php +++ b/examples/addconst.php @@ -1,7 +1,7 @@ #!/usr/bin/env php Date: Tue, 24 Jan 2023 19:11:31 +0000 Subject: [PATCH 052/123] improve ffi startup again thanks West14 see https://github.com/libvips/php-vips/issues/183 --- CHANGELOG.md | 2 +- examples/composer.json | 2 +- src/FFI.php | 17 ++++++----------- 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index acc5eae..950805b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to `:vips` will be documented in this file. ## master -- fix startup on windows [West14] +- improve FFI startup [West14] ## 2.1.1 - 2022-11-13 diff --git a/examples/composer.json b/examples/composer.json index 2de9b0e..46f0eba 100644 --- a/examples/composer.json +++ b/examples/composer.json @@ -1,5 +1,5 @@ { "require": { - "jcupitt/vips": "2.0.0" + "jcupitt/vips": "2.1.1" } } diff --git a/src/FFI.php b/src/FFI.php index fff9c77..b76c8bf 100644 --- a/src/FFI.php +++ b/src/FFI.php @@ -201,17 +201,12 @@ private static function init(): void return; } - // try experimentally binding a bit of stdio ... if this fails, FFI - // has probably not been installed or enabled, and php will throw a - // useful error message - // - // this won't work on windows since there's no run time linker - // - // FIXME ... find a better way to test if FFI has been enabled - if (PHP_OS_FAMILY !== "Windows") { - $stdio = \FFI::cdef(<< Date: Sun, 26 Feb 2023 16:54:25 +0100 Subject: [PATCH 053/123] feat: Implement true streaming and signal handling --- src/Connection.php | 36 +++++++++++++++++++++++ src/FFI.php | 6 ---- src/GObject.php | 52 ++++++++++++++++++++++++++++++++-- src/VipsObject.php | 2 +- src/VipsSource.php | 58 ++++++++++++++++++++++++++++++++++++++ src/VipsSourceCustom.php | 53 ++++++++++++++++++++++++++++++++++ src/VipsSourceResource.php | 41 +++++++++++++++++++++++++++ src/VipsTarget.php | 38 +++++++++++++++++++++++++ src/VipsTargetCustom.php | 55 ++++++++++++++++++++++++++++++++++++ src/VipsTargetResource.php | 53 ++++++++++++++++++++++++++++++++++ 10 files changed, 384 insertions(+), 10 deletions(-) create mode 100644 src/Connection.php create mode 100644 src/VipsSource.php create mode 100644 src/VipsSourceCustom.php create mode 100644 src/VipsSourceResource.php create mode 100644 src/VipsTarget.php create mode 100644 src/VipsTargetCustom.php create mode 100644 src/VipsTargetResource.php diff --git a/src/Connection.php b/src/Connection.php new file mode 100644 index 0000000..36f7a1d --- /dev/null +++ b/src/Connection.php @@ -0,0 +1,36 @@ +pointer); + $pointer = FFI::vips()->vips_connection_filename($so); + + if (\FFI::isNull($pointer)) { + return null; + } + + return \FFI::string($pointer); + } + + /** + * Make a human-readable name for a connection suitable for error messages. + */ + public function nick(): ?string + { + $so = \FFI::cast(FFI::ctypes('VipsConnection'), $this->pointer); + $pointer = FFI::vips()->vips_connection_nick($so); + + if (\FFI::isNull($pointer)) { + return null; + } + + return \FFI::string($pointer); + } +} diff --git a/src/FFI.php b/src/FFI.php index b76c8bf..fa7a0d0 100644 --- a/src/FFI.php +++ b/src/FFI.php @@ -703,12 +703,6 @@ private static function init(): void VipsSourceCustom* vips_source_custom_new (void); -// FIXME ... these need porting to php-ffi -// extern "Python" gint64 _marshal_read (VipsSource*, -// void*, gint64, void*); -// extern "Python" gint64 _marshal_seek (VipsSource*, -// gint64, int, void*); - typedef struct _VipsTarget { VipsConnection parent_object; diff --git a/src/GObject.php b/src/GObject.php index 4ebc500..c87fd92 100644 --- a/src/GObject.php +++ b/src/GObject.php @@ -38,6 +38,9 @@ namespace Jcupitt\Vips; +use Closure; +use FFI\CData; + /** * This class holds a pointer to a GObject and manages object lifetime. * @@ -55,7 +58,7 @@ abstract class GObject * * @internal */ - private \FFI\CData $pointer; + private CData $pointer; /** * Wrap a GObject around an underlying vips resource. The GObject takes @@ -69,7 +72,7 @@ abstract class GObject * * @internal */ - public function __construct(\FFI\CData $pointer) + public function __construct(CData $pointer) { $this->pointer = \FFI::cast(FFI::ctypes("GObject"), $pointer); } @@ -94,7 +97,50 @@ public function unref(): void FFI::gobject()->g_object_unref($this->pointer); } - // TODO signal marshalling to go in + public function signalConnect(string $name, Closure $callback): void + { + static $marshalers = null; + + if ($marshalers === null) { + $imageProgressCb = static function (CData $vi, CData $progress, CData $handle) { + FFI::gobject()->g_object_ref($vi); + $image = new Image($vi); + $progress = \FFI::cast(FFI::ctypes('VipsProgress'), $progress); + $handle($image, $progress); + }; + $marshalers = ['preeval' => $imageProgressCb, 'eval' => $imageProgressCb, 'posteval' => $imageProgressCb]; + + if (FFI::atLeast(8, 9)) { + $marshalers['read'] = static function (CData $gObject, CData $pointer, int $length, CData $handle): int { + $buffer = \FFI::string($pointer, $length); + return $handle($buffer); + }; + $marshalers['seek'] = static function (CData $gObject, int $offset, int $whence, CData $handle): int { + return $handle($offset, $whence); + }; + $marshalers['write'] = static function (CData $gObject, CData $pointer, int $length, CData $handle): int { + $buffer = \FFI::string($pointer, $length); + return $handle($buffer); + }; + $marshalers['finish'] = static function (CData $gObject, CData $handle): void { + $handle(); + }; + } + + if (FFI::atLeast(8, 13)) { + $marshalers['end'] = static function (CData $gObject, CData $handle): int { + return $handle(); + }; + } + } + + if (!isset($marshalers[$name])) { + throw new Exception("unsupported signal $name"); + } + + $go = \FFI::cast(FFI::ctypes('GObject'), $this->pointer); + FFI::gobject()->g_signal_connect_data($go, $name, $marshalers[$name], $callback, null, 0); + } } /* diff --git a/src/VipsObject.php b/src/VipsObject.php index 74bb83b..e82a0e2 100644 --- a/src/VipsObject.php +++ b/src/VipsObject.php @@ -56,7 +56,7 @@ abstract class VipsObject extends GObject * * @internal */ - private \FFI\CData $pointer; + protected \FFI\CData $pointer; /** * A pointer to the underlying GObject. This is the same as the diff --git a/src/VipsSource.php b/src/VipsSource.php new file mode 100644 index 0000000..0691edb --- /dev/null +++ b/src/VipsSource.php @@ -0,0 +1,58 @@ +vips_source_new_from_descriptor($descriptor); + + if (\FFI::isNull($pointer)) { + throw new Exception("can't create source from descriptor $descriptor"); + } + + return new self($pointer); + } + + /** + * Make a new source from a filename. + * Make a new source that is attached to the named file. For example: + * source = pyvips.Source.new_from_file("myfile.jpg") + * You can pass this source to (for example) :meth:`new_from_source`. + * @throws Exception + */ + public static function newFromFile(string $filename): self + { + $pointer = FFI::vips()->vips_source_new_from_file($filename); + + if (\FFI::isNull($pointer)) { + throw new Exception("can't create source from filename $filename"); + } + + return new self($pointer); + } + + /** + * @TODO Not sure how best to implement this since PHP does not have buffers like Python + * @throws Exception + */ + public static function newFromMemory(string $data): self + { + $pointer = FFI::vips()->vips_source_new_from_memory($data, strlen($data)); + + if (\FFI::isNull($pointer)) { + throw new Exception("can't create source from memory"); + } + + return new self($pointer); + } +} diff --git a/src/VipsSourceCustom.php b/src/VipsSourceCustom.php new file mode 100644 index 0000000..b2841c5 --- /dev/null +++ b/src/VipsSourceCustom.php @@ -0,0 +1,53 @@ +vips_source_custom_new()); + parent::__construct($source); + } + + /** + * Attach a read handler. + * The interface is exactly as io.read() in Python. The handler is given a number + * of bytes to fetch, and should return a bytes-like object containing up + * to that number of bytes. If there is no more data available, it should + * return None. + */ + public function onRead(Closure $callback): void + { + $this->signalConnect('read', static function (string &$buffer) use ($callback): int { + $chunk = $callback(strlen($buffer)); + + if ($chunk === null) { + return 0; + } + + $buffer = substr_replace($buffer, $chunk, 0); + return strlen($chunk); + }); + } + + /** + * Attach a seek handler. + * The interface is the same as fseek, so the handler is passed + * parameters for $offset and $whence with the same meanings. + * However, the handler MUST return the new seek position. A simple way + * to do this is to call ftell() and return that result. + * Seek handlers are optional. If you do not set one, your source will be + * treated as unseekable and libvips will do extra caching. + * $whence in particular: + * 0 => start + * 1 => current position + * 2 => end + */ + public function onSeek(Closure $callback): void + { + $this->signalConnect('seek', $callback); + } +} diff --git a/src/VipsSourceResource.php b/src/VipsSourceResource.php new file mode 100644 index 0000000..f26cae4 --- /dev/null +++ b/src/VipsSourceResource.php @@ -0,0 +1,41 @@ +resource = $resource; + parent::__construct(); + + $this->onRead(static function (int $length) use ($resource): ?string { + return fread($resource, $length) ?: null; + }); + + if (stream_get_meta_data($resource)['seekable']) { + $this->onSeek(static function (int $offset, int $whence) use ($resource): int { + fseek($resource, $offset, $whence); + return ftell($resource); + }); + } + } + + public function __destruct() + { + fclose($this->resource); + parent::__destruct(); // TODO: Change the autogenerated stub + } +} diff --git a/src/VipsTarget.php b/src/VipsTarget.php new file mode 100644 index 0000000..354fd85 --- /dev/null +++ b/src/VipsTarget.php @@ -0,0 +1,38 @@ +vips_target_new_to_descriptor($descriptor); + if (\FFI::isNull($pointer)) { + throw new Exception("can't create output target from descriptor $descriptor"); + } + + return new self($pointer); + } + + public static function newToFile(string $filename): self + { + $pointer = FFI::vips()->vips_target_new_to_file($filename); + + if (\FFI::isNull($pointer)) { + throw new Exception("can't create output target from filename $filename"); + } + + return new self($pointer); + } + + public static function newToMemory(): self + { + $pointer = FFI::vips()->vips_target_new_to_memory(); + + if (\FFI::isNull($pointer)) { + throw new Exception("can't create output target from memory"); + } + + return new self($pointer); + } +} diff --git a/src/VipsTargetCustom.php b/src/VipsTargetCustom.php new file mode 100644 index 0000000..75e3cd2 --- /dev/null +++ b/src/VipsTargetCustom.php @@ -0,0 +1,55 @@ +vips_target_custom_new()); + parent::__construct($pointer); + } + + public function onWrite(Closure $callback): void + { + $this->signalConnect('write', $callback); + } + + public function onRead(Closure $callback): void + { + if (FFI::atLeast(8, 13)) { + $this->signalConnect('read', static function (string &$buffer) use ($callback): int { + $chunk = $callback(strlen($buffer)); + + if ($chunk === null) { + return 0; + } + $buffer = substr_replace($buffer, $chunk, 0); + return strlen($chunk); + }); + } + } + + public function onSeek(Closure $callback): void + { + if (FFI::atLeast(8, 13)) { + $this->signalConnect('seek', $callback); + } + } + + public function onEnd(Closure $callback): void + { + if (FFI::atLeast(8, 13)) { + $this->signalConnect('end', $callback); + } else { + $this->onFinish($callback); + } + } + + public function onFinish(Closure $callback): void + { + $this->signalConnect('finish', $callback); + } +} diff --git a/src/VipsTargetResource.php b/src/VipsTargetResource.php new file mode 100644 index 0000000..712c4ca --- /dev/null +++ b/src/VipsTargetResource.php @@ -0,0 +1,53 @@ +resource = $resource; + parent::__construct(); + + $this->onWrite(static function (string $buffer) use ($resource): int { + return fwrite($resource, $buffer) ?: 0; + }); + + $this->onEnd(static function () use ($resource): void { + fclose($resource); + }); + + $meta = stream_get_meta_data($resource); + // See: https://www.php.net/manual/en/function.fopen.php + if (substr($meta['mode'], -1) === '+') { + $this->onRead(static function (int $length) use ($resource): ?string { + return fread($resource, $length) ?: null; + }); + } + + if ($meta['seekable']) { + $this->onSeek(static function (int $offset, int $whence) use ($resource): int { + fseek($resource, $offset, $whence); + return ftell($resource); + }); + } + } + + public function __destruct() + { + if (is_resource($this->resource)) { + fclose($this->resource); + } + parent::__destruct(); // TODO: Change the autogenerated stub + } +} From a852510fae4418ff56ebec50f644216d284208de Mon Sep 17 00:00:00 2001 From: L3tum <9307432+L3tum@users.noreply.github.com> Date: Sun, 26 Feb 2023 20:25:02 +0100 Subject: [PATCH 054/123] fix: Some cleanup and making pointers usable --- src/Connection.php | 32 ++++++++-------- src/FFI.php | 7 ++++ src/Image.php | 54 ++++++++++++++++++++++++++ src/VipsObject.php | 2 +- src/VipsSource.php | 14 +++++++ src/VipsSourceCustom.php | 12 +++++- src/VipsSourceResource.php | 3 +- src/VipsTarget.php | 14 +++++++ src/VipsTargetCustom.php | 12 +++++- src/VipsTargetResource.php | 3 +- tests/StreamingTest.php | 77 ++++++++++++++++++++++++++++++++++++++ 11 files changed, 207 insertions(+), 23 deletions(-) create mode 100644 tests/StreamingTest.php diff --git a/src/Connection.php b/src/Connection.php index 36f7a1d..ea38166 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -4,19 +4,26 @@ abstract class Connection extends VipsObject { + /** + * A pointer to the underlying Connection. This is the same as the + * GObject, just cast to Connection to help FFI. + * + * @internal + */ + public \FFI\CData $pointer; + + public function __construct(\FFI\CData $pointer) + { + $this->pointer = \FFI::cast(FFI::ctypes('VipsConnection'), $pointer); + parent::__construct($pointer); + } + /** * Get the filename associated with a connection. Return null if there is no associated file. */ public function filename(): ?string { - $so = \FFI::cast(FFI::ctypes('VipsConnection'), $this->pointer); - $pointer = FFI::vips()->vips_connection_filename($so); - - if (\FFI::isNull($pointer)) { - return null; - } - - return \FFI::string($pointer); + return FFI::vips()->vips_connection_filename($this->pointer); } /** @@ -24,13 +31,6 @@ public function filename(): ?string */ public function nick(): ?string { - $so = \FFI::cast(FFI::ctypes('VipsConnection'), $this->pointer); - $pointer = FFI::vips()->vips_connection_nick($so); - - if (\FFI::isNull($pointer)) { - return null; - } - - return \FFI::string($pointer); + return FFI::vips()->vips_connection_nick($this->pointer); } } diff --git a/src/FFI.php b/src/FFI.php index fa7a0d0..c396f25 100644 --- a/src/FFI.php +++ b/src/FFI.php @@ -751,6 +751,11 @@ private static function init(): void "VipsOperation" => self::$vips->type("VipsOperation*"), "VipsImage" => self::$vips->type("VipsImage*"), "VipsInterpolate" => self::$vips->type("VipsInterpolate*"), + "VipsConnection" => self::$vips->type("VipsConnection*"), + "VipsSource" => self::$vips->type("VipsSource*"), + "VipsSourceCustom" => self::$vips->type("VipsSourceCustom*"), + "VipsTarget" => self::$vips->type("VipsTarget*"), + "VipsTargetCustom" => self::$vips->type("VipsTargetCustom*"), ]; self::$gtypes = [ @@ -774,6 +779,8 @@ private static function init(): void "GObject" => self::$gobject->g_type_from_name("GObject"), "VipsImage" => self::$gobject->g_type_from_name("VipsImage"), + + "GCallback" => self::$gobject->g_type_from_name("GCallback"), ]; // map vips format names to c type names diff --git a/src/Image.php b/src/Image.php index bbddf07..a7b53e1 100644 --- a/src/Image.php +++ b/src/Image.php @@ -901,6 +901,38 @@ public function newFromImage($value): Image ]); } + /** + * Find the name of the load operation vips will use to load a VipsSource, for + * example 'VipsForeignLoadJpegSource'. You can use this to work out what + * options to pass to newFromSource(). + * + * @param VipsSource $source The source to test + * @return string|null The name of the load operation, or null. + */ + public static function findLoadSource(VipsSource $source): ?string + { + return FFI::vips()->vips_foreign_find_load_source(\FFI::cast(FFI::ctypes('VipsSource'), $source->pointer)); + } + + /** + * @throws Exception + */ + public static function newFromSource(VipsSource $source, string $string_options = '', array $options = []): self + { + $loader = self::findLoadSource($source); + if ($loader === null) { + throw new Exception('unable to load from source'); + } + + if ($string_options !== '') { + $options = array_merge([ + "string_options" => $string_options, + ], $options); + } + + return VipsOperation::call($loader, null, [$source], $options); + } + /** * Write an image to a file. * @@ -1040,6 +1072,28 @@ public function writeToArray(): array return $result; } + /** + * @throws Exception + */ + public function writeToTarget(VipsTarget $target, string $suffix, array $options = []): void + { + $filename = Utils::filenameGetFilename($suffix); + $string_options = Utils::filenameGetOptions($suffix); + $saver = FFI::vips()->vips_foreign_find_save_target($filename); + + if ($saver === '') { + throw new Exception("can't save to target with filename $filename"); + } + + if ($string_options !== '') { + $options = array_merge([ + "string_options" => $string_options, + ], $options); + } + + VipsOperation::call($saver, $this, [$target], $options); + } + /** * Copy to memory. * diff --git a/src/VipsObject.php b/src/VipsObject.php index e82a0e2..74bb83b 100644 --- a/src/VipsObject.php +++ b/src/VipsObject.php @@ -56,7 +56,7 @@ abstract class VipsObject extends GObject * * @internal */ - protected \FFI\CData $pointer; + private \FFI\CData $pointer; /** * A pointer to the underlying GObject. This is the same as the diff --git a/src/VipsSource.php b/src/VipsSource.php index 0691edb..a382217 100644 --- a/src/VipsSource.php +++ b/src/VipsSource.php @@ -4,6 +4,20 @@ class VipsSource extends Connection { + /** + * A pointer to the underlying VipsSource. This is the same as the + * GObject, just cast to VipsSource to help FFI. + * + * @internal + */ + public \FFI\CData $pointer; + + public function __construct(\FFI\CData $pointer) + { + $this->pointer = \FFI::cast(FFI::ctypes('VipsSource'), $pointer); + parent::__construct($pointer); + } + /** * Make a new source from a file descriptor (a small integer). * Make a new source that is attached to the descriptor. For example: diff --git a/src/VipsSourceCustom.php b/src/VipsSourceCustom.php index b2841c5..fd4f39c 100644 --- a/src/VipsSourceCustom.php +++ b/src/VipsSourceCustom.php @@ -6,10 +6,18 @@ class VipsSourceCustom extends VipsSource { + /** + * A pointer to the underlying VipsSourceCustom. This is the same as the + * GObject, just cast to VipsSourceCustom to help FFI. + * + * @internal + */ + public \FFI\CData $pointer; + public function __construct() { - $source = \FFI::cast(FFI::ctypes('VipsSource'), FFI::vips()->vips_source_custom_new()); - parent::__construct($source); + $this->pointer = FFI::vips()->vips_source_custom_new(); + parent::__construct($this->pointer); } /** diff --git a/src/VipsSourceResource.php b/src/VipsSourceResource.php index f26cae4..6027c01 100644 --- a/src/VipsSourceResource.php +++ b/src/VipsSourceResource.php @@ -12,7 +12,8 @@ class VipsSourceResource extends VipsSourceCustom private $resource; /** - * The resource passed in will become "owned" by this class. On destruction of this class, the resource will be closed. + * The resource passed in will become "owned" by this class. + * On destruction of this class, the resource will be closed. * * @param resource $resource */ diff --git a/src/VipsTarget.php b/src/VipsTarget.php index 354fd85..e7b0687 100644 --- a/src/VipsTarget.php +++ b/src/VipsTarget.php @@ -4,6 +4,20 @@ class VipsTarget extends Connection { + /** + * A pointer to the underlying VipsTarget. This is the same as the + * GObject, just cast to VipsTarget to help FFI. + * + * @internal + */ + public \FFI\CData $pointer; + + public function __construct(\FFI\CData $pointer) + { + $this->pointer = \FFI::cast(FFI::ctypes('VipsTarget'), $pointer); + parent::__construct($pointer); + } + public static function newToDescriptor(int $descriptor): self { $pointer = FFI::vips()->vips_target_new_to_descriptor($descriptor); diff --git a/src/VipsTargetCustom.php b/src/VipsTargetCustom.php index 75e3cd2..5901b72 100644 --- a/src/VipsTargetCustom.php +++ b/src/VipsTargetCustom.php @@ -6,10 +6,18 @@ class VipsTargetCustom extends VipsTarget { + /** + * A pointer to the underlying VipsTargetCustom. This is the same as the + * GObject, just cast to VipsTargetCustom to help FFI. + * + * @internal + */ + public \FFI\CData $pointer; + public function __construct() { - $pointer = \FFI::cast(FFI::ctypes('VipsTarget'), FFI::vips()->vips_target_custom_new()); - parent::__construct($pointer); + $this->pointer = FFI::vips()->vips_target_custom_new(); + parent::__construct($this->pointer); } public function onWrite(Closure $callback): void diff --git a/src/VipsTargetResource.php b/src/VipsTargetResource.php index 712c4ca..57d3dfa 100644 --- a/src/VipsTargetResource.php +++ b/src/VipsTargetResource.php @@ -10,7 +10,8 @@ class VipsTargetResource extends VipsTargetCustom private $resource; /** - * The resource passed in will become "owned" by this class. On destruction of this class, the resource will be closed. + * The resource passed in will become "owned" by this class. + * On destruction of this class, the resource will be closed. * * @param resource $resource */ diff --git a/tests/StreamingTest.php b/tests/StreamingTest.php new file mode 100644 index 0000000..c61882b --- /dev/null +++ b/tests/StreamingTest.php @@ -0,0 +1,77 @@ + fn() => VipsSource::newFromFile(__DIR__ . '/images/img_0076.jpg'), + 'Memory' => fn() => VipsSource::newFromMemory(file_get_contents(__DIR__ . '/images/img_0076.jpg')), + 'Resource' => fn() => new VipsSourceResource(fopen(__DIR__ . '/images/img_0076.jpg', 'rb')) + ]; + $targets = [ + 'File' => fn() => VipsTarget::newToFile(tempnam(sys_get_temp_dir(), 'image')), + 'Memory' => fn() => VipsTarget::newToMemory(), + 'Resource' => fn() => new VipsTargetResource(fopen('php://memory', 'wb+')), + 'Resource(Not Readable)' => fn() => new VipsTargetResource(fopen('php://memory', 'wb')) + ]; + + foreach ($sources as $sourceName => $source) { + foreach ($targets as $targetName => $target) { + yield "$sourceName => $targetName" => [$source(), $target()]; + } + } + } + + /** + * @dataProvider sourceAndTargetProvider + */ + public function testFromSourceToTarget(VipsSource $source, VipsTarget $target): void + { + $image = Image::newFromSource($source); + $image->writeToTarget($target, '.jpg[Q=95]'); + + // Try delete temporary file + if ($target->filename() !== null) { + @unlink($target->filename()); + } + } + + /** + * This test case is extra since it's the easiest to make sure we can "reload" the saved image + */ + public function testFromFileToFile(): void + { + $source = VipsSource::newFromFile(__DIR__ . '/images/img_0076.jpg'); + $target = VipsTarget::newToFile(tempnam(sys_get_temp_dir(), 'image')); + $image = Image::newFromSource($source); + $image->writeToTarget($target, '.jpg[Q=95]'); + + // Make sure we can load the file + $image = Image::newFromFile($target->filename()); + $image->writeToBuffer('.jpg[Q=95]'); + unlink($target->filename()); + } + + public function testFromFileToDescriptor(): void + { + // NOTE(L3tum): There is no way to get a file descriptor in PHP :) + // In theory we could use the known fds like stdin or stdout, + // but that would spam those channels full with an entire image file. + // Because of that I've chosen to omit this test. + } +} From f6287c17506c45b376f50f3bde5fd4cc9cb8712f Mon Sep 17 00:00:00 2001 From: L3tum <9307432+L3tum@users.noreply.github.com> Date: Mon, 27 Feb 2023 16:36:28 +0100 Subject: [PATCH 055/123] fix: Callbacks are working now --- src/FFI.php | 17 +++++- src/GObject.php | 140 ++++++++++++++++++++++++++++++++++----------- src/GValue.php | 4 +- src/VipsSource.php | 7 ++- 4 files changed, 130 insertions(+), 38 deletions(-) diff --git a/src/FFI.php b/src/FFI.php index c396f25..f00d4d3 100644 --- a/src/FFI.php +++ b/src/FFI.php @@ -302,6 +302,7 @@ private static function init(): void typedef int32_t gint32; typedef uint64_t guint64; typedef int64_t gint64; +typedef void* gpointer; typedef $gtype GType; @@ -366,6 +367,7 @@ private static function init(): void void g_value_set_flags (GValue* value, unsigned int f); void g_value_set_string (GValue* value, const char* str); void g_value_set_object (GValue* value, void* object); +void g_value_set_pointer (GValue* value, gpointer pointer); bool g_value_get_boolean (const GValue* value); int g_value_get_int (GValue* value); @@ -376,6 +378,7 @@ private static function init(): void unsigned int g_value_get_flags (GValue* value); const char* g_value_get_string (GValue* value); void* g_value_get_object (GValue* value); +gpointer g_value_get_pointer (GValue* value); typedef struct _GEnumValue { int value; @@ -429,6 +432,17 @@ private static function init(): void int connect_flags); const char* g_param_spec_get_blurb (GParamSpec* psp); + +typedef struct _GClosure GClosure; +typedef void (*marshaler)(struct GClosure* closure, GValue* return_value, int n_param_values, const GValue* param_values, void* invocation_hint, void* marshal_data); +struct _GClosure { + int in_marshal : 1; + int is_invalid : 1; + marshaler marshal; +}; +long g_signal_connect_closure(GObject* object, const char* detailed_signal, GClosure *closure, bool after); +GClosure* g_closure_ref(GClosure* closure); +GClosure* g_closure_new_simple (int sizeof_closure, void* data); EOS; # the whole libvips API, mostly adapted from pyvips @@ -746,6 +760,7 @@ private static function init(): void // look these up in advance self::$ctypes = [ "GObject" => self::$gobject->type("GObject*"), + "GClosure" => self::$gobject->type("GClosure"), "GParamSpec" => self::$gobject->type("GParamSpec*"), "VipsObject" => self::$vips->type("VipsObject*"), "VipsOperation" => self::$vips->type("VipsOperation*"), @@ -780,7 +795,7 @@ private static function init(): void "GObject" => self::$gobject->g_type_from_name("GObject"), "VipsImage" => self::$gobject->g_type_from_name("VipsImage"), - "GCallback" => self::$gobject->g_type_from_name("GCallback"), + "GClosure" => self::$gobject->g_type_from_name("GClosure"), ]; // map vips format names to c type names diff --git a/src/GObject.php b/src/GObject.php index c87fd92..3a24698 100644 --- a/src/GObject.php +++ b/src/GObject.php @@ -67,7 +67,7 @@ abstract class GObject * Don't call this yourself, users should stick to (for example) * Image::newFromFile(). * - * @param FFI\CData $pointer The underlying pointer that this + * @param CData $pointer The underlying pointer that this * object should wrap. * * @internal @@ -99,39 +99,111 @@ public function unref(): void public function signalConnect(string $name, Closure $callback): void { - static $marshalers = null; - - if ($marshalers === null) { - $imageProgressCb = static function (CData $vi, CData $progress, CData $handle) { - FFI::gobject()->g_object_ref($vi); - $image = new Image($vi); - $progress = \FFI::cast(FFI::ctypes('VipsProgress'), $progress); - $handle($image, $progress); + $imageProgressCb = static function ( + CData $gClosure, + ?CData $returnValue, + int $numberOfParams, + CData $params, + CData $hint, + ?CData $data + ) use ($callback) { + assert($numberOfParams === 3); + /** + * Marshal-Signature: void(VipsImage*, void*, void*) + */ + $vi = \FFI::cast(FFI::ctypes('GObject'), FFI::gobject()->g_value_get_pointer(\FFI::addr($params[1]))); + FFI::gobject()->g_object_ref($vi); + $image = new Image($vi); + $pr = \FFI::cast(FFI::ctypes('VipsProgress'), FFI::gobject()->g_value_get_pointer(\FFI::addr($params[2]))); + $callback($image, $pr); + }; + $marshalers = ['preeval' => $imageProgressCb, 'eval' => $imageProgressCb, 'posteval' => $imageProgressCb]; + + if (FFI::atLeast(8, 9)) { + $marshalers['read'] = static function ( + CData $gClosure, + CData $returnValue, + int $numberOfParams, + CData $params, + CData $hint, + ?CData $data + ) use (&$callback): void { + assert($numberOfParams === 4); + /* + * Marshal-Signature: gint64(VipsSourceCustom*, void*, gint64, void*) + */ + $bufferPointer = FFI::gobject()->g_value_get_pointer(\FFI::addr($params[1])); + $bufferLength = (int) FFI::gobject()->g_value_get_int64(\FFI::addr($params[2])); + $buffer = \FFI::string($bufferPointer, $bufferLength); + $returnBufferLength = $callback($buffer); + \FFI::memcpy($bufferPointer, $buffer, $returnBufferLength); + FFI::gobject()->g_value_set_int64($returnValue, $returnBufferLength); + FFI::gobject()->g_value_set_pointer(\FFI::addr($params[1]), $bufferPointer); }; - $marshalers = ['preeval' => $imageProgressCb, 'eval' => $imageProgressCb, 'posteval' => $imageProgressCb]; - - if (FFI::atLeast(8, 9)) { - $marshalers['read'] = static function (CData $gObject, CData $pointer, int $length, CData $handle): int { - $buffer = \FFI::string($pointer, $length); - return $handle($buffer); - }; - $marshalers['seek'] = static function (CData $gObject, int $offset, int $whence, CData $handle): int { - return $handle($offset, $whence); - }; - $marshalers['write'] = static function (CData $gObject, CData $pointer, int $length, CData $handle): int { - $buffer = \FFI::string($pointer, $length); - return $handle($buffer); - }; - $marshalers['finish'] = static function (CData $gObject, CData $handle): void { - $handle(); - }; - } + $marshalers['seek'] = static function ( + CData $gClosure, + CData $returnValue, + int $numberOfParams, + CData $params, + CData $hint, + ?CData $data + ) use (&$callback): void { + assert($numberOfParams === 4); + /* + * Marshal-Signature: gint64(VipsSourceCustom*, gint64, int, void*) + */ + $offset = (int) FFI::gobject()->g_value_get_int64(\FFI::addr($params[1])); + $whence = (int) FFI::gobject()->g_value_get_int(\FFI::addr($params[2])); + FFI::gobject()->g_value_set_int64($returnValue, $callback($offset, $whence)); + }; + $marshalers['write'] = static function ( + CData $gClosure, + CData $returnValue, + int $numberOfParams, + CData $params, + CData $hint, + ?CData $data + ) use (&$callback): void { + assert($numberOfParams === 4); + /* + * Marshal-Signature: gint64(VipsTargetCustom*, void*, gint64, void*) + */ + $bufferPointer = FFI::gobject()->g_value_get_pointer(\FFI::addr($params[1])); + $bufferLength = (int) FFI::gobject()->g_value_get_int64(\FFI::addr($params[2])); + $buffer = \FFI::string($bufferPointer, $bufferLength); + FFI::gobject()->g_value_set_int64($returnValue, $callback($buffer)); + }; + $marshalers['finish'] = static function ( + CData $gClosure, + ?CData $returnValue, + int $numberOfParams, + CData $params, + CData $hint, + ?CData $data + ) use (&$callback): void { + assert($numberOfParams === 2); + /** + * Marshal-Signature: void(VipsTargetCustom*, void*) + */ + $callback(); + }; + } - if (FFI::atLeast(8, 13)) { - $marshalers['end'] = static function (CData $gObject, CData $handle): int { - return $handle(); - }; - } + if (FFI::atLeast(8, 13)) { + $marshalers['end'] = static function ( + CData $gClosure, + CData $returnValue, + int $numberOfParams, + CData $params, + CData $hint, + ?CData $data + ) use (&$callback): void { + assert($numberOfParams === 2); + /** + * Marshal-Signature: int(VipsTargetCustom*, void*) + */ + FFI::gobject()->g_value_set_int($returnValue, $callback()); + }; } if (!isset($marshalers[$name])) { @@ -139,7 +211,9 @@ public function signalConnect(string $name, Closure $callback): void } $go = \FFI::cast(FFI::ctypes('GObject'), $this->pointer); - FFI::gobject()->g_signal_connect_data($go, $name, $marshalers[$name], $callback, null, 0); + $gc = FFI::gobject()->g_closure_new_simple(64, null); + $gc->marshal = $marshalers[$name]; + FFI::gobject()->g_signal_connect_closure($go, $name, $gc, 0); } } diff --git a/src/GValue.php b/src/GValue.php index 6478352..c310e6a 100644 --- a/src/GValue.php +++ b/src/GValue.php @@ -188,9 +188,7 @@ public function set($value): void # can own and free $n = strlen($value); $memory = \FFI::new("char[$n]", false, true); - for ($i = 0; $i < $n; $i++) { - $memory[$i] = $value[$i]; - } + \FFI::memcpy($memory, $value, $n); FFI::vips()-> vips_value_set_blob_free($this->pointer, $memory, $n); break; diff --git a/src/VipsSource.php b/src/VipsSource.php index a382217..7bb4e17 100644 --- a/src/VipsSource.php +++ b/src/VipsSource.php @@ -61,7 +61,12 @@ public static function newFromFile(string $filename): self */ public static function newFromMemory(string $data): self { - $pointer = FFI::vips()->vips_source_new_from_memory($data, strlen($data)); + # we need to set the memory to a copy of the data that vips_lib + # can own and free + $n = strlen($data); + $memory = \FFI::new("char[$n]", false, true); + \FFI::memcpy($memory, $data, $n); + $pointer = FFI::vips()->vips_source_new_from_memory($memory, $n); if (\FFI::isNull($pointer)) { throw new Exception("can't create source from memory"); From 1a5f7f003faf07a31e1677708f5a5d7c9f02f370 Mon Sep 17 00:00:00 2001 From: L3tum <9307432+L3tum@users.noreply.github.com> Date: Mon, 27 Feb 2023 20:23:47 +0100 Subject: [PATCH 056/123] fix: PHP already converts null fix: Buffer needs to be copied over fix: sizeof(GClosure) works now feat: Added more tests and an example --- examples/streaming.php | 12 ++++++++++ src/FFI.php | 53 +++++++++++++++++++++++++++++++++++++---- src/GObject.php | 7 +++--- src/VipsSource.php | 6 ++--- src/VipsTarget.php | 15 +++++++++--- tests/StreamingTest.php | 19 +++++++++++++++ 6 files changed, 98 insertions(+), 14 deletions(-) create mode 100644 examples/streaming.php diff --git a/examples/streaming.php b/examples/streaming.php new file mode 100644 index 0000000..585185d --- /dev/null +++ b/examples/streaming.php @@ -0,0 +1,12 @@ +#!/usr/bin/env php +writeToTarget($target, '.jpg[Q=95]'); diff --git a/src/FFI.php b/src/FFI.php index f00d4d3..6a76d9f 100644 --- a/src/FFI.php +++ b/src/FFI.php @@ -434,11 +434,54 @@ private static function init(): void const char* g_param_spec_get_blurb (GParamSpec* psp); typedef struct _GClosure GClosure; -typedef void (*marshaler)(struct GClosure* closure, GValue* return_value, int n_param_values, const GValue* param_values, void* invocation_hint, void* marshal_data); -struct _GClosure { - int in_marshal : 1; - int is_invalid : 1; - marshaler marshal; +typedef void (*marshaler)( + struct GClosure* closure, + GValue* return_value, + int n_param_values, + const GValue* param_values, + void* invocation_hint, + void* marshal_data +); + +typedef struct _GClosureNotifyData GClosureNotifyData; +struct _GClosureNotifyData +{ + void* data; + GClosureNotify notify; +}; +struct _GClosure +{ + /*< private >*/ + int ref_count : 15; /* (atomic) */ + /* meta_marshal is not used anymore but must be zero for historical reasons + as it was exposed in the G_CLOSURE_N_NOTIFIERS macro */ + int meta_marshal_nouse : 1; /* (atomic) */ + int n_guards : 1; /* (atomic) */ + int n_fnotifiers : 2; /* finalization notifiers (atomic) */ + int n_inotifiers : 8; /* invalidation notifiers (atomic) */ + int in_inotify : 1; /* (atomic) */ + int floating : 1; /* (atomic) */ + /*< protected >*/ + int derivative_flag : 1; /* (atomic) */ + /*< public >*/ + int in_marshal : 1; /* (atomic) */ + int is_invalid : 1; /* (atomic) */ + + /*< private >*/ marshaler marshal; + /*< protected >*/ void* data; + + /*< private >*/ GClosureNotifyData *notifiers; + + /* invariants/constraints: + * - ->marshal and ->data are _invalid_ as soon as ->is_invalid==TRUE + * - invocation of all inotifiers occurs prior to fnotifiers + * - order of inotifiers is random + * inotifiers may _not_ free/invalidate parameter values (e.g. ->data) + * - order of fnotifiers is random + * - each notifier may only be removed before or during its invocation + * - reference counting may only happen prior to fnotify invocation + * (in that sense, fnotifiers are really finalization handlers) + */ }; long g_signal_connect_closure(GObject* object, const char* detailed_signal, GClosure *closure, bool after); GClosure* g_closure_ref(GClosure* closure); diff --git a/src/GObject.php b/src/GObject.php index 3a24698..45bd964 100644 --- a/src/GObject.php +++ b/src/GObject.php @@ -138,7 +138,6 @@ public function signalConnect(string $name, Closure $callback): void $returnBufferLength = $callback($buffer); \FFI::memcpy($bufferPointer, $buffer, $returnBufferLength); FFI::gobject()->g_value_set_int64($returnValue, $returnBufferLength); - FFI::gobject()->g_value_set_pointer(\FFI::addr($params[1]), $bufferPointer); }; $marshalers['seek'] = static function ( CData $gClosure, @@ -171,7 +170,9 @@ public function signalConnect(string $name, Closure $callback): void $bufferPointer = FFI::gobject()->g_value_get_pointer(\FFI::addr($params[1])); $bufferLength = (int) FFI::gobject()->g_value_get_int64(\FFI::addr($params[2])); $buffer = \FFI::string($bufferPointer, $bufferLength); - FFI::gobject()->g_value_set_int64($returnValue, $callback($buffer)); + $returnBufferLength = $callback($buffer); + \FFI::memcpy($bufferPointer, $buffer, $returnBufferLength); + FFI::gobject()->g_value_set_int64($returnValue, $returnBufferLength); }; $marshalers['finish'] = static function ( CData $gClosure, @@ -211,7 +212,7 @@ public function signalConnect(string $name, Closure $callback): void } $go = \FFI::cast(FFI::ctypes('GObject'), $this->pointer); - $gc = FFI::gobject()->g_closure_new_simple(64, null); + $gc = FFI::gobject()->g_closure_new_simple(\FFI::sizeof(FFI::ctypes('GClosure')), null); $gc->marshal = $marshalers[$name]; FFI::gobject()->g_signal_connect_closure($go, $name, $gc, 0); } diff --git a/src/VipsSource.php b/src/VipsSource.php index 7bb4e17..ee3ef71 100644 --- a/src/VipsSource.php +++ b/src/VipsSource.php @@ -30,7 +30,7 @@ public static function newFromDescriptor(int $descriptor): self { $pointer = FFI::vips()->vips_source_new_from_descriptor($descriptor); - if (\FFI::isNull($pointer)) { + if ($pointer === null) { throw new Exception("can't create source from descriptor $descriptor"); } @@ -48,7 +48,7 @@ public static function newFromFile(string $filename): self { $pointer = FFI::vips()->vips_source_new_from_file($filename); - if (\FFI::isNull($pointer)) { + if ($pointer === null) { throw new Exception("can't create source from filename $filename"); } @@ -68,7 +68,7 @@ public static function newFromMemory(string $data): self \FFI::memcpy($memory, $data, $n); $pointer = FFI::vips()->vips_source_new_from_memory($memory, $n); - if (\FFI::isNull($pointer)) { + if ($pointer === null) { throw new Exception("can't create source from memory"); } diff --git a/src/VipsTarget.php b/src/VipsTarget.php index e7b0687..a67e663 100644 --- a/src/VipsTarget.php +++ b/src/VipsTarget.php @@ -18,32 +18,41 @@ public function __construct(\FFI\CData $pointer) parent::__construct($pointer); } + /** + * @throws Exception + */ public static function newToDescriptor(int $descriptor): self { $pointer = FFI::vips()->vips_target_new_to_descriptor($descriptor); - if (\FFI::isNull($pointer)) { + if ($pointer === null) { throw new Exception("can't create output target from descriptor $descriptor"); } return new self($pointer); } + /** + * @throws Exception + */ public static function newToFile(string $filename): self { $pointer = FFI::vips()->vips_target_new_to_file($filename); - if (\FFI::isNull($pointer)) { + if ($pointer === null) { throw new Exception("can't create output target from filename $filename"); } return new self($pointer); } + /** + * @throws Exception + */ public static function newToMemory(): self { $pointer = FFI::vips()->vips_target_new_to_memory(); - if (\FFI::isNull($pointer)) { + if ($pointer === null) { throw new Exception("can't create output target from memory"); } diff --git a/tests/StreamingTest.php b/tests/StreamingTest.php index c61882b..741ee74 100644 --- a/tests/StreamingTest.php +++ b/tests/StreamingTest.php @@ -67,6 +67,25 @@ public function testFromFileToFile(): void unlink($target->filename()); } + public function testNoLeak(): void + { + $lastUsage = 0; + for ($i = 0; $i < 10; $i++) { + $filename = tempnam(sys_get_temp_dir(), 'image'); + $source = new VipsSourceResource(fopen(__DIR__ . '/images/img_0076.jpg', 'rb')); + $target = new VipsTargetResource(fopen($filename, 'wb+')); + $image = Image::newFromSource($source); + $image->writeToTarget($target, '.jpg[Q=95]'); + unlink($filename); + $usage = memory_get_peak_usage(true); + $diff = $usage - $lastUsage; + if ($lastUsage !== 0 && $diff > 0) { + echo "LEAK LEAK LEAK" . PHP_EOL; + } + $lastUsage = $usage; + } + } + public function testFromFileToDescriptor(): void { // NOTE(L3tum): There is no way to get a file descriptor in PHP :) From 2feeb9ac08255ad7c255f54c2c79446e71f3b79e Mon Sep 17 00:00:00 2001 From: L3tum <9307432+L3tum@users.noreply.github.com> Date: Tue, 28 Feb 2023 15:36:29 +0100 Subject: [PATCH 057/123] chore: Generate autodoc methods for source/target feat: Add benchmark example --- examples/generate_phpdoc.py | 8 +- examples/streaming-bench.php | 131 ++++++++++++++++++++++++++++++++ src/ForeignDzLayout.php | 1 - src/GsfOutputCsvQuotingMode.php | 54 +++++++++++++ src/ImageAutodoc.php | 88 ++++++++------------- src/OperationMath.php | 6 -- src/OperationMath2.php | 1 - tests/StreamingTest.php | 5 +- 8 files changed, 225 insertions(+), 69 deletions(-) create mode 100644 examples/streaming-bench.php create mode 100644 src/GsfOutputCsvQuotingMode.php diff --git a/examples/generate_phpdoc.py b/examples/generate_phpdoc.py index 0f2c60f..006a34e 100755 --- a/examples/generate_phpdoc.py +++ b/examples/generate_phpdoc.py @@ -31,7 +31,9 @@ GValue.array_int_type: 'integer[]|integer', GValue.array_double_type: 'float[]|float', GValue.array_image_type: 'Image[]|Image', - GValue.blob_type: 'string' + GValue.blob_type: 'string', + GValue.source_type: 'VipsSource', + GValue.target_type: 'VipsTarget' } # php result type names are different, annoyingly, and very restricted @@ -48,7 +50,9 @@ GValue.array_int_type: 'array', GValue.array_double_type: 'array', GValue.array_image_type: 'array', - GValue.blob_type: 'string' + GValue.blob_type: 'string', + GValue.source_type: 'VipsSource', + GValue.target_type: 'VipsTarget' } # values for VipsArgumentFlags diff --git a/examples/streaming-bench.php b/examples/streaming-bench.php new file mode 100644 index 0000000..cbf22f1 --- /dev/null +++ b/examples/streaming-bench.php @@ -0,0 +1,131 @@ +#!/usr/bin/env php + 'sequential']; + $sourceOptionString = 'access=sequential'; + $iterations = 100; + $targetWidth = 100.0; + $targetSuffix = '.jpg'; + $targetOptions = ['optimize-coding' => true, 'strip' => true, 'Q' => 100, 'profile' => 'srgb']; + $targetFile = dirname(__DIR__) . "/tests/images/target.jpg"; + $sourceFile = dirname(__DIR__) . '/tests/images/img_0076.jpg'; + +### Callbacks + $start = microtime(true); + + for ($i = 0; $i < $iterations; $i++) { + $source = new VipsSourceResource(fopen($sourceFile, 'rb')); + $target = new VipsTargetResource(fopen($targetFile, 'wb+')); + $image = Image::newFromSource($source, '', $sourceOptions); + $image = $image->resize($targetWidth / $image->width); + $image->writeToTarget( + $target, + $targetSuffix, + $targetOptions + ); + unlink($targetFile); + } + + echo (microtime(true) - $start) . ' Seconds for Streaming with callbacks' . PHP_EOL; + +### Builtin + $start = microtime(true); + + for ($i = 0; $i < $iterations; $i++) { + $source = VipsSource::newFromFile($sourceFile); + $target = VipsTarget::newToFile($targetFile); + $image = Image::newFromSource($source, '', $sourceOptions); + $image = $image->resize($targetWidth / $image->width); + $image->writeToTarget( + $target, + $targetSuffix, + $targetOptions + ); + unlink($targetFile); + } + + echo (microtime(true) - $start) . ' Seconds for Streaming with builtin source/target' . PHP_EOL; + +### Callbacks Thumbnail + $start = microtime(true); + + for ($i = 0; $i < $iterations; $i++) { + $source = new VipsSourceResource(fopen($sourceFile, 'rb')); + $target = new VipsTargetResource(fopen($targetFile, 'wb+')); + $image = Image::thumbnail_source($source, $targetWidth); + $image->writeToTarget( + $target, + $targetSuffix, + $targetOptions + ); + unlink($targetFile); + } + + echo (microtime(true) - $start) . ' Seconds for Streaming Thumbnail with callbacks' . PHP_EOL; + +### Builtin Thumbnail + $start = microtime(true); + + for ($i = 0; $i < $iterations; $i++) { + $source = VipsSource::newFromFile($sourceFile); + $target = VipsTarget::newToFile($targetFile); + $image = Image::thumbnail_source($source, $targetWidth); + $image->writeToTarget( + $target, + $targetSuffix, + $targetOptions + ); + unlink($targetFile); + } + + echo (microtime(true) - $start) . ' Seconds for Streaming Thumbnail with builtin source/target' . PHP_EOL; + +### Thumbnail + $start = microtime(true); + + for ($i = 0; $i < $iterations; $i++) { + $image = Image::thumbnail($sourceFile . "[$sourceOptionString]", $targetWidth); + $image->writeToFile( + $targetFile, + $targetOptions + ); + unlink($targetFile); + } + + echo (microtime(true) - $start) . ' Seconds for Thumbnail API' . PHP_EOL; + +### Classic + $start = microtime(true); + + for ($i = 0; $i < $iterations; $i++) { + $image = Image::newFromFile($sourceFile, $sourceOptions); + $image = $image->resize($targetWidth / $image->width); + $image->writeToFile( + $targetFile, + $targetOptions + ); + unlink($targetFile); + } + + echo (microtime(true) - $start) . ' Seconds for Classic API' . PHP_EOL; +}; + +$doBenchmark(); + +echo "=== NOW NO CACHE ===" . PHP_EOL; + +Config::cacheSetMax(0); +Config::cacheSetMaxFiles(0); +Config::cacheSetMaxMem(0); + +$doBenchmark(); diff --git a/src/ForeignDzLayout.php b/src/ForeignDzLayout.php index ae36259..021d75f 100644 --- a/src/ForeignDzLayout.php +++ b/src/ForeignDzLayout.php @@ -53,5 +53,4 @@ abstract class ForeignDzLayout const ZOOMIFY = 'zoomify'; const GOOGLE = 'google'; const IIIF = 'iiif'; - const IIIF3 = 'iiif3'; } diff --git a/src/GsfOutputCsvQuotingMode.php b/src/GsfOutputCsvQuotingMode.php new file mode 100644 index 0000000..a8a08f0 --- /dev/null +++ b/src/GsfOutputCsvQuotingMode.php @@ -0,0 +1,54 @@ + + * @copyright 2016 John Cupitt + * @license https://opensource.org/licenses/MIT MIT + * @link https://github.com/jcupitt/php-vips + */ + +namespace Jcupitt\Vips; + +/** + * The GsfOutputCsvQuotingMode enum. + * @category Images + * @package Jcupitt\Vips + * @author John Cupitt + * @copyright 2016 John Cupitt + * @license https://opensource.org/licenses/MIT MIT + * @link https://github.com/jcupitt/php-vips + */ +abstract class GsfOutputCsvQuotingMode +{ + const NEVER = 'never'; + const AUTO = 'auto'; +} diff --git a/src/ImageAutodoc.php b/src/ImageAutodoc.php index 529c834..5162dae 100644 --- a/src/ImageAutodoc.php +++ b/src/ImageAutodoc.php @@ -171,11 +171,11 @@ * @throws Exception * @method static Image csvload(string $filename, array $options = []) Load csv. * @throws Exception - * @method static Image csvload_source(string $source, array $options = []) Load csv. + * @method static Image csvload_source(VipsSource $source, array $options = []) Load csv. * @throws Exception * @method void csvsave(string $filename, array $options = []) Save image to csv. * @throws Exception - * @method void csvsave_target(string $target, array $options = []) Save image to csv. + * @method void csvsave_target(VipsTarget $target, array $options = []) Save image to csv. * @throws Exception * @method Image dE00(Image $right, array $options = []) Calculate dE00. * @throws Exception @@ -203,8 +203,6 @@ * @throws Exception * @method string dzsave_buffer(array $options = []) Save image to dz buffer. * @throws Exception - * @method void dzsave_target(string $target, array $options = []) Save image to deepzoom target. - * @throws Exception * @method Image embed(integer $x, integer $y, integer $width, integer $height, array $options = []) Embed an image in a larger image. * @throws Exception * @method Image extract_area(integer $left, integer $top, integer $width, integer $height, array $options = []) Extract an area from an image. @@ -229,7 +227,7 @@ * @throws Exception * @method static Image fitsload(string $filename, array $options = []) Load a FITS image. * @throws Exception - * @method static Image fitsload_source(string $source, array $options = []) Load FITS from a source. + * @method static Image fitsload_source(VipsSource $source, array $options = []) Load FITS from a source. * @throws Exception * @method void fitssave(string $filename, array $options = []) Save image to fits file. * @throws Exception @@ -260,13 +258,7 @@ * @throws Exception * @method static Image gifload_buffer(string $buffer, array $options = []) Load GIF with libnsgif. * @throws Exception - * @method static Image gifload_source(string $source, array $options = []) Load gif from source. - * @throws Exception - * @method void gifsave(string $filename, array $options = []) Save as gif. - * @throws Exception - * @method string gifsave_buffer(array $options = []) Save as gif. - * @throws Exception - * @method void gifsave_target(string $target, array $options = []) Save as gif. + * @method static Image gifload_source(VipsSource $source, array $options = []) Load gif from source. * @throws Exception * @method Image globalbalance(array $options = []) Global balance an image mosaic. * @throws Exception @@ -281,13 +273,13 @@ * @throws Exception * @method static Image heifload_buffer(string $buffer, array $options = []) Load a HEIF image. * @throws Exception - * @method static Image heifload_source(string $source, array $options = []) Load a HEIF image. + * @method static Image heifload_source(VipsSource $source, array $options = []) Load a HEIF image. * @throws Exception * @method void heifsave(string $filename, array $options = []) Save image in HEIF format. * @throws Exception * @method string heifsave_buffer(array $options = []) Save image in HEIF format. * @throws Exception - * @method void heifsave_target(string $target, array $options = []) Save image in HEIF format. + * @method void heifsave_target(VipsTarget $target, array $options = []) Save image in HEIF format. * @throws Exception * @method Image hist_cum(array $options = []) Form cumulative histogram. * @throws Exception @@ -334,23 +326,11 @@ * @method Image join(Image $in2, string $direction, array $options = []) Join a pair of images. * @see Direction for possible values for $direction * @throws Exception - * @method static Image jp2kload(string $filename, array $options = []) Load JPEG2000 image. - * @throws Exception - * @method static Image jp2kload_buffer(string $buffer, array $options = []) Load JPEG2000 image. - * @throws Exception - * @method static Image jp2kload_source(string $source, array $options = []) Load JPEG2000 image. - * @throws Exception - * @method void jp2ksave(string $filename, array $options = []) Save image in JPEG2000 format. - * @throws Exception - * @method string jp2ksave_buffer(array $options = []) Save image in JPEG2000 format. - * @throws Exception - * @method void jp2ksave_target(string $target, array $options = []) Save image in JPEG2000 format. - * @throws Exception * @method static Image jpegload(string $filename, array $options = []) Load jpeg from file. * @throws Exception * @method static Image jpegload_buffer(string $buffer, array $options = []) Load jpeg from buffer. * @throws Exception - * @method static Image jpegload_source(string $source, array $options = []) Load image from jpeg source. + * @method static Image jpegload_source(VipsSource $source, array $options = []) Load image from jpeg source. * @throws Exception * @method void jpegsave(string $filename, array $options = []) Save image to jpeg file. * @throws Exception @@ -358,19 +338,19 @@ * @throws Exception * @method void jpegsave_mime(array $options = []) Save image to jpeg mime. * @throws Exception - * @method void jpegsave_target(string $target, array $options = []) Save image to jpeg target. + * @method void jpegsave_target(VipsTarget $target, array $options = []) Save image to jpeg target. * @throws Exception * @method static Image jxlload(string $filename, array $options = []) Load JPEG-XL image. * @throws Exception * @method static Image jxlload_buffer(string $buffer, array $options = []) Load JPEG-XL image. * @throws Exception - * @method static Image jxlload_source(string $source, array $options = []) Load JPEG-XL image. + * @method static Image jxlload_source(VipsSource $source, array $options = []) Load JPEG-XL image. * @throws Exception * @method void jxlsave(string $filename, array $options = []) Save image in JPEG-XL format. * @throws Exception * @method string jxlsave_buffer(array $options = []) Save image in JPEG-XL format. * @throws Exception - * @method void jxlsave_target(string $target, array $options = []) Save image in JPEG-XL format. + * @method void jxlsave_target(VipsTarget $target, array $options = []) Save image in JPEG-XL format. * @throws Exception * @method Image labelregions(array $options = []) Label regions in an image. * @throws Exception @@ -378,7 +358,7 @@ * @throws Exception * @method Image linecache(array $options = []) Cache an image as a set of lines. * @throws Exception - * @method static Image logmat(float $sigma, float $min_ampl, array $options = []) Make a Laplacian of Gaussian image. + * @method static Image logmat(float $sigma, float $min_ampl, array $options = []) Make a laplacian of gaussian image. * @throws Exception * @method static Image magickload(string $filename, array $options = []) Load file with ImageMagick. * @throws Exception @@ -429,13 +409,13 @@ * @throws Exception * @method static Image matrixload(string $filename, array $options = []) Load matrix. * @throws Exception - * @method static Image matrixload_source(string $source, array $options = []) Load matrix. + * @method static Image matrixload_source(VipsSource $source, array $options = []) Load matrix. * @throws Exception * @method void matrixprint(array $options = []) Print matrix. * @throws Exception * @method void matrixsave(string $filename, array $options = []) Save image to matrix. * @throws Exception - * @method void matrixsave_target(string $target, array $options = []) Save image to matrix. + * @method void matrixsave_target(VipsTarget $target, array $options = []) Save image to matrix. * @throws Exception * @method float max(array $options = []) Find image maximum. * @throws Exception @@ -457,23 +437,17 @@ * @throws Exception * @method Image msb(array $options = []) Pick most-significant byte from an image. * @throws Exception - * @method static Image niftiload(string $filename, array $options = []) Load NIfTI volume. - * @throws Exception - * @method static Image niftiload_source(string $source, array $options = []) Load NIfTI volumes. - * @throws Exception - * @method void niftisave(string $filename, array $options = []) Save image to nifti file. - * @throws Exception * @method static Image openexrload(string $filename, array $options = []) Load an OpenEXR image. * @throws Exception * @method static Image openslideload(string $filename, array $options = []) Load file with OpenSlide. * @throws Exception - * @method static Image openslideload_source(string $source, array $options = []) Load source with OpenSlide. + * @method static Image openslideload_source(VipsSource $source, array $options = []) Load source with OpenSlide. * @throws Exception * @method static Image pdfload(string $filename, array $options = []) Load PDF from file. * @throws Exception * @method static Image pdfload_buffer(string $buffer, array $options = []) Load PDF from buffer. * @throws Exception - * @method static Image pdfload_source(string $source, array $options = []) Load PDF from source. + * @method static Image pdfload_source(VipsSource $source, array $options = []) Load PDF from source. * @throws Exception * @method integer percent(float $percent, array $options = []) Find threshold for percent of pixels. * @throws Exception @@ -485,21 +459,21 @@ * @throws Exception * @method static Image pngload_buffer(string $buffer, array $options = []) Load png from buffer. * @throws Exception - * @method static Image pngload_source(string $source, array $options = []) Load png from source. + * @method static Image pngload_source(VipsSource $source, array $options = []) Load png from source. * @throws Exception - * @method void pngsave(string $filename, array $options = []) Save image to file as PNG. + * @method void pngsave(string $filename, array $options = []) Save image to png file. * @throws Exception - * @method string pngsave_buffer(array $options = []) Save image to buffer as PNG. + * @method string pngsave_buffer(array $options = []) Save image to png buffer. * @throws Exception - * @method void pngsave_target(string $target, array $options = []) Save image to target as PNG. + * @method void pngsave_target(VipsTarget $target, array $options = []) Save image to target as PNG. * @throws Exception * @method static Image ppmload(string $filename, array $options = []) Load ppm from file. * @throws Exception - * @method static Image ppmload_source(string $source, array $options = []) Load ppm base class. + * @method static Image ppmload_source(VipsSource $source, array $options = []) Load ppm base class. * @throws Exception * @method void ppmsave(string $filename, array $options = []) Save image to ppm file. * @throws Exception - * @method void ppmsave_target(string $target, array $options = []) Save to ppm. + * @method void ppmsave_target(VipsTarget $target, array $options = []) Save to ppm. * @throws Exception * @method Image premultiply(array $options = []) Premultiply image alpha. * @throws Exception @@ -525,13 +499,13 @@ * @throws Exception * @method static Image radload_buffer(string $buffer, array $options = []) Load rad from buffer. * @throws Exception - * @method static Image radload_source(string $source, array $options = []) Load rad from source. + * @method static Image radload_source(VipsSource $source, array $options = []) Load rad from source. * @throws Exception * @method void radsave(string $filename, array $options = []) Save image to Radiance file. * @throws Exception * @method string radsave_buffer(array $options = []) Save image to Radiance buffer. * @throws Exception - * @method void radsave_target(string $target, array $options = []) Save image to Radiance target. + * @method void radsave_target(VipsTarget $target, array $options = []) Save image to Radiance target. * @throws Exception * @method Image rank(integer $width, integer $height, integer $index, array $options = []) Rank filter. * @throws Exception @@ -619,7 +593,7 @@ * @throws Exception * @method static Image svgload_buffer(string $buffer, array $options = []) Load SVG with rsvg. * @throws Exception - * @method static Image svgload_source(string $source, array $options = []) Load svg from source. + * @method static Image svgload_source(VipsSource $source, array $options = []) Load svg from source. * @throws Exception * @method static Image switch(Image[]|Image $tests, array $options = []) Find the index of the first non-zero pixel in tests. * @throws Exception @@ -633,20 +607,18 @@ * @throws Exception * @method Image thumbnail_image(integer $width, array $options = []) Generate thumbnail from image. * @throws Exception - * @method static Image thumbnail_source(string $source, integer $width, array $options = []) Generate thumbnail from source. + * @method static Image thumbnail_source(VipsSource $source, integer $width, array $options = []) Generate thumbnail from source. * @throws Exception * @method static Image tiffload(string $filename, array $options = []) Load tiff from file. * @throws Exception * @method static Image tiffload_buffer(string $buffer, array $options = []) Load tiff from buffer. * @throws Exception - * @method static Image tiffload_source(string $source, array $options = []) Load tiff from source. + * @method static Image tiffload_source(VipsSource $source, array $options = []) Load tiff from source. * @throws Exception * @method void tiffsave(string $filename, array $options = []) Save image to tiff file. * @throws Exception * @method string tiffsave_buffer(array $options = []) Save image to tiff buffer. * @throws Exception - * @method void tiffsave_target(string $target, array $options = []) Save image to tiff target. - * @throws Exception * @method Image tilecache(array $options = []) Cache an image as a set of tiles. * @throws Exception * @method static Image tonelut(array $options = []) Build a look-up table. @@ -657,23 +629,23 @@ * @throws Exception * @method static Image vipsload(string $filename, array $options = []) Load vips from file. * @throws Exception - * @method static Image vipsload_source(string $source, array $options = []) Load vips from source. + * @method static Image vipsload_source(VipsSource $source, array $options = []) Load vips from source. * @throws Exception * @method void vipssave(string $filename, array $options = []) Save image to file in vips format. * @throws Exception - * @method void vipssave_target(string $target, array $options = []) Save image to target in vips format. + * @method void vipssave_target(VipsTarget $target, array $options = []) Save image to target in vips format. * @throws Exception * @method static Image webpload(string $filename, array $options = []) Load webp from file. * @throws Exception * @method static Image webpload_buffer(string $buffer, array $options = []) Load webp from buffer. * @throws Exception - * @method static Image webpload_source(string $source, array $options = []) Load webp from source. + * @method static Image webpload_source(VipsSource $source, array $options = []) Load webp from source. * @throws Exception * @method void webpsave(string $filename, array $options = []) Save image to webp file. * @throws Exception * @method string webpsave_buffer(array $options = []) Save image to webp buffer. * @throws Exception - * @method void webpsave_target(string $target, array $options = []) Save image to webp target. + * @method void webpsave_target(VipsTarget $target, array $options = []) Save image to webp target. * @throws Exception * @method static Image worley(integer $width, integer $height, array $options = []) Make a worley noise image. * @throws Exception diff --git a/src/OperationMath.php b/src/OperationMath.php index 857b488..52ffef1 100644 --- a/src/OperationMath.php +++ b/src/OperationMath.php @@ -59,10 +59,4 @@ abstract class OperationMath const LOG10 = 'log10'; const EXP = 'exp'; const EXP10 = 'exp10'; - const SINH = 'sinh'; - const COSH = 'cosh'; - const TANH = 'tanh'; - const ASINH = 'asinh'; - const ACOSH = 'acosh'; - const ATANH = 'atanh'; } diff --git a/src/OperationMath2.php b/src/OperationMath2.php index b1fc69e..9e86093 100644 --- a/src/OperationMath2.php +++ b/src/OperationMath2.php @@ -51,5 +51,4 @@ abstract class OperationMath2 { const POW = 'pow'; const WOP = 'wop'; - const ATAN2 = 'atan2'; } diff --git a/tests/StreamingTest.php b/tests/StreamingTest.php index 741ee74..694ccd2 100644 --- a/tests/StreamingTest.php +++ b/tests/StreamingTest.php @@ -70,6 +70,7 @@ public function testFromFileToFile(): void public function testNoLeak(): void { $lastUsage = 0; + $leaked = false; for ($i = 0; $i < 10; $i++) { $filename = tempnam(sys_get_temp_dir(), 'image'); $source = new VipsSourceResource(fopen(__DIR__ . '/images/img_0076.jpg', 'rb')); @@ -80,10 +81,12 @@ public function testNoLeak(): void $usage = memory_get_peak_usage(true); $diff = $usage - $lastUsage; if ($lastUsage !== 0 && $diff > 0) { - echo "LEAK LEAK LEAK" . PHP_EOL; + $leaked = true; } $lastUsage = $usage; } + + $this->assertFalse($leaked, 'Streaming leaked memory'); } public function testFromFileToDescriptor(): void From b2d90b5430f26a2e68d1ee2376582d23d2de912c Mon Sep 17 00:00:00 2001 From: L3tum <9307432+L3tum@users.noreply.github.com> Date: Tue, 28 Feb 2023 23:02:07 +0100 Subject: [PATCH 058/123] fix: Avoid extra memcpy in write callback fix: Avoid extra cast in signal_connect --- examples/streaming-bench.php | 14 +++++++------- src/GObject.php | 4 +--- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/examples/streaming-bench.php b/examples/streaming-bench.php index cbf22f1..26c414c 100644 --- a/examples/streaming-bench.php +++ b/examples/streaming-bench.php @@ -122,10 +122,10 @@ $doBenchmark(); -echo "=== NOW NO CACHE ===" . PHP_EOL; - -Config::cacheSetMax(0); -Config::cacheSetMaxFiles(0); -Config::cacheSetMaxMem(0); - -$doBenchmark(); +//echo "=== NOW NO CACHE ===" . PHP_EOL; +// +//Config::cacheSetMax(0); +//Config::cacheSetMaxFiles(0); +//Config::cacheSetMaxMem(0); +// +//$doBenchmark(); diff --git a/src/GObject.php b/src/GObject.php index 45bd964..61759b2 100644 --- a/src/GObject.php +++ b/src/GObject.php @@ -171,7 +171,6 @@ public function signalConnect(string $name, Closure $callback): void $bufferLength = (int) FFI::gobject()->g_value_get_int64(\FFI::addr($params[2])); $buffer = \FFI::string($bufferPointer, $bufferLength); $returnBufferLength = $callback($buffer); - \FFI::memcpy($bufferPointer, $buffer, $returnBufferLength); FFI::gobject()->g_value_set_int64($returnValue, $returnBufferLength); }; $marshalers['finish'] = static function ( @@ -211,10 +210,9 @@ public function signalConnect(string $name, Closure $callback): void throw new Exception("unsupported signal $name"); } - $go = \FFI::cast(FFI::ctypes('GObject'), $this->pointer); $gc = FFI::gobject()->g_closure_new_simple(\FFI::sizeof(FFI::ctypes('GClosure')), null); $gc->marshal = $marshalers[$name]; - FFI::gobject()->g_signal_connect_closure($go, $name, $gc, 0); + FFI::gobject()->g_signal_connect_closure($this->pointer, $name, $gc, 0); } } From 03ab2a6cf01018da59982b695449c1cadf996f18 Mon Sep 17 00:00:00 2001 From: L3tum <9307432+L3tum@users.noreply.github.com> Date: Sat, 4 Mar 2023 15:29:42 +0100 Subject: [PATCH 059/123] fix: Implement better read functionality fix: Use resource as reference for callbacks --- src/FFI.php | 1 - src/GObject.php | 95 ++++++++++++++++++++------------------ src/VipsSourceCustom.php | 13 +----- src/VipsSourceResource.php | 4 +- src/VipsTargetCustom.php | 10 +--- src/VipsTargetResource.php | 8 ++-- 6 files changed, 60 insertions(+), 71 deletions(-) diff --git a/src/FFI.php b/src/FFI.php index 6a76d9f..c701bf9 100644 --- a/src/FFI.php +++ b/src/FFI.php @@ -484,7 +484,6 @@ private static function init(): void */ }; long g_signal_connect_closure(GObject* object, const char* detailed_signal, GClosure *closure, bool after); -GClosure* g_closure_ref(GClosure* closure); GClosure* g_closure_new_simple (int sizeof_closure, void* data); EOS; diff --git a/src/GObject.php b/src/GObject.php index 61759b2..decfdaa 100644 --- a/src/GObject.php +++ b/src/GObject.php @@ -97,93 +97,100 @@ public function unref(): void FFI::gobject()->g_object_unref($this->pointer); } + /** + * @throws Exception + */ public function signalConnect(string $name, Closure $callback): void { $imageProgressCb = static function ( - CData $gClosure, + CData $gClosure, ?CData $returnValue, - int $numberOfParams, - CData $params, - CData $hint, + int $numberOfParams, + CData $params, + CData $hint, ?CData $data - ) use ($callback) { + ) use (&$callback) { assert($numberOfParams === 3); /** - * Marshal-Signature: void(VipsImage*, void*, void*) + * Marshal-Signature: void(VipsImage* image, void* progress, void* handle) */ - $vi = \FFI::cast(FFI::ctypes('GObject'), FFI::gobject()->g_value_get_pointer(\FFI::addr($params[1]))); + $vi = \FFI::cast(FFI::ctypes('GObject'), FFI::gobject()->g_value_get_pointer(\FFI::addr($params[0]))); FFI::gobject()->g_object_ref($vi); $image = new Image($vi); - $pr = \FFI::cast(FFI::ctypes('VipsProgress'), FFI::gobject()->g_value_get_pointer(\FFI::addr($params[2]))); + $pr = \FFI::cast(FFI::ctypes('VipsProgress'), FFI::gobject()->g_value_get_pointer(\FFI::addr($params[1]))); $callback($image, $pr); }; $marshalers = ['preeval' => $imageProgressCb, 'eval' => $imageProgressCb, 'posteval' => $imageProgressCb]; if (FFI::atLeast(8, 9)) { $marshalers['read'] = static function ( - CData $gClosure, - CData $returnValue, - int $numberOfParams, - CData $params, - CData $hint, + CData $gClosure, + CData $returnValue, + int $numberOfParams, + CData $params, + CData $hint, ?CData $data ) use (&$callback): void { assert($numberOfParams === 4); /* - * Marshal-Signature: gint64(VipsSourceCustom*, void*, gint64, void*) + * Marshal-Signature: gint64(VipsSourceCustom* source, void* buffer, gint64 length, void* handle) */ $bufferPointer = FFI::gobject()->g_value_get_pointer(\FFI::addr($params[1])); - $bufferLength = (int) FFI::gobject()->g_value_get_int64(\FFI::addr($params[2])); - $buffer = \FFI::string($bufferPointer, $bufferLength); - $returnBufferLength = $callback($buffer); - \FFI::memcpy($bufferPointer, $buffer, $returnBufferLength); + $bufferLength = (int)FFI::gobject()->g_value_get_int64(\FFI::addr($params[2])); + $returnBuffer = $callback($bufferLength); + $returnBufferLength = 0; + + if ($returnBuffer !== null) { + $returnBufferLength = strlen($returnBuffer); + \FFI::memcpy($bufferPointer, $returnBuffer, $returnBufferLength); + } FFI::gobject()->g_value_set_int64($returnValue, $returnBufferLength); }; $marshalers['seek'] = static function ( - CData $gClosure, - CData $returnValue, - int $numberOfParams, - CData $params, - CData $hint, + CData $gClosure, + CData $returnValue, + int $numberOfParams, + CData $params, + CData $hint, ?CData $data ) use (&$callback): void { assert($numberOfParams === 4); /* - * Marshal-Signature: gint64(VipsSourceCustom*, gint64, int, void*) + * Marshal-Signature: gint64(VipsSourceCustom* source, gint64 offset, int whence, void* handle) */ - $offset = (int) FFI::gobject()->g_value_get_int64(\FFI::addr($params[1])); - $whence = (int) FFI::gobject()->g_value_get_int(\FFI::addr($params[2])); + $offset = (int)FFI::gobject()->g_value_get_int64(\FFI::addr($params[1])); + $whence = (int)FFI::gobject()->g_value_get_int(\FFI::addr($params[2])); FFI::gobject()->g_value_set_int64($returnValue, $callback($offset, $whence)); }; $marshalers['write'] = static function ( - CData $gClosure, - CData $returnValue, - int $numberOfParams, - CData $params, - CData $hint, + CData $gClosure, + CData $returnValue, + int $numberOfParams, + CData $params, + CData $hint, ?CData $data ) use (&$callback): void { assert($numberOfParams === 4); /* - * Marshal-Signature: gint64(VipsTargetCustom*, void*, gint64, void*) + * Marshal-Signature: gint64(VipsTargetCustom* target, void* buffer, gint64 length, void* handle) */ $bufferPointer = FFI::gobject()->g_value_get_pointer(\FFI::addr($params[1])); - $bufferLength = (int) FFI::gobject()->g_value_get_int64(\FFI::addr($params[2])); + $bufferLength = (int)FFI::gobject()->g_value_get_int64(\FFI::addr($params[2])); $buffer = \FFI::string($bufferPointer, $bufferLength); $returnBufferLength = $callback($buffer); FFI::gobject()->g_value_set_int64($returnValue, $returnBufferLength); }; $marshalers['finish'] = static function ( - CData $gClosure, + CData $gClosure, ?CData $returnValue, - int $numberOfParams, - CData $params, - CData $hint, + int $numberOfParams, + CData $params, + CData $hint, ?CData $data ) use (&$callback): void { assert($numberOfParams === 2); /** - * Marshal-Signature: void(VipsTargetCustom*, void*) + * Marshal-Signature: void(VipsTargetCustom* target, void* handle) */ $callback(); }; @@ -191,16 +198,16 @@ public function signalConnect(string $name, Closure $callback): void if (FFI::atLeast(8, 13)) { $marshalers['end'] = static function ( - CData $gClosure, - CData $returnValue, - int $numberOfParams, - CData $params, - CData $hint, + CData $gClosure, + CData $returnValue, + int $numberOfParams, + CData $params, + CData $hint, ?CData $data ) use (&$callback): void { assert($numberOfParams === 2); /** - * Marshal-Signature: int(VipsTargetCustom*, void*) + * Marshal-Signature: int(VipsTargetCustom* target, void* handle) */ FFI::gobject()->g_value_set_int($returnValue, $callback()); }; diff --git a/src/VipsSourceCustom.php b/src/VipsSourceCustom.php index fd4f39c..2c40a46 100644 --- a/src/VipsSourceCustom.php +++ b/src/VipsSourceCustom.php @@ -22,23 +22,14 @@ public function __construct() /** * Attach a read handler. - * The interface is exactly as io.read() in Python. The handler is given a number + * The interface is similar to fread. The handler is given a number * of bytes to fetch, and should return a bytes-like object containing up * to that number of bytes. If there is no more data available, it should * return None. */ public function onRead(Closure $callback): void { - $this->signalConnect('read', static function (string &$buffer) use ($callback): int { - $chunk = $callback(strlen($buffer)); - - if ($chunk === null) { - return 0; - } - - $buffer = substr_replace($buffer, $chunk, 0); - return strlen($chunk); - }); + $this->signalConnect('read', $callback); } /** diff --git a/src/VipsSourceResource.php b/src/VipsSourceResource.php index 6027c01..396051c 100644 --- a/src/VipsSourceResource.php +++ b/src/VipsSourceResource.php @@ -22,12 +22,12 @@ public function __construct($resource) $this->resource = $resource; parent::__construct(); - $this->onRead(static function (int $length) use ($resource): ?string { + $this->onRead(static function (int $length) use (&$resource): ?string { return fread($resource, $length) ?: null; }); if (stream_get_meta_data($resource)['seekable']) { - $this->onSeek(static function (int $offset, int $whence) use ($resource): int { + $this->onSeek(static function (int $offset, int $whence) use (&$resource): int { fseek($resource, $offset, $whence); return ftell($resource); }); diff --git a/src/VipsTargetCustom.php b/src/VipsTargetCustom.php index 5901b72..d6f96b8 100644 --- a/src/VipsTargetCustom.php +++ b/src/VipsTargetCustom.php @@ -28,15 +28,7 @@ public function onWrite(Closure $callback): void public function onRead(Closure $callback): void { if (FFI::atLeast(8, 13)) { - $this->signalConnect('read', static function (string &$buffer) use ($callback): int { - $chunk = $callback(strlen($buffer)); - - if ($chunk === null) { - return 0; - } - $buffer = substr_replace($buffer, $chunk, 0); - return strlen($chunk); - }); + $this->signalConnect('read', $callback); } } diff --git a/src/VipsTargetResource.php b/src/VipsTargetResource.php index 57d3dfa..667f1eb 100644 --- a/src/VipsTargetResource.php +++ b/src/VipsTargetResource.php @@ -20,24 +20,24 @@ public function __construct($resource) $this->resource = $resource; parent::__construct(); - $this->onWrite(static function (string $buffer) use ($resource): int { + $this->onWrite(static function (string $buffer) use (&$resource): int { return fwrite($resource, $buffer) ?: 0; }); - $this->onEnd(static function () use ($resource): void { + $this->onEnd(static function () use (&$resource): void { fclose($resource); }); $meta = stream_get_meta_data($resource); // See: https://www.php.net/manual/en/function.fopen.php if (substr($meta['mode'], -1) === '+') { - $this->onRead(static function (int $length) use ($resource): ?string { + $this->onRead(static function (int $length) use (&$resource): ?string { return fread($resource, $length) ?: null; }); } if ($meta['seekable']) { - $this->onSeek(static function (int $offset, int $whence) use ($resource): int { + $this->onSeek(static function (int $offset, int $whence) use (&$resource): int { fseek($resource, $offset, $whence); return ftell($resource); }); From a95fbcbfe61d9265f0d645f9719823363c621928 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sun, 5 Mar 2023 13:09:54 +0000 Subject: [PATCH 060/123] use `composer.json` in the project root So the examples always use the latest version. --- CHANGELOG.md | 1 + examples/addconst.php | 2 +- examples/bench.php | 7 ++++++- examples/class.php | 2 +- examples/composer.json | 5 ----- examples/sig.php | 7 ++++++- examples/vips-magick.php | 7 ++++++- examples/watermark-image.php | 4 ++-- examples/watermark-text.php | 2 +- 9 files changed, 24 insertions(+), 13 deletions(-) delete mode 100644 examples/composer.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 950805b..f2d86ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ All notable changes to `:vips` will be documented in this file. ## master - improve FFI startup [West14] +- revise example use of composer [jcupitt] ## 2.1.1 - 2022-11-13 diff --git a/examples/addconst.php b/examples/addconst.php index 971d757..e9d330e 100755 --- a/examples/addconst.php +++ b/examples/addconst.php @@ -1,7 +1,7 @@ #!/usr/bin/env php Vips\Access::SEQUENTIAL]); $im = $im->crop(100, 100, $im->width - 200, $im->height - 200); diff --git a/examples/class.php b/examples/class.php index c98b72a..47c5b3b 100755 --- a/examples/class.php +++ b/examples/class.php @@ -1,7 +1,7 @@ #!/usr/bin/env php Vips\Access::SEQUENTIAL]); /** diff --git a/examples/vips-magick.php b/examples/vips-magick.php index 5d355c6..42e6691 100755 --- a/examples/vips-magick.php +++ b/examples/vips-magick.php @@ -1,10 +1,15 @@ #!/usr/bin/env php Date: Mon, 13 Mar 2023 12:27:59 +0100 Subject: [PATCH 061/123] feat: Cleanup GObject.php chore: Add docs --- src/GObject.php | 263 +++++++++++++++++++++---------------- src/VipsSource.php | 13 +- src/VipsSourceCustom.php | 2 +- src/VipsSourceResource.php | 2 +- src/VipsTarget.php | 14 ++ src/VipsTargetCustom.php | 41 ++++++ src/VipsTargetResource.php | 2 +- 7 files changed, 216 insertions(+), 121 deletions(-) diff --git a/src/GObject.php b/src/GObject.php index decfdaa..8a747fc 100644 --- a/src/GObject.php +++ b/src/GObject.php @@ -98,129 +98,166 @@ public function unref(): void } /** + * Connect to a signal on this object. + * The callback will be triggered every time this signal is issued on this instance. * @throws Exception */ public function signalConnect(string $name, Closure $callback): void { - $imageProgressCb = static function ( - CData $gClosure, - ?CData $returnValue, - int $numberOfParams, - CData $params, - CData $hint, - ?CData $data - ) use (&$callback) { - assert($numberOfParams === 3); - /** - * Marshal-Signature: void(VipsImage* image, void* progress, void* handle) - */ - $vi = \FFI::cast(FFI::ctypes('GObject'), FFI::gobject()->g_value_get_pointer(\FFI::addr($params[0]))); - FFI::gobject()->g_object_ref($vi); - $image = new Image($vi); - $pr = \FFI::cast(FFI::ctypes('VipsProgress'), FFI::gobject()->g_value_get_pointer(\FFI::addr($params[1]))); - $callback($image, $pr); - }; - $marshalers = ['preeval' => $imageProgressCb, 'eval' => $imageProgressCb, 'posteval' => $imageProgressCb]; - - if (FFI::atLeast(8, 9)) { - $marshalers['read'] = static function ( - CData $gClosure, - CData $returnValue, - int $numberOfParams, - CData $params, - CData $hint, - ?CData $data - ) use (&$callback): void { - assert($numberOfParams === 4); - /* - * Marshal-Signature: gint64(VipsSourceCustom* source, void* buffer, gint64 length, void* handle) - */ - $bufferPointer = FFI::gobject()->g_value_get_pointer(\FFI::addr($params[1])); - $bufferLength = (int)FFI::gobject()->g_value_get_int64(\FFI::addr($params[2])); - $returnBuffer = $callback($bufferLength); - $returnBufferLength = 0; - - if ($returnBuffer !== null) { - $returnBufferLength = strlen($returnBuffer); - \FFI::memcpy($bufferPointer, $returnBuffer, $returnBufferLength); - } - FFI::gobject()->g_value_set_int64($returnValue, $returnBufferLength); - }; - $marshalers['seek'] = static function ( - CData $gClosure, - CData $returnValue, - int $numberOfParams, - CData $params, - CData $hint, - ?CData $data - ) use (&$callback): void { - assert($numberOfParams === 4); - /* - * Marshal-Signature: gint64(VipsSourceCustom* source, gint64 offset, int whence, void* handle) - */ - $offset = (int)FFI::gobject()->g_value_get_int64(\FFI::addr($params[1])); - $whence = (int)FFI::gobject()->g_value_get_int(\FFI::addr($params[2])); - FFI::gobject()->g_value_set_int64($returnValue, $callback($offset, $whence)); - }; - $marshalers['write'] = static function ( - CData $gClosure, - CData $returnValue, - int $numberOfParams, - CData $params, - CData $hint, - ?CData $data - ) use (&$callback): void { - assert($numberOfParams === 4); - /* - * Marshal-Signature: gint64(VipsTargetCustom* target, void* buffer, gint64 length, void* handle) - */ - $bufferPointer = FFI::gobject()->g_value_get_pointer(\FFI::addr($params[1])); - $bufferLength = (int)FFI::gobject()->g_value_get_int64(\FFI::addr($params[2])); - $buffer = \FFI::string($bufferPointer, $bufferLength); - $returnBufferLength = $callback($buffer); - FFI::gobject()->g_value_set_int64($returnValue, $returnBufferLength); - }; - $marshalers['finish'] = static function ( - CData $gClosure, - ?CData $returnValue, - int $numberOfParams, - CData $params, - CData $hint, - ?CData $data - ) use (&$callback): void { - assert($numberOfParams === 2); - /** - * Marshal-Signature: void(VipsTargetCustom* target, void* handle) - */ - $callback(); - }; - } - - if (FFI::atLeast(8, 13)) { - $marshalers['end'] = static function ( - CData $gClosure, - CData $returnValue, - int $numberOfParams, - CData $params, - CData $hint, - ?CData $data - ) use (&$callback): void { - assert($numberOfParams === 2); - /** - * Marshal-Signature: int(VipsTargetCustom* target, void* handle) - */ - FFI::gobject()->g_value_set_int($returnValue, $callback()); - }; - } - - if (!isset($marshalers[$name])) { + $marshaler = self::getMarshaler($name, $callback); + if ($marshaler === null) { throw new Exception("unsupported signal $name"); } $gc = FFI::gobject()->g_closure_new_simple(\FFI::sizeof(FFI::ctypes('GClosure')), null); - $gc->marshal = $marshalers[$name]; + $gc->marshal = $marshaler; FFI::gobject()->g_signal_connect_closure($this->pointer, $name, $gc, 0); } + + private static function getMarshaler(string $name, Closure $callback): ?Closure + { + switch ($name) { + case 'preeval': + case 'eval': + case 'posteval': + return static function ( + CData $gClosure, + ?CData $returnValue, + int $numberOfParams, + CData $params, + CData $hint, + ?CData $data + ) use (&$callback) { + assert($numberOfParams === 3); + /** + * Signature: void(VipsImage* image, void* progress, void* handle) + */ + $vi = \FFI::cast( + FFI::ctypes('GObject'), + FFI::gobject()->g_value_get_pointer(\FFI::addr($params[0])) + ); + FFI::gobject()->g_object_ref($vi); + $image = new Image($vi); + $pr = \FFI::cast( + FFI::ctypes('VipsProgress'), + FFI::gobject()->g_value_get_pointer(\FFI::addr($params[1])) + ); + $callback($image, $pr); + }; + case 'read': + if (FFI::atLeast(8, 9)) { + return static function ( + CData $gClosure, + CData $returnValue, + int $numberOfParams, + CData $params, + CData $hint, + ?CData $data + ) use (&$callback): void { + assert($numberOfParams === 4); + /* + * Signature: gint64(VipsSourceCustom* source, void* buffer, gint64 length, void* handle) + */ + $bufferLength = (int)FFI::gobject()->g_value_get_int64(\FFI::addr($params[2])); + $returnBuffer = $callback($bufferLength); + $returnBufferLength = 0; + + if ($returnBuffer !== null) { + $returnBufferLength = strlen($returnBuffer); + $bufferPointer = FFI::gobject()->g_value_get_pointer(\FFI::addr($params[1])); + \FFI::memcpy($bufferPointer, $returnBuffer, $returnBufferLength); + } + FFI::gobject()->g_value_set_int64($returnValue, $returnBufferLength); + }; + } + + return null; + case 'seek': + if (FFI::atLeast(8, 9)) { + return static function ( + CData $gClosure, + CData $returnValue, + int $numberOfParams, + CData $params, + CData $hint, + ?CData $data + ) use (&$callback): void { + assert($numberOfParams === 4); + /* + * Signature: gint64(VipsSourceCustom* source, gint64 offset, int whence, void* handle) + */ + $offset = (int)FFI::gobject()->g_value_get_int64(\FFI::addr($params[1])); + $whence = (int)FFI::gobject()->g_value_get_int(\FFI::addr($params[2])); + FFI::gobject()->g_value_set_int64($returnValue, $callback($offset, $whence)); + }; + } + + return null; + case 'write': + if (FFI::atLeast(8, 9)) { + return static function ( + CData $gClosure, + CData $returnValue, + int $numberOfParams, + CData $params, + CData $hint, + ?CData $data + ) use (&$callback): void { + assert($numberOfParams === 4); + /* + * Signature: gint64(VipsTargetCustom* target, void* buffer, gint64 length, void* handle) + */ + $bufferPointer = FFI::gobject()->g_value_get_pointer(\FFI::addr($params[1])); + $bufferLength = (int)FFI::gobject()->g_value_get_int64(\FFI::addr($params[2])); + $buffer = \FFI::string($bufferPointer, $bufferLength); + $returnBufferLength = $callback($buffer); + FFI::gobject()->g_value_set_int64($returnValue, $returnBufferLength); + }; + } + + return null; + case 'finish': + if (FFI::atLeast(8, 9)) { + return static function ( + CData $gClosure, + ?CData $returnValue, + int $numberOfParams, + CData $params, + CData $hint, + ?CData $data + ) use (&$callback): void { + assert($numberOfParams === 2); + /** + * Signature: void(VipsTargetCustom* target, void* handle) + */ + $callback(); + }; + } + + return null; + case 'end': + if (FFI::atLeast(8, 13)) { + return static function ( + CData $gClosure, + CData $returnValue, + int $numberOfParams, + CData $params, + CData $hint, + ?CData $data + ) use (&$callback): void { + assert($numberOfParams === 2); + /** + * Signature: int(VipsTargetCustom* target, void* handle) + */ + FFI::gobject()->g_value_set_int($returnValue, $callback()); + }; + } + + return null; + default: + return null; + } + } } /* diff --git a/src/VipsSource.php b/src/VipsSource.php index ee3ef71..1d15dda 100644 --- a/src/VipsSource.php +++ b/src/VipsSource.php @@ -21,9 +21,9 @@ public function __construct(\FFI\CData $pointer) /** * Make a new source from a file descriptor (a small integer). * Make a new source that is attached to the descriptor. For example: - * source = pyvips.Source.new_from_descriptor(0) + * $source = VipsSource::newFromDescriptor(0) * Makes a descriptor attached to stdin. - * You can pass this source to (for example) :meth:`new_from_source`. + * You can pass this source to (for example) @see Image::newFromSource() * @throws Exception */ public static function newFromDescriptor(int $descriptor): self @@ -40,8 +40,8 @@ public static function newFromDescriptor(int $descriptor): self /** * Make a new source from a filename. * Make a new source that is attached to the named file. For example: - * source = pyvips.Source.new_from_file("myfile.jpg") - * You can pass this source to (for example) :meth:`new_from_source`. + * $source = VipsSource::newFromFile("myfile.jpg") + * You can pass this source to (for example) @see Image::newFromSource() * @throws Exception */ public static function newFromFile(string $filename): self @@ -56,7 +56,10 @@ public static function newFromFile(string $filename): self } /** - * @TODO Not sure how best to implement this since PHP does not have buffers like Python + * Make a new source from a filename. + * Make a new source that uses the provided $data. For example: + * $source = VipsSource::newFromFile(file_get_contents("myfile.jpg")) + * You can pass this source to (for example) @see Image::newFromSource() * @throws Exception */ public static function newFromMemory(string $data): self diff --git a/src/VipsSourceCustom.php b/src/VipsSourceCustom.php index 2c40a46..8c40a5b 100644 --- a/src/VipsSourceCustom.php +++ b/src/VipsSourceCustom.php @@ -25,7 +25,7 @@ public function __construct() * The interface is similar to fread. The handler is given a number * of bytes to fetch, and should return a bytes-like object containing up * to that number of bytes. If there is no more data available, it should - * return None. + * return null. */ public function onRead(Closure $callback): void { diff --git a/src/VipsSourceResource.php b/src/VipsSourceResource.php index 396051c..a521fbd 100644 --- a/src/VipsSourceResource.php +++ b/src/VipsSourceResource.php @@ -37,6 +37,6 @@ public function __construct($resource) public function __destruct() { fclose($this->resource); - parent::__destruct(); // TODO: Change the autogenerated stub + parent::__destruct(); } } diff --git a/src/VipsTarget.php b/src/VipsTarget.php index a67e663..3c7f1c4 100644 --- a/src/VipsTarget.php +++ b/src/VipsTarget.php @@ -19,6 +19,12 @@ public function __construct(\FFI\CData $pointer) } /** + * Make a new target to write to a file descriptor (a small integer). + * Make a new target that is attached to the descriptor. For example:: + * $target = VipsTarget.newToDescriptor(1) + * Makes a descriptor attached to stdout. + * You can pass this target to (for example) @see Image::writeToTarget() + * @throws Exception */ public static function newToDescriptor(int $descriptor): self @@ -32,6 +38,10 @@ public static function newToDescriptor(int $descriptor): self } /** + * Make a new target to write to a file name. + * Make a new target that is attached to the file name. For example:: + * $target = VipsTarget.newToFile("myfile.jpg") + * You can pass this target to (for example) @see Image::writeToTarget() * @throws Exception */ public static function newToFile(string $filename): self @@ -46,6 +56,10 @@ public static function newToFile(string $filename): self } /** + * Make a new target to write to a memory buffer. + * For example:: + * $target = VipsTarget.newToMemory() + * You can pass this target to (for example) @see Image::writeToTarget() * @throws Exception */ public static function newToMemory(): self diff --git a/src/VipsTargetCustom.php b/src/VipsTargetCustom.php index d6f96b8..112231b 100644 --- a/src/VipsTargetCustom.php +++ b/src/VipsTargetCustom.php @@ -20,11 +20,27 @@ public function __construct() parent::__construct($this->pointer); } + /** + * Attach a write handler. + * The interface is exactly as fwrite. The handler is given a bytes-like object to write, + * and should return the number of bytes written. + * @throws Exception + */ public function onWrite(Closure $callback): void { $this->signalConnect('write', $callback); } + /** + * Attach a read handler. + * The interface is similar to fread. The handler is given a number + * of bytes to fetch, and should return a bytes-like object containing up + * to that number of bytes. If there is no more data available, it should + * return null. + * Read handlers on VipsTarget are optional. If you do not set one, your + * target will be treated as unreadable and libvips will be unable to + * write some file types (just TIFF, as of the time of writing). + */ public function onRead(Closure $callback): void { if (FFI::atLeast(8, 13)) { @@ -32,6 +48,19 @@ public function onRead(Closure $callback): void } } + /** + * Attach a seek handler. + * The interface is the same as fseek, so the handler is passed + * parameters for $offset and $whence with the same meanings. + * However, the handler MUST return the new seek position. A simple way + * to do this is to call ftell() and return that result. + * Seek handlers are optional. If you do not set one, your source will be + * treated as unseekable and libvips will do extra caching. + * $whence in particular: + * 0 => start + * 1 => current position + * 2 => end + */ public function onSeek(Closure $callback): void { if (FFI::atLeast(8, 13)) { @@ -39,6 +68,13 @@ public function onSeek(Closure $callback): void } } + /** + * Attach an end handler. + * This optional handler is called at the end of write. It should do any + * cleaning up necessary, and return 0 on success and -1 on error. + * Automatically falls back to onFinish if libvips <8.13 + * @throws Exception + */ public function onEnd(Closure $callback): void { if (FFI::atLeast(8, 13)) { @@ -48,6 +84,11 @@ public function onEnd(Closure $callback): void } } + /** + * Attach a finish handler. + * For libvips 8.13 and later, this method is deprecated in favour of @see VipsTargetCustom::onEnd() + * @throws Exception + */ public function onFinish(Closure $callback): void { $this->signalConnect('finish', $callback); diff --git a/src/VipsTargetResource.php b/src/VipsTargetResource.php index 667f1eb..ffcaa04 100644 --- a/src/VipsTargetResource.php +++ b/src/VipsTargetResource.php @@ -49,6 +49,6 @@ public function __destruct() if (is_resource($this->resource)) { fclose($this->resource); } - parent::__destruct(); // TODO: Change the autogenerated stub + parent::__destruct(); } } From d11e3e28bd043954827fee8d2c1b2bafed0ca916 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 24 Mar 2023 15:15:00 +0000 Subject: [PATCH 062/123] fix bandrank We were not forming the argument image list correctly. see https://github.com/libvips/php-vips/issues/195 --- CHANGELOG.md | 1 + src/Image.php | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2d86ba..13e1709 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to `:vips` will be documented in this file. - improve FFI startup [West14] - revise example use of composer [jcupitt] +- fix bandrank [axkirillov] ## 2.1.1 - 2022-11-13 diff --git a/src/Image.php b/src/Image.php index bbddf07..ab2c2b4 100644 --- a/src/Image.php +++ b/src/Image.php @@ -1885,7 +1885,11 @@ public function bandrank($other, array $options = []): Image $other = (array) $other; } - return VipsOperation::call('bandrank', $this, $other, $options); + return VipsOperation::call( + 'bandrank', + null, + [array_merge([$this], $other)], + $options); } /** From aa86adc368fa8a70369999e7770bdf013b6ecb9c Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 3 May 2023 08:52:46 +0100 Subject: [PATCH 063/123] fix FFI startup log message thanks ganicus see https://github.com/libvips/php-vips/issues/201 --- src/FFI.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/FFI.php b/src/FFI.php index b76c8bf..4f9d726 100644 --- a/src/FFI.php +++ b/src/FFI.php @@ -253,7 +253,10 @@ private static function init(): void EOS, $path . $vips_libname); break; } catch (\FFI\Exception $e) { - Utils::debugLog("init", ["msg" => "library load failed", "exception" => $e]); + Utils::debugLog("init", [ + "msg" => "library load failed", + "exception" => $e->getMessage() + ]); } } From 972f7c3c4c71b534db21cb7e23ffc01d24c2950f Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 3 May 2023 08:54:18 +0100 Subject: [PATCH 064/123] credit ganicus in changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 13e1709..de03a92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ All notable changes to `:vips` will be documented in this file. - improve FFI startup [West14] - revise example use of composer [jcupitt] - fix bandrank [axkirillov] +- fix FFI startup log error [ganicus] ## 2.1.1 - 2022-11-13 From 83fe8e0dfd0d95ae5303e52d45b241ef938b2e4d Mon Sep 17 00:00:00 2001 From: L3tum <9307432+L3tum@users.noreply.github.com> Date: Wed, 31 May 2023 09:11:25 +0200 Subject: [PATCH 065/123] fix: Do not use Vips Prefix --- examples/generate_phpdoc.py | 8 +-- examples/streaming-bench.php | 24 ++++----- examples/streaming.php | 6 +-- src/Image.php | 8 +-- src/ImageAutodoc.php | 54 +++++++++---------- src/{VipsSource.php => Source.php} | 2 +- ...{VipsSourceCustom.php => SourceCustom.php} | 2 +- ...sSourceResource.php => SourceResource.php} | 2 +- src/{VipsTarget.php => Target.php} | 2 +- ...{VipsTargetCustom.php => TargetCustom.php} | 6 +-- ...sTargetResource.php => TargetResource.php} | 2 +- tests/StreamingTest.php | 32 +++++------ 12 files changed, 74 insertions(+), 74 deletions(-) rename src/{VipsSource.php => Source.php} (98%) rename src/{VipsSourceCustom.php => SourceCustom.php} (97%) rename src/{VipsSourceResource.php => SourceResource.php} (94%) rename src/{VipsTarget.php => Target.php} (98%) rename src/{VipsTargetCustom.php => TargetCustom.php} (96%) rename src/{VipsTargetResource.php => TargetResource.php} (96%) diff --git a/examples/generate_phpdoc.py b/examples/generate_phpdoc.py index 006a34e..632db36 100755 --- a/examples/generate_phpdoc.py +++ b/examples/generate_phpdoc.py @@ -32,8 +32,8 @@ GValue.array_double_type: 'float[]|float', GValue.array_image_type: 'Image[]|Image', GValue.blob_type: 'string', - GValue.source_type: 'VipsSource', - GValue.target_type: 'VipsTarget' + GValue.source_type: 'Source', + GValue.target_type: 'Target' } # php result type names are different, annoyingly, and very restricted @@ -51,8 +51,8 @@ GValue.array_double_type: 'array', GValue.array_image_type: 'array', GValue.blob_type: 'string', - GValue.source_type: 'VipsSource', - GValue.target_type: 'VipsTarget' + GValue.source_type: 'Source', + GValue.target_type: 'Target' } # values for VipsArgumentFlags diff --git a/examples/streaming-bench.php b/examples/streaming-bench.php index 26c414c..1fcf04c 100644 --- a/examples/streaming-bench.php +++ b/examples/streaming-bench.php @@ -3,10 +3,10 @@ use Jcupitt\Vips\Config; use Jcupitt\Vips\Image; -use Jcupitt\Vips\VipsSource; -use Jcupitt\Vips\VipsSourceResource; -use Jcupitt\Vips\VipsTarget; -use Jcupitt\Vips\VipsTargetResource; +use Jcupitt\Vips\Source; +use Jcupitt\Vips\SourceResource; +use Jcupitt\Vips\Target; +use Jcupitt\Vips\TargetResource; require dirname(__DIR__) . '/vendor/autoload.php'; @@ -24,8 +24,8 @@ $start = microtime(true); for ($i = 0; $i < $iterations; $i++) { - $source = new VipsSourceResource(fopen($sourceFile, 'rb')); - $target = new VipsTargetResource(fopen($targetFile, 'wb+')); + $source = new SourceResource(fopen($sourceFile, 'rb')); + $target = new TargetResource(fopen($targetFile, 'wb+')); $image = Image::newFromSource($source, '', $sourceOptions); $image = $image->resize($targetWidth / $image->width); $image->writeToTarget( @@ -42,8 +42,8 @@ $start = microtime(true); for ($i = 0; $i < $iterations; $i++) { - $source = VipsSource::newFromFile($sourceFile); - $target = VipsTarget::newToFile($targetFile); + $source = Source::newFromFile($sourceFile); + $target = Target::newToFile($targetFile); $image = Image::newFromSource($source, '', $sourceOptions); $image = $image->resize($targetWidth / $image->width); $image->writeToTarget( @@ -60,8 +60,8 @@ $start = microtime(true); for ($i = 0; $i < $iterations; $i++) { - $source = new VipsSourceResource(fopen($sourceFile, 'rb')); - $target = new VipsTargetResource(fopen($targetFile, 'wb+')); + $source = new SourceResource(fopen($sourceFile, 'rb')); + $target = new TargetResource(fopen($targetFile, 'wb+')); $image = Image::thumbnail_source($source, $targetWidth); $image->writeToTarget( $target, @@ -77,8 +77,8 @@ $start = microtime(true); for ($i = 0; $i < $iterations; $i++) { - $source = VipsSource::newFromFile($sourceFile); - $target = VipsTarget::newToFile($targetFile); + $source = Source::newFromFile($sourceFile); + $target = Target::newToFile($targetFile); $image = Image::thumbnail_source($source, $targetWidth); $image->writeToTarget( $target, diff --git a/examples/streaming.php b/examples/streaming.php index 585185d..f5628e2 100644 --- a/examples/streaming.php +++ b/examples/streaming.php @@ -4,9 +4,9 @@ require dirname(__DIR__) . '/vendor/autoload.php'; use Jcupitt\Vips; -use Jcupitt\Vips\VipsSource; +use Jcupitt\Vips\Source; -$source = VipsSource::newFromFile(dirname(__DIR__) . '/tests/images/img_0076.jpg'); -$target = Vips\VipsTarget::newToFile(dirname(__DIR__) . "/tests/images/target.jpg"); +$source = Source::newFromFile(dirname(__DIR__) . '/tests/images/img_0076.jpg'); +$target = Vips\Target::newToFile(dirname(__DIR__) . "/tests/images/target.jpg"); $image = Vips\Image::newFromSource($source); $image->writeToTarget($target, '.jpg[Q=95]'); diff --git a/src/Image.php b/src/Image.php index a7b53e1..64d9902 100644 --- a/src/Image.php +++ b/src/Image.php @@ -906,10 +906,10 @@ public function newFromImage($value): Image * example 'VipsForeignLoadJpegSource'. You can use this to work out what * options to pass to newFromSource(). * - * @param VipsSource $source The source to test + * @param Source $source The source to test * @return string|null The name of the load operation, or null. */ - public static function findLoadSource(VipsSource $source): ?string + public static function findLoadSource(Source $source): ?string { return FFI::vips()->vips_foreign_find_load_source(\FFI::cast(FFI::ctypes('VipsSource'), $source->pointer)); } @@ -917,7 +917,7 @@ public static function findLoadSource(VipsSource $source): ?string /** * @throws Exception */ - public static function newFromSource(VipsSource $source, string $string_options = '', array $options = []): self + public static function newFromSource(Source $source, string $string_options = '', array $options = []): self { $loader = self::findLoadSource($source); if ($loader === null) { @@ -1075,7 +1075,7 @@ public function writeToArray(): array /** * @throws Exception */ - public function writeToTarget(VipsTarget $target, string $suffix, array $options = []): void + public function writeToTarget(Target $target, string $suffix, array $options = []): void { $filename = Utils::filenameGetFilename($suffix); $string_options = Utils::filenameGetOptions($suffix); diff --git a/src/ImageAutodoc.php b/src/ImageAutodoc.php index 5162dae..36f017b 100644 --- a/src/ImageAutodoc.php +++ b/src/ImageAutodoc.php @@ -171,11 +171,11 @@ * @throws Exception * @method static Image csvload(string $filename, array $options = []) Load csv. * @throws Exception - * @method static Image csvload_source(VipsSource $source, array $options = []) Load csv. + * @method static Image csvload_source(Source $source, array $options = []) Load csv. * @throws Exception * @method void csvsave(string $filename, array $options = []) Save image to csv. * @throws Exception - * @method void csvsave_target(VipsTarget $target, array $options = []) Save image to csv. + * @method void csvsave_target(Target $target, array $options = []) Save image to csv. * @throws Exception * @method Image dE00(Image $right, array $options = []) Calculate dE00. * @throws Exception @@ -227,7 +227,7 @@ * @throws Exception * @method static Image fitsload(string $filename, array $options = []) Load a FITS image. * @throws Exception - * @method static Image fitsload_source(VipsSource $source, array $options = []) Load FITS from a source. + * @method static Image fitsload_source(Source $source, array $options = []) Load FITS from a source. * @throws Exception * @method void fitssave(string $filename, array $options = []) Save image to fits file. * @throws Exception @@ -258,7 +258,7 @@ * @throws Exception * @method static Image gifload_buffer(string $buffer, array $options = []) Load GIF with libnsgif. * @throws Exception - * @method static Image gifload_source(VipsSource $source, array $options = []) Load gif from source. + * @method static Image gifload_source(Source $source, array $options = []) Load gif from source. * @throws Exception * @method Image globalbalance(array $options = []) Global balance an image mosaic. * @throws Exception @@ -273,13 +273,13 @@ * @throws Exception * @method static Image heifload_buffer(string $buffer, array $options = []) Load a HEIF image. * @throws Exception - * @method static Image heifload_source(VipsSource $source, array $options = []) Load a HEIF image. + * @method static Image heifload_source(Source $source, array $options = []) Load a HEIF image. * @throws Exception * @method void heifsave(string $filename, array $options = []) Save image in HEIF format. * @throws Exception * @method string heifsave_buffer(array $options = []) Save image in HEIF format. * @throws Exception - * @method void heifsave_target(VipsTarget $target, array $options = []) Save image in HEIF format. + * @method void heifsave_target(Target $target, array $options = []) Save image in HEIF format. * @throws Exception * @method Image hist_cum(array $options = []) Form cumulative histogram. * @throws Exception @@ -330,7 +330,7 @@ * @throws Exception * @method static Image jpegload_buffer(string $buffer, array $options = []) Load jpeg from buffer. * @throws Exception - * @method static Image jpegload_source(VipsSource $source, array $options = []) Load image from jpeg source. + * @method static Image jpegload_source(Source $source, array $options = []) Load image from jpeg source. * @throws Exception * @method void jpegsave(string $filename, array $options = []) Save image to jpeg file. * @throws Exception @@ -338,19 +338,19 @@ * @throws Exception * @method void jpegsave_mime(array $options = []) Save image to jpeg mime. * @throws Exception - * @method void jpegsave_target(VipsTarget $target, array $options = []) Save image to jpeg target. + * @method void jpegsave_target(Target $target, array $options = []) Save image to jpeg target. * @throws Exception * @method static Image jxlload(string $filename, array $options = []) Load JPEG-XL image. * @throws Exception * @method static Image jxlload_buffer(string $buffer, array $options = []) Load JPEG-XL image. * @throws Exception - * @method static Image jxlload_source(VipsSource $source, array $options = []) Load JPEG-XL image. + * @method static Image jxlload_source(Source $source, array $options = []) Load JPEG-XL image. * @throws Exception * @method void jxlsave(string $filename, array $options = []) Save image in JPEG-XL format. * @throws Exception * @method string jxlsave_buffer(array $options = []) Save image in JPEG-XL format. * @throws Exception - * @method void jxlsave_target(VipsTarget $target, array $options = []) Save image in JPEG-XL format. + * @method void jxlsave_target(Target $target, array $options = []) Save image in JPEG-XL format. * @throws Exception * @method Image labelregions(array $options = []) Label regions in an image. * @throws Exception @@ -409,13 +409,13 @@ * @throws Exception * @method static Image matrixload(string $filename, array $options = []) Load matrix. * @throws Exception - * @method static Image matrixload_source(VipsSource $source, array $options = []) Load matrix. + * @method static Image matrixload_source(Source $source, array $options = []) Load matrix. * @throws Exception * @method void matrixprint(array $options = []) Print matrix. * @throws Exception * @method void matrixsave(string $filename, array $options = []) Save image to matrix. * @throws Exception - * @method void matrixsave_target(VipsTarget $target, array $options = []) Save image to matrix. + * @method void matrixsave_target(Target $target, array $options = []) Save image to matrix. * @throws Exception * @method float max(array $options = []) Find image maximum. * @throws Exception @@ -441,13 +441,13 @@ * @throws Exception * @method static Image openslideload(string $filename, array $options = []) Load file with OpenSlide. * @throws Exception - * @method static Image openslideload_source(VipsSource $source, array $options = []) Load source with OpenSlide. + * @method static Image openslideload_source(Source $source, array $options = []) Load source with OpenSlide. * @throws Exception * @method static Image pdfload(string $filename, array $options = []) Load PDF from file. * @throws Exception * @method static Image pdfload_buffer(string $buffer, array $options = []) Load PDF from buffer. * @throws Exception - * @method static Image pdfload_source(VipsSource $source, array $options = []) Load PDF from source. + * @method static Image pdfload_source(Source $source, array $options = []) Load PDF from source. * @throws Exception * @method integer percent(float $percent, array $options = []) Find threshold for percent of pixels. * @throws Exception @@ -459,21 +459,21 @@ * @throws Exception * @method static Image pngload_buffer(string $buffer, array $options = []) Load png from buffer. * @throws Exception - * @method static Image pngload_source(VipsSource $source, array $options = []) Load png from source. + * @method static Image pngload_source(Source $source, array $options = []) Load png from source. * @throws Exception * @method void pngsave(string $filename, array $options = []) Save image to png file. * @throws Exception * @method string pngsave_buffer(array $options = []) Save image to png buffer. * @throws Exception - * @method void pngsave_target(VipsTarget $target, array $options = []) Save image to target as PNG. + * @method void pngsave_target(Target $target, array $options = []) Save image to target as PNG. * @throws Exception * @method static Image ppmload(string $filename, array $options = []) Load ppm from file. * @throws Exception - * @method static Image ppmload_source(VipsSource $source, array $options = []) Load ppm base class. + * @method static Image ppmload_source(Source $source, array $options = []) Load ppm base class. * @throws Exception * @method void ppmsave(string $filename, array $options = []) Save image to ppm file. * @throws Exception - * @method void ppmsave_target(VipsTarget $target, array $options = []) Save to ppm. + * @method void ppmsave_target(Target $target, array $options = []) Save to ppm. * @throws Exception * @method Image premultiply(array $options = []) Premultiply image alpha. * @throws Exception @@ -499,13 +499,13 @@ * @throws Exception * @method static Image radload_buffer(string $buffer, array $options = []) Load rad from buffer. * @throws Exception - * @method static Image radload_source(VipsSource $source, array $options = []) Load rad from source. + * @method static Image radload_source(Source $source, array $options = []) Load rad from source. * @throws Exception * @method void radsave(string $filename, array $options = []) Save image to Radiance file. * @throws Exception * @method string radsave_buffer(array $options = []) Save image to Radiance buffer. * @throws Exception - * @method void radsave_target(VipsTarget $target, array $options = []) Save image to Radiance target. + * @method void radsave_target(Target $target, array $options = []) Save image to Radiance target. * @throws Exception * @method Image rank(integer $width, integer $height, integer $index, array $options = []) Rank filter. * @throws Exception @@ -593,7 +593,7 @@ * @throws Exception * @method static Image svgload_buffer(string $buffer, array $options = []) Load SVG with rsvg. * @throws Exception - * @method static Image svgload_source(VipsSource $source, array $options = []) Load svg from source. + * @method static Image svgload_source(Source $source, array $options = []) Load svg from source. * @throws Exception * @method static Image switch(Image[]|Image $tests, array $options = []) Find the index of the first non-zero pixel in tests. * @throws Exception @@ -607,13 +607,13 @@ * @throws Exception * @method Image thumbnail_image(integer $width, array $options = []) Generate thumbnail from image. * @throws Exception - * @method static Image thumbnail_source(VipsSource $source, integer $width, array $options = []) Generate thumbnail from source. + * @method static Image thumbnail_source(Source $source, integer $width, array $options = []) Generate thumbnail from source. * @throws Exception * @method static Image tiffload(string $filename, array $options = []) Load tiff from file. * @throws Exception * @method static Image tiffload_buffer(string $buffer, array $options = []) Load tiff from buffer. * @throws Exception - * @method static Image tiffload_source(VipsSource $source, array $options = []) Load tiff from source. + * @method static Image tiffload_source(Source $source, array $options = []) Load tiff from source. * @throws Exception * @method void tiffsave(string $filename, array $options = []) Save image to tiff file. * @throws Exception @@ -629,23 +629,23 @@ * @throws Exception * @method static Image vipsload(string $filename, array $options = []) Load vips from file. * @throws Exception - * @method static Image vipsload_source(VipsSource $source, array $options = []) Load vips from source. + * @method static Image vipsload_source(Source $source, array $options = []) Load vips from source. * @throws Exception * @method void vipssave(string $filename, array $options = []) Save image to file in vips format. * @throws Exception - * @method void vipssave_target(VipsTarget $target, array $options = []) Save image to target in vips format. + * @method void vipssave_target(Target $target, array $options = []) Save image to target in vips format. * @throws Exception * @method static Image webpload(string $filename, array $options = []) Load webp from file. * @throws Exception * @method static Image webpload_buffer(string $buffer, array $options = []) Load webp from buffer. * @throws Exception - * @method static Image webpload_source(VipsSource $source, array $options = []) Load webp from source. + * @method static Image webpload_source(Source $source, array $options = []) Load webp from source. * @throws Exception * @method void webpsave(string $filename, array $options = []) Save image to webp file. * @throws Exception * @method string webpsave_buffer(array $options = []) Save image to webp buffer. * @throws Exception - * @method void webpsave_target(VipsTarget $target, array $options = []) Save image to webp target. + * @method void webpsave_target(Target $target, array $options = []) Save image to webp target. * @throws Exception * @method static Image worley(integer $width, integer $height, array $options = []) Make a worley noise image. * @throws Exception diff --git a/src/VipsSource.php b/src/Source.php similarity index 98% rename from src/VipsSource.php rename to src/Source.php index 1d15dda..60975fe 100644 --- a/src/VipsSource.php +++ b/src/Source.php @@ -2,7 +2,7 @@ namespace Jcupitt\Vips; -class VipsSource extends Connection +class Source extends Connection { /** * A pointer to the underlying VipsSource. This is the same as the diff --git a/src/VipsSourceCustom.php b/src/SourceCustom.php similarity index 97% rename from src/VipsSourceCustom.php rename to src/SourceCustom.php index 8c40a5b..80d67b8 100644 --- a/src/VipsSourceCustom.php +++ b/src/SourceCustom.php @@ -4,7 +4,7 @@ use Closure; -class VipsSourceCustom extends VipsSource +class SourceCustom extends Source { /** * A pointer to the underlying VipsSourceCustom. This is the same as the diff --git a/src/VipsSourceResource.php b/src/SourceResource.php similarity index 94% rename from src/VipsSourceResource.php rename to src/SourceResource.php index a521fbd..342e6ae 100644 --- a/src/VipsSourceResource.php +++ b/src/SourceResource.php @@ -4,7 +4,7 @@ use Closure; -class VipsSourceResource extends VipsSourceCustom +class SourceResource extends SourceCustom { /** * @var resource diff --git a/src/VipsTarget.php b/src/Target.php similarity index 98% rename from src/VipsTarget.php rename to src/Target.php index 3c7f1c4..568de55 100644 --- a/src/VipsTarget.php +++ b/src/Target.php @@ -2,7 +2,7 @@ namespace Jcupitt\Vips; -class VipsTarget extends Connection +class Target extends Connection { /** * A pointer to the underlying VipsTarget. This is the same as the diff --git a/src/VipsTargetCustom.php b/src/TargetCustom.php similarity index 96% rename from src/VipsTargetCustom.php rename to src/TargetCustom.php index 112231b..595bf9b 100644 --- a/src/VipsTargetCustom.php +++ b/src/TargetCustom.php @@ -4,7 +4,7 @@ use Closure; -class VipsTargetCustom extends VipsTarget +class TargetCustom extends Target { /** * A pointer to the underlying VipsTargetCustom. This is the same as the @@ -86,8 +86,8 @@ public function onEnd(Closure $callback): void /** * Attach a finish handler. - * For libvips 8.13 and later, this method is deprecated in favour of @see VipsTargetCustom::onEnd() - * @throws Exception + * For libvips 8.13 and later, this method is deprecated in favour of @throws Exception + * @see TargetCustom::onEnd() */ public function onFinish(Closure $callback): void { diff --git a/src/VipsTargetResource.php b/src/TargetResource.php similarity index 96% rename from src/VipsTargetResource.php rename to src/TargetResource.php index ffcaa04..b8e90a6 100644 --- a/src/VipsTargetResource.php +++ b/src/TargetResource.php @@ -2,7 +2,7 @@ namespace Jcupitt\Vips; -class VipsTargetResource extends VipsTargetCustom +class TargetResource extends TargetCustom { /** * @var resource diff --git a/tests/StreamingTest.php b/tests/StreamingTest.php index 694ccd2..9d07184 100644 --- a/tests/StreamingTest.php +++ b/tests/StreamingTest.php @@ -5,10 +5,10 @@ use Generator; use Jcupitt\Vips\Exception; use Jcupitt\Vips\Image; -use Jcupitt\Vips\VipsSource; -use Jcupitt\Vips\VipsSourceResource; -use Jcupitt\Vips\VipsTarget; -use Jcupitt\Vips\VipsTargetResource; +use Jcupitt\Vips\Source; +use Jcupitt\Vips\SourceResource; +use Jcupitt\Vips\Target; +use Jcupitt\Vips\TargetResource; use PHPUnit\Framework\TestCase; class StreamingTest extends TestCase @@ -19,15 +19,15 @@ class StreamingTest extends TestCase public function sourceAndTargetProvider(): Generator { $sources = [ - 'File' => fn() => VipsSource::newFromFile(__DIR__ . '/images/img_0076.jpg'), - 'Memory' => fn() => VipsSource::newFromMemory(file_get_contents(__DIR__ . '/images/img_0076.jpg')), - 'Resource' => fn() => new VipsSourceResource(fopen(__DIR__ . '/images/img_0076.jpg', 'rb')) + 'File' => fn() => Source::newFromFile(__DIR__ . '/images/img_0076.jpg'), + 'Memory' => fn() => Source::newFromMemory(file_get_contents(__DIR__ . '/images/img_0076.jpg')), + 'Resource' => fn() => new SourceResource(fopen(__DIR__ . '/images/img_0076.jpg', 'rb')) ]; $targets = [ - 'File' => fn() => VipsTarget::newToFile(tempnam(sys_get_temp_dir(), 'image')), - 'Memory' => fn() => VipsTarget::newToMemory(), - 'Resource' => fn() => new VipsTargetResource(fopen('php://memory', 'wb+')), - 'Resource(Not Readable)' => fn() => new VipsTargetResource(fopen('php://memory', 'wb')) + 'File' => fn() => Target::newToFile(tempnam(sys_get_temp_dir(), 'image')), + 'Memory' => fn() => Target::newToMemory(), + 'Resource' => fn() => new TargetResource(fopen('php://memory', 'wb+')), + 'Resource(Not Readable)' => fn() => new TargetResource(fopen('php://memory', 'wb')) ]; foreach ($sources as $sourceName => $source) { @@ -40,7 +40,7 @@ public function sourceAndTargetProvider(): Generator /** * @dataProvider sourceAndTargetProvider */ - public function testFromSourceToTarget(VipsSource $source, VipsTarget $target): void + public function testFromSourceToTarget(Source $source, Target $target): void { $image = Image::newFromSource($source); $image->writeToTarget($target, '.jpg[Q=95]'); @@ -56,8 +56,8 @@ public function testFromSourceToTarget(VipsSource $source, VipsTarget $target): */ public function testFromFileToFile(): void { - $source = VipsSource::newFromFile(__DIR__ . '/images/img_0076.jpg'); - $target = VipsTarget::newToFile(tempnam(sys_get_temp_dir(), 'image')); + $source = Source::newFromFile(__DIR__ . '/images/img_0076.jpg'); + $target = Target::newToFile(tempnam(sys_get_temp_dir(), 'image')); $image = Image::newFromSource($source); $image->writeToTarget($target, '.jpg[Q=95]'); @@ -73,8 +73,8 @@ public function testNoLeak(): void $leaked = false; for ($i = 0; $i < 10; $i++) { $filename = tempnam(sys_get_temp_dir(), 'image'); - $source = new VipsSourceResource(fopen(__DIR__ . '/images/img_0076.jpg', 'rb')); - $target = new VipsTargetResource(fopen($filename, 'wb+')); + $source = new SourceResource(fopen(__DIR__ . '/images/img_0076.jpg', 'rb')); + $target = new TargetResource(fopen($filename, 'wb+')); $image = Image::newFromSource($source); $image->writeToTarget($target, '.jpg[Q=95]'); unlink($filename); From 965a4bdba14998b990a8f2b36a899b1d315e1e0c Mon Sep 17 00:00:00 2001 From: L3tum <9307432+L3tum@users.noreply.github.com> Date: Wed, 31 May 2023 09:13:38 +0200 Subject: [PATCH 066/123] fix: Indentation --- src/FFI.php | 66 ++++++++++++++++++++++++++--------------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/src/FFI.php b/src/FFI.php index c701bf9..e684aec 100644 --- a/src/FFI.php +++ b/src/FFI.php @@ -446,42 +446,42 @@ private static function init(): void typedef struct _GClosureNotifyData GClosureNotifyData; struct _GClosureNotifyData { - void* data; - GClosureNotify notify; + void* data; + GClosureNotify notify; }; struct _GClosure { - /*< private >*/ - int ref_count : 15; /* (atomic) */ - /* meta_marshal is not used anymore but must be zero for historical reasons - as it was exposed in the G_CLOSURE_N_NOTIFIERS macro */ - int meta_marshal_nouse : 1; /* (atomic) */ - int n_guards : 1; /* (atomic) */ - int n_fnotifiers : 2; /* finalization notifiers (atomic) */ - int n_inotifiers : 8; /* invalidation notifiers (atomic) */ - int in_inotify : 1; /* (atomic) */ - int floating : 1; /* (atomic) */ - /*< protected >*/ - int derivative_flag : 1; /* (atomic) */ - /*< public >*/ - int in_marshal : 1; /* (atomic) */ - int is_invalid : 1; /* (atomic) */ - - /*< private >*/ marshaler marshal; - /*< protected >*/ void* data; - - /*< private >*/ GClosureNotifyData *notifiers; - - /* invariants/constraints: - * - ->marshal and ->data are _invalid_ as soon as ->is_invalid==TRUE - * - invocation of all inotifiers occurs prior to fnotifiers - * - order of inotifiers is random - * inotifiers may _not_ free/invalidate parameter values (e.g. ->data) - * - order of fnotifiers is random - * - each notifier may only be removed before or during its invocation - * - reference counting may only happen prior to fnotify invocation - * (in that sense, fnotifiers are really finalization handlers) - */ + /*< private >*/ + int ref_count : 15; /* (atomic) */ + /* meta_marshal is not used anymore but must be zero for historical reasons + as it was exposed in the G_CLOSURE_N_NOTIFIERS macro */ + int meta_marshal_nouse : 1; /* (atomic) */ + int n_guards : 1; /* (atomic) */ + int n_fnotifiers : 2; /* finalization notifiers (atomic) */ + int n_inotifiers : 8; /* invalidation notifiers (atomic) */ + int in_inotify : 1; /* (atomic) */ + int floating : 1; /* (atomic) */ + /*< protected >*/ + int derivative_flag : 1; /* (atomic) */ + /*< public >*/ + int in_marshal : 1; /* (atomic) */ + int is_invalid : 1; /* (atomic) */ + + /*< private >*/ marshaler marshal; + /*< protected >*/ void* data; + + /*< private >*/ GClosureNotifyData *notifiers; + + /* invariants/constraints: + * - ->marshal and ->data are _invalid_ as soon as ->is_invalid==TRUE + * - invocation of all inotifiers occurs prior to fnotifiers + * - order of inotifiers is random + * inotifiers may _not_ free/invalidate parameter values (e.g. ->data) + * - order of fnotifiers is random + * - each notifier may only be removed before or during its invocation + * - reference counting may only happen prior to fnotify invocation + * (in that sense, fnotifiers are really finalization handlers) + */ }; long g_signal_connect_closure(GObject* object, const char* detailed_signal, GClosure *closure, bool after); GClosure* g_closure_new_simple (int sizeof_closure, void* data); From eb3d0b237419d4200414a14199719edce8e64de7 Mon Sep 17 00:00:00 2001 From: L3tum <9307432+L3tum@users.noreply.github.com> Date: Wed, 31 May 2023 09:15:22 +0200 Subject: [PATCH 067/123] fix: Use g_value_get_object instead of cast --- src/GObject.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/GObject.php b/src/GObject.php index 8a747fc..7acb5f4 100644 --- a/src/GObject.php +++ b/src/GObject.php @@ -132,10 +132,7 @@ private static function getMarshaler(string $name, Closure $callback): ?Closure /** * Signature: void(VipsImage* image, void* progress, void* handle) */ - $vi = \FFI::cast( - FFI::ctypes('GObject'), - FFI::gobject()->g_value_get_pointer(\FFI::addr($params[0])) - ); + $vi = FFI::gobject()->g_value_get_object(\FFI::addr($params[0])); FFI::gobject()->g_object_ref($vi); $image = new Image($vi); $pr = \FFI::cast( From 238ea36eda2ec1902def4ed246a86c250ae1ee0b Mon Sep 17 00:00:00 2001 From: L3tum <9307432+L3tum@users.noreply.github.com> Date: Wed, 31 May 2023 09:16:49 +0200 Subject: [PATCH 068/123] fix: Better error description --- src/Image.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Image.php b/src/Image.php index 64d9902..5701de2 100644 --- a/src/Image.php +++ b/src/Image.php @@ -1082,7 +1082,7 @@ public function writeToTarget(Target $target, string $suffix, array $options = [ $saver = FFI::vips()->vips_foreign_find_save_target($filename); if ($saver === '') { - throw new Exception("can't save to target with filename $filename"); + throw new Exception("can't save to target with given suffix $filename"); } if ($string_options !== '') { From e5e4e3072eeb695238d3699283a24c65cec81bcb Mon Sep 17 00:00:00 2001 From: L3tum <9307432+L3tum@users.noreply.github.com> Date: Wed, 31 May 2023 09:18:01 +0200 Subject: [PATCH 069/123] fix: Comment Indentation --- src/Target.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Target.php b/src/Target.php index 568de55..9b31e61 100644 --- a/src/Target.php +++ b/src/Target.php @@ -24,7 +24,6 @@ public function __construct(\FFI\CData $pointer) * $target = VipsTarget.newToDescriptor(1) * Makes a descriptor attached to stdout. * You can pass this target to (for example) @see Image::writeToTarget() - * @throws Exception */ public static function newToDescriptor(int $descriptor): self From fdcbf623d0840a5e9547c5620ae97394cb506691 Mon Sep 17 00:00:00 2001 From: L3tum <9307432+L3tum@users.noreply.github.com> Date: Wed, 31 May 2023 09:23:55 +0200 Subject: [PATCH 070/123] fix: Replace Closure with callable --- src/GObject.php | 4 ++-- src/SourceCustom.php | 6 ++---- src/SourceResource.php | 2 -- src/TargetCustom.php | 12 +++++------- 4 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/GObject.php b/src/GObject.php index 7acb5f4..88998d4 100644 --- a/src/GObject.php +++ b/src/GObject.php @@ -102,7 +102,7 @@ public function unref(): void * The callback will be triggered every time this signal is issued on this instance. * @throws Exception */ - public function signalConnect(string $name, Closure $callback): void + public function signalConnect(string $name, callable $callback): void { $marshaler = self::getMarshaler($name, $callback); if ($marshaler === null) { @@ -114,7 +114,7 @@ public function signalConnect(string $name, Closure $callback): void FFI::gobject()->g_signal_connect_closure($this->pointer, $name, $gc, 0); } - private static function getMarshaler(string $name, Closure $callback): ?Closure + private static function getMarshaler(string $name, callable $callback): ?Closure { switch ($name) { case 'preeval': diff --git a/src/SourceCustom.php b/src/SourceCustom.php index 80d67b8..47d9bb4 100644 --- a/src/SourceCustom.php +++ b/src/SourceCustom.php @@ -2,8 +2,6 @@ namespace Jcupitt\Vips; -use Closure; - class SourceCustom extends Source { /** @@ -27,7 +25,7 @@ public function __construct() * to that number of bytes. If there is no more data available, it should * return null. */ - public function onRead(Closure $callback): void + public function onRead(callable $callback): void { $this->signalConnect('read', $callback); } @@ -45,7 +43,7 @@ public function onRead(Closure $callback): void * 1 => current position * 2 => end */ - public function onSeek(Closure $callback): void + public function onSeek(callable $callback): void { $this->signalConnect('seek', $callback); } diff --git a/src/SourceResource.php b/src/SourceResource.php index 342e6ae..9eaefdd 100644 --- a/src/SourceResource.php +++ b/src/SourceResource.php @@ -2,8 +2,6 @@ namespace Jcupitt\Vips; -use Closure; - class SourceResource extends SourceCustom { /** diff --git a/src/TargetCustom.php b/src/TargetCustom.php index 595bf9b..dedb149 100644 --- a/src/TargetCustom.php +++ b/src/TargetCustom.php @@ -2,8 +2,6 @@ namespace Jcupitt\Vips; -use Closure; - class TargetCustom extends Target { /** @@ -26,7 +24,7 @@ public function __construct() * and should return the number of bytes written. * @throws Exception */ - public function onWrite(Closure $callback): void + public function onWrite(callable $callback): void { $this->signalConnect('write', $callback); } @@ -41,7 +39,7 @@ public function onWrite(Closure $callback): void * target will be treated as unreadable and libvips will be unable to * write some file types (just TIFF, as of the time of writing). */ - public function onRead(Closure $callback): void + public function onRead(callable $callback): void { if (FFI::atLeast(8, 13)) { $this->signalConnect('read', $callback); @@ -61,7 +59,7 @@ public function onRead(Closure $callback): void * 1 => current position * 2 => end */ - public function onSeek(Closure $callback): void + public function onSeek(callable $callback): void { if (FFI::atLeast(8, 13)) { $this->signalConnect('seek', $callback); @@ -75,7 +73,7 @@ public function onSeek(Closure $callback): void * Automatically falls back to onFinish if libvips <8.13 * @throws Exception */ - public function onEnd(Closure $callback): void + public function onEnd(callable $callback): void { if (FFI::atLeast(8, 13)) { $this->signalConnect('end', $callback); @@ -89,7 +87,7 @@ public function onEnd(Closure $callback): void * For libvips 8.13 and later, this method is deprecated in favour of @throws Exception * @see TargetCustom::onEnd() */ - public function onFinish(Closure $callback): void + public function onFinish(callable $callback): void { $this->signalConnect('finish', $callback); } From 3e3d140efde7959e81233d58afde00b53bd51dc2 Mon Sep 17 00:00:00 2001 From: L3tum <9307432+L3tum@users.noreply.github.com> Date: Wed, 31 May 2023 10:00:49 +0200 Subject: [PATCH 071/123] fix: Remove GClosure implementation details and use hardcoded sizeof --- src/FFI.php | 56 +++++++++++++------------------------------------ src/GObject.php | 4 ++-- 2 files changed, 16 insertions(+), 44 deletions(-) diff --git a/src/FFI.php b/src/FFI.php index e684aec..f6cb23d 100644 --- a/src/FFI.php +++ b/src/FFI.php @@ -178,6 +178,18 @@ public static function shutDown(): void self::vips()->vips_shutdown(); } + public static function newGClosure(): \FFI\CData + { + // GClosure measures 32-bit with the first few fields until marshal + // Marshal is a function pointer, thus platform-dependant. + // Data is a pointer, thus platform-dependant. + // Notifiers is an array-pointer, thus platform-dependant. + // All in all it's basically 4 (bytes) + 3 * POINTER_SIZE + // However, gobject wants 8 (bytes) + 3 * POINTER_SIZE. + // I'm not sure where that extra byte comes from. Padding on 64-bit machines? + return self::gobject()->g_closure_new_simple(8 + 3 * PHP_INT_SIZE, null); + } + private static function libraryName(string $name, int $abi): string { switch (PHP_OS_FAMILY) { @@ -433,7 +445,7 @@ private static function init(): void const char* g_param_spec_get_blurb (GParamSpec* psp); -typedef struct _GClosure GClosure; +typedef void *GClosure; typedef void (*marshaler)( struct GClosure* closure, GValue* return_value, @@ -442,47 +454,7 @@ private static function init(): void void* invocation_hint, void* marshal_data ); - -typedef struct _GClosureNotifyData GClosureNotifyData; -struct _GClosureNotifyData -{ - void* data; - GClosureNotify notify; -}; -struct _GClosure -{ - /*< private >*/ - int ref_count : 15; /* (atomic) */ - /* meta_marshal is not used anymore but must be zero for historical reasons - as it was exposed in the G_CLOSURE_N_NOTIFIERS macro */ - int meta_marshal_nouse : 1; /* (atomic) */ - int n_guards : 1; /* (atomic) */ - int n_fnotifiers : 2; /* finalization notifiers (atomic) */ - int n_inotifiers : 8; /* invalidation notifiers (atomic) */ - int in_inotify : 1; /* (atomic) */ - int floating : 1; /* (atomic) */ - /*< protected >*/ - int derivative_flag : 1; /* (atomic) */ - /*< public >*/ - int in_marshal : 1; /* (atomic) */ - int is_invalid : 1; /* (atomic) */ - - /*< private >*/ marshaler marshal; - /*< protected >*/ void* data; - - /*< private >*/ GClosureNotifyData *notifiers; - - /* invariants/constraints: - * - ->marshal and ->data are _invalid_ as soon as ->is_invalid==TRUE - * - invocation of all inotifiers occurs prior to fnotifiers - * - order of inotifiers is random - * inotifiers may _not_ free/invalidate parameter values (e.g. ->data) - * - order of fnotifiers is random - * - each notifier may only be removed before or during its invocation - * - reference counting may only happen prior to fnotify invocation - * (in that sense, fnotifiers are really finalization handlers) - */ -}; +void g_closure_set_marshal(GClosure* closure, marshaler marshal); long g_signal_connect_closure(GObject* object, const char* detailed_signal, GClosure *closure, bool after); GClosure* g_closure_new_simple (int sizeof_closure, void* data); EOS; diff --git a/src/GObject.php b/src/GObject.php index 88998d4..0047e20 100644 --- a/src/GObject.php +++ b/src/GObject.php @@ -109,8 +109,8 @@ public function signalConnect(string $name, callable $callback): void throw new Exception("unsupported signal $name"); } - $gc = FFI::gobject()->g_closure_new_simple(\FFI::sizeof(FFI::ctypes('GClosure')), null); - $gc->marshal = $marshaler; + $gc = FFI::newGClosure(); + FFI::gobject()->g_closure_set_marshal($gc, $marshaler); FFI::gobject()->g_signal_connect_closure($this->pointer, $name, $gc, 0); } From 8196dafe21d739698cabcb15ba707c8a25d17b1e Mon Sep 17 00:00:00 2001 From: Kleis Auke Wolthuizen Date: Thu, 29 Jun 2023 21:46:24 +0200 Subject: [PATCH 072/123] Fix CI (#202) --- src/FFI.php | 2 +- src/Image.php | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/FFI.php b/src/FFI.php index 4f9d726..16cc02d 100644 --- a/src/FFI.php +++ b/src/FFI.php @@ -254,7 +254,7 @@ private static function init(): void break; } catch (\FFI\Exception $e) { Utils::debugLog("init", [ - "msg" => "library load failed", + "msg" => "library load failed", "exception" => $e->getMessage() ]); } diff --git a/src/Image.php b/src/Image.php index ab2c2b4..39646f1 100644 --- a/src/Image.php +++ b/src/Image.php @@ -1889,7 +1889,8 @@ public function bandrank($other, array $options = []): Image 'bandrank', null, [array_merge([$this], $other)], - $options); + $options + ); } /** From cbd5b0528578e51b0b050be4016762ef6c783bee Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 30 Jun 2023 01:36:58 +0100 Subject: [PATCH 073/123] update enums A few were dropped in https://github.com/libvips/php-vips/pull/191 --- src/ForeignDzLayout.php | 1 + src/ForeignHeifEncoder.php | 57 ++++++++++++++++++++++++++++++++++++++ src/ForeignPpmFormat.php | 1 + src/ImageAutodoc.php | 44 +++++++++++++++++++++++------ src/OperationMath.php | 6 ++++ src/OperationMath2.php | 1 + src/TextWrap.php | 56 +++++++++++++++++++++++++++++++++++++ 7 files changed, 158 insertions(+), 8 deletions(-) create mode 100644 src/ForeignHeifEncoder.php create mode 100644 src/TextWrap.php diff --git a/src/ForeignDzLayout.php b/src/ForeignDzLayout.php index 021d75f..ae36259 100644 --- a/src/ForeignDzLayout.php +++ b/src/ForeignDzLayout.php @@ -53,4 +53,5 @@ abstract class ForeignDzLayout const ZOOMIFY = 'zoomify'; const GOOGLE = 'google'; const IIIF = 'iiif'; + const IIIF3 = 'iiif3'; } diff --git a/src/ForeignHeifEncoder.php b/src/ForeignHeifEncoder.php new file mode 100644 index 0000000..718644b --- /dev/null +++ b/src/ForeignHeifEncoder.php @@ -0,0 +1,57 @@ + + * @copyright 2016 John Cupitt + * @license https://opensource.org/licenses/MIT MIT + * @link https://github.com/jcupitt/php-vips + */ + +namespace Jcupitt\Vips; + +/** + * The ForeignHeifEncoder enum. + * @category Images + * @package Jcupitt\Vips + * @author John Cupitt + * @copyright 2016 John Cupitt + * @license https://opensource.org/licenses/MIT MIT + * @link https://github.com/jcupitt/php-vips + */ +abstract class ForeignHeifEncoder +{ + const AUTO = 'auto'; + const AOM = 'aom'; + const RAV1E = 'rav1e'; + const SVT = 'svt'; + const X265 = 'x265'; +} diff --git a/src/ForeignPpmFormat.php b/src/ForeignPpmFormat.php index ed28dc8..e476cc6 100644 --- a/src/ForeignPpmFormat.php +++ b/src/ForeignPpmFormat.php @@ -53,4 +53,5 @@ abstract class ForeignPpmFormat const PGM = 'pgm'; const PPM = 'ppm'; const PFM = 'pfm'; + const PNM = 'pnm'; } diff --git a/src/ImageAutodoc.php b/src/ImageAutodoc.php index 36f017b..e8b80e7 100644 --- a/src/ImageAutodoc.php +++ b/src/ImageAutodoc.php @@ -203,6 +203,8 @@ * @throws Exception * @method string dzsave_buffer(array $options = []) Save image to dz buffer. * @throws Exception + * @method void dzsave_target(Target $target, array $options = []) Save image to deepzoom target. + * @throws Exception * @method Image embed(integer $x, integer $y, integer $width, integer $height, array $options = []) Embed an image in a larger image. * @throws Exception * @method Image extract_area(integer $left, integer $top, integer $width, integer $height, array $options = []) Extract an area from an image. @@ -260,6 +262,12 @@ * @throws Exception * @method static Image gifload_source(Source $source, array $options = []) Load gif from source. * @throws Exception + * @method void gifsave(string $filename, array $options = []) Save as gif. + * @throws Exception + * @method string gifsave_buffer(array $options = []) Save as gif. + * @throws Exception + * @method void gifsave_target(Target $target, array $options = []) Save as gif. + * @throws Exception * @method Image globalbalance(array $options = []) Global balance an image mosaic. * @throws Exception * @method Image gravity(string $direction, integer $width, integer $height, array $options = []) Place an image within a larger image with a certain gravity. @@ -326,6 +334,18 @@ * @method Image join(Image $in2, string $direction, array $options = []) Join a pair of images. * @see Direction for possible values for $direction * @throws Exception + * @method static Image jp2kload(string $filename, array $options = []) Load JPEG2000 image. + * @throws Exception + * @method static Image jp2kload_buffer(string $buffer, array $options = []) Load JPEG2000 image. + * @throws Exception + * @method static Image jp2kload_source(Source $source, array $options = []) Load JPEG2000 image. + * @throws Exception + * @method void jp2ksave(string $filename, array $options = []) Save image in JPEG2000 format. + * @throws Exception + * @method string jp2ksave_buffer(array $options = []) Save image in JPEG2000 format. + * @throws Exception + * @method void jp2ksave_target(Target $target, array $options = []) Save image in JPEG2000 format. + * @throws Exception * @method static Image jpegload(string $filename, array $options = []) Load jpeg from file. * @throws Exception * @method static Image jpegload_buffer(string $buffer, array $options = []) Load jpeg from buffer. @@ -358,11 +378,11 @@ * @throws Exception * @method Image linecache(array $options = []) Cache an image as a set of lines. * @throws Exception - * @method static Image logmat(float $sigma, float $min_ampl, array $options = []) Make a laplacian of gaussian image. + * @method static Image logmat(float $sigma, float $min_ampl, array $options = []) Make a Laplacian of Gaussian image. * @throws Exception - * @method static Image magickload(string $filename, array $options = []) Load file with ImageMagick. + * @method static Image magickload(string $filename, array $options = []) Load file with ImageMagick7. * @throws Exception - * @method static Image magickload_buffer(string $buffer, array $options = []) Load buffer with ImageMagick. + * @method static Image magickload_buffer(string $buffer, array $options = []) Load buffer with ImageMagick7. * @throws Exception * @method void magicksave(string $filename, array $options = []) Save file with ImageMagick. * @throws Exception @@ -461,9 +481,9 @@ * @throws Exception * @method static Image pngload_source(Source $source, array $options = []) Load png from source. * @throws Exception - * @method void pngsave(string $filename, array $options = []) Save image to png file. + * @method void pngsave(string $filename, array $options = []) Save image to file as PNG. * @throws Exception - * @method string pngsave_buffer(array $options = []) Save image to png buffer. + * @method string pngsave_buffer(array $options = []) Save image to buffer as PNG. * @throws Exception * @method void pngsave_target(Target $target, array $options = []) Save image to target as PNG. * @throws Exception @@ -477,6 +497,8 @@ * @throws Exception * @method Image premultiply(array $options = []) Premultiply image alpha. * @throws Exception + * @method Image prewitt(array $options = []) Prewitt edge detector. + * @throws Exception * @method array profile(array $options = []) Find image profiles. * Return array with: [ * 'columns' => @type Image First non-zero pixel in column @@ -557,6 +579,8 @@ * @throws Exception * @method Image scale(array $options = []) Scale an image to uchar. * @throws Exception + * @method Image scharr(array $options = []) Scharr edge detector. + * @throws Exception * @method Image sequential(array $options = []) Check sequential access. * @throws Exception * @method Image sharpen(array $options = []) Unsharp masking for print. @@ -619,6 +643,8 @@ * @throws Exception * @method string tiffsave_buffer(array $options = []) Save image to tiff buffer. * @throws Exception + * @method void tiffsave_target(Target $target, array $options = []) Save image to tiff target. + * @throws Exception * @method Image tilecache(array $options = []) Cache an image as a set of tiles. * @throws Exception * @method static Image tonelut(array $options = []) Build a look-up table. @@ -641,11 +667,13 @@ * @throws Exception * @method static Image webpload_source(Source $source, array $options = []) Load webp from source. * @throws Exception - * @method void webpsave(string $filename, array $options = []) Save image to webp file. + * @method void webpsave(string $filename, array $options = []) Save as WebP. + * @throws Exception + * @method string webpsave_buffer(array $options = []) Save as WebP. * @throws Exception - * @method string webpsave_buffer(array $options = []) Save image to webp buffer. + * @method void webpsave_mime(array $options = []) Save image to webp mime. * @throws Exception - * @method void webpsave_target(Target $target, array $options = []) Save image to webp target. + * @method void webpsave_target(Target $target, array $options = []) Save as WebP. * @throws Exception * @method static Image worley(integer $width, integer $height, array $options = []) Make a worley noise image. * @throws Exception diff --git a/src/OperationMath.php b/src/OperationMath.php index 52ffef1..857b488 100644 --- a/src/OperationMath.php +++ b/src/OperationMath.php @@ -59,4 +59,10 @@ abstract class OperationMath const LOG10 = 'log10'; const EXP = 'exp'; const EXP10 = 'exp10'; + const SINH = 'sinh'; + const COSH = 'cosh'; + const TANH = 'tanh'; + const ASINH = 'asinh'; + const ACOSH = 'acosh'; + const ATANH = 'atanh'; } diff --git a/src/OperationMath2.php b/src/OperationMath2.php index 9e86093..b1fc69e 100644 --- a/src/OperationMath2.php +++ b/src/OperationMath2.php @@ -51,4 +51,5 @@ abstract class OperationMath2 { const POW = 'pow'; const WOP = 'wop'; + const ATAN2 = 'atan2'; } diff --git a/src/TextWrap.php b/src/TextWrap.php new file mode 100644 index 0000000..db6ff14 --- /dev/null +++ b/src/TextWrap.php @@ -0,0 +1,56 @@ + + * @copyright 2016 John Cupitt + * @license https://opensource.org/licenses/MIT MIT + * @link https://github.com/jcupitt/php-vips + */ + +namespace Jcupitt\Vips; + +/** + * The TextWrap enum. + * @category Images + * @package Jcupitt\Vips + * @author John Cupitt + * @copyright 2016 John Cupitt + * @license https://opensource.org/licenses/MIT MIT + * @link https://github.com/jcupitt/php-vips + */ +abstract class TextWrap +{ + const WORD = 'word'; + const CHAR = 'char'; + const WORD_CHAR = 'word-char'; + const NONE = 'none'; +} From a38b72c0a131da073a9aa119c205286bf61003f6 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 30 Jun 2023 01:48:52 +0100 Subject: [PATCH 074/123] Improve library finding (#206) * improve library finding This PR adds a libraryLoad() function which searches a path for a library that supports an API, then uses that to load libvips, libglib and libgobject. This fixes #178 and #201 See also https://github.com/libvips/php-vips/pull/198 Tested on macOS 13.4 * fix formatting --- src/FFI.php | 70 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 45 insertions(+), 25 deletions(-) diff --git a/src/FFI.php b/src/FFI.php index ce00d33..7ed1f3b 100644 --- a/src/FFI.php +++ b/src/FFI.php @@ -204,6 +204,29 @@ private static function libraryName(string $name, int $abi): string // most *nix return "$name.so.$abi"; } + + return null; + } + + private static function libraryLoad( + array $libraryPaths, + string $libraryName, + string $interface + ): \FFI { + Utils::debugLog("trying to open", ["libraryName" => $libraryName]); + foreach ($libraryPaths as $path) { + Utils::debugLog("trying path", ["path" => $path]); + try { + $library = \FFI::cdef($interface, $path . $libraryName); + Utils::debugLog("success", []); + return $library; + } catch (\FFI\Exception $e) { + Utils::debugLog("init", [ + "msg" => "library load failed", + "exception" => $e->getMessage() + ]); + } + } } private static function init(): void @@ -251,30 +274,15 @@ private static function init(): void $libraryPaths[] = "/opt/homebrew/lib/"; // Homebrew on Apple Silicon } - // attempt to open libraries using the system library search method - // (no prefix) and a couple of fallback paths, if any - $vips = null; - foreach ($libraryPaths as $path) { - Utils::debugLog("init", ["path" => $path]); - - try { - $vips = \FFI::cdef(<< "library load failed", - "exception" => $e->getMessage() - ]); - } - } + $vips = self::libraryLoad($libraryPaths, $vips_libname, <<vips_leak_set(1); From bbedf8de38f6012bc30e7e946e22e9fdb35e504a Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 30 Jun 2023 01:55:06 +0100 Subject: [PATCH 075/123] add setProgress And an example program to demo it. Uses signalConnect, see https://github.com/libvips/php-vips/pull/191 --- examples/progress.php | 28 ++++++++++++++++++++++++++++ src/FFI.php | 1 + src/GObject.php | 2 +- src/Image.php | 17 +++++++++++++++++ 4 files changed, 47 insertions(+), 1 deletion(-) create mode 100755 examples/progress.php diff --git a/examples/progress.php b/examples/progress.php new file mode 100755 index 0000000..eca9a57 --- /dev/null +++ b/examples/progress.php @@ -0,0 +1,28 @@ +#!/usr/bin/env php +setProgress(true); + +$image->signalConnect("preeval", function($image, $progress) { + echo "preeval:\n"; +}); +$image->signalConnect("eval", function($image, $progress) { + echo "eval: $progress->percent % complete\r"; +}); + +$image->signalConnect("posteval", function($image, $progress) { + echo "\nposteval:\n"; +}); + +// trigger evaluation +$image->avg(); + +$image = null; + +Vips\FFI::shutDown(); diff --git a/src/FFI.php b/src/FFI.php index 7ed1f3b..8b2859f 100644 --- a/src/FFI.php +++ b/src/FFI.php @@ -808,6 +808,7 @@ private static function init(): void "VipsSourceCustom" => self::$vips->type("VipsSourceCustom*"), "VipsTarget" => self::$vips->type("VipsTarget*"), "VipsTargetCustom" => self::$vips->type("VipsTargetCustom*"), + "VipsProgress" => self::$vips->type("VipsProgress*"), ]; self::$gtypes = [ diff --git a/src/GObject.php b/src/GObject.php index 0047e20..97be094 100644 --- a/src/GObject.php +++ b/src/GObject.php @@ -128,7 +128,7 @@ private static function getMarshaler(string $name, callable $callback): ?Closure CData $hint, ?CData $data ) use (&$callback) { - assert($numberOfParams === 3); + assert($numberOfParams === 2); /** * Signature: void(VipsImage* image, void* progress, void* handle) */ diff --git a/src/Image.php b/src/Image.php index e9c6c78..f1f4056 100644 --- a/src/Image.php +++ b/src/Image.php @@ -1296,6 +1296,23 @@ public function remove(string $name): void } } + /** + * Enable progress reporting on an image. + * + * The preeval, eval and posteval signals will be emitted on the + * most-downstream image for which setProgress() was enabled. @see + * GObject::signalConnect(). + * + * @param bool $progress TRUE to enable progress reporting. + * + * @return void + */ + public function setProgress(bool $progress): void + { + FFI::vips()->vips_image_set_progress($this->pointer, $progress); + + } + /** * Makes a string-ified version of the Image. * From b15049bff13177ed815b6c8694a090ba29d8b9d3 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 30 Jun 2023 02:09:42 +0100 Subject: [PATCH 076/123] add streaming-custom example See https://github.com/libvips/php-vips/pull/191 --- CHANGELOG.md | 6 +++- examples/streaming-custom.php | 56 +++++++++++++++++++++++++++++++++++ src/GObject.php | 10 +++---- 3 files changed, 66 insertions(+), 6 deletions(-) create mode 100755 examples/streaming-custom.php diff --git a/CHANGELOG.md b/CHANGELOG.md index de03a92..7c88bc9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,14 @@ All notable changes to `:vips` will be documented in this file. ## master -- improve FFI startup [West14] +- improve FFI startup [West14, jcupitt] - revise example use of composer [jcupitt] - fix bandrank [axkirillov] - fix FFI startup log error [ganicus] +- add Source, Target, SourceResource, TargetResource, SourceCustom, + TargetCustom [L3tum] +- add setProgress and progress example [jcupitt] +- add streaming-custom example [jcupitt] ## 2.1.1 - 2022-11-13 diff --git a/examples/streaming-custom.php b/examples/streaming-custom.php new file mode 100755 index 0000000..fa65405 --- /dev/null +++ b/examples/streaming-custom.php @@ -0,0 +1,56 @@ +#!/usr/bin/env php +onRead(function ($bufferLength) use (&$in_file) { + // return 0 for EOF, -ve for read error + return fread($in_file, $bufferLength); +}); +// seek is optional +$source->onSeek(function ($offset, $whence) use (&$in_file) { + if (fseek($in_file, $offset, $whence)) { + return -1; + } + + return ftell($in_file); +}); + +// open for write and read ... formats like tiff need to be able to seek back +// in the output and update bytes later +$out_file = fopen($argv[2], 'w+'); +$target = new Vips\TargetCustom(); +$target->onWrite(function ($buffer) use (&$out_file) { + $result = fwrite($out_file, $buffer); + if ($result === false) { + // IO error + return -1; + } + else + return $result; +}); +// read and seek are optional +$target->onSeek(function ($offset, $whence) use (&$out_file) { + if (fseek($out_file, $offset, $whence)) { + return -1; + } + + return ftell($out_file); +}); +$target->onRead(function ($bufferLength) use (&$out_file) { + return fread($out_file, $bufferLength); +}); + +$image = Vips\Image::newFromSource($source); +$image->writeToTarget($target, $argv[3]); + diff --git a/src/GObject.php b/src/GObject.php index 97be094..964393a 100644 --- a/src/GObject.php +++ b/src/GObject.php @@ -151,7 +151,7 @@ private static function getMarshaler(string $name, callable $callback): ?Closure CData $hint, ?CData $data ) use (&$callback): void { - assert($numberOfParams === 4); + assert($numberOfParams === 3); /* * Signature: gint64(VipsSourceCustom* source, void* buffer, gint64 length, void* handle) */ @@ -179,7 +179,7 @@ private static function getMarshaler(string $name, callable $callback): ?Closure CData $hint, ?CData $data ) use (&$callback): void { - assert($numberOfParams === 4); + assert($numberOfParams === 3); /* * Signature: gint64(VipsSourceCustom* source, gint64 offset, int whence, void* handle) */ @@ -200,7 +200,7 @@ private static function getMarshaler(string $name, callable $callback): ?Closure CData $hint, ?CData $data ) use (&$callback): void { - assert($numberOfParams === 4); + assert($numberOfParams === 3); /* * Signature: gint64(VipsTargetCustom* target, void* buffer, gint64 length, void* handle) */ @@ -223,7 +223,7 @@ private static function getMarshaler(string $name, callable $callback): ?Closure CData $hint, ?CData $data ) use (&$callback): void { - assert($numberOfParams === 2); + assert($numberOfParams === 0); /** * Signature: void(VipsTargetCustom* target, void* handle) */ @@ -242,7 +242,7 @@ private static function getMarshaler(string $name, callable $callback): ?Closure CData $hint, ?CData $data ) use (&$callback): void { - assert($numberOfParams === 2); + assert($numberOfParams === 0); /** * Signature: int(VipsTargetCustom* target, void* handle) */ From 908bf980681f9e2fb6d417974fed395555f7cd7d Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 30 Jun 2023 02:15:12 +0100 Subject: [PATCH 077/123] fix formatting --- examples/progress.php | 8 ++++---- examples/streaming-custom.php | 7 +++---- src/Image.php | 3 +-- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/examples/progress.php b/examples/progress.php index eca9a57..d53faac 100755 --- a/examples/progress.php +++ b/examples/progress.php @@ -9,14 +9,14 @@ $image = Vips\Image::black(1, 1000000); $image->setProgress(true); -$image->signalConnect("preeval", function($image, $progress) { +$image->signalConnect("preeval", function ($image, $progress) { echo "preeval:\n"; }); -$image->signalConnect("eval", function($image, $progress) { +$image->signalConnect("eval", function ($image, $progress) { echo "eval: $progress->percent % complete\r"; -}); +}); -$image->signalConnect("posteval", function($image, $progress) { +$image->signalConnect("posteval", function ($image, $progress) { echo "\nposteval:\n"; }); diff --git a/examples/streaming-custom.php b/examples/streaming-custom.php index fa65405..d3b78f8 100755 --- a/examples/streaming-custom.php +++ b/examples/streaming-custom.php @@ -27,7 +27,7 @@ }); // open for write and read ... formats like tiff need to be able to seek back -// in the output and update bytes later +// in the output and update bytes later $out_file = fopen($argv[2], 'w+'); $target = new Vips\TargetCustom(); $target->onWrite(function ($buffer) use (&$out_file) { @@ -35,9 +35,9 @@ if ($result === false) { // IO error return -1; - } - else + } else { return $result; + } }); // read and seek are optional $target->onSeek(function ($offset, $whence) use (&$out_file) { @@ -53,4 +53,3 @@ $image = Vips\Image::newFromSource($source); $image->writeToTarget($target, $argv[3]); - diff --git a/src/Image.php b/src/Image.php index f1f4056..1b6c257 100644 --- a/src/Image.php +++ b/src/Image.php @@ -1299,7 +1299,7 @@ public function remove(string $name): void /** * Enable progress reporting on an image. * - * The preeval, eval and posteval signals will be emitted on the + * The preeval, eval and posteval signals will be emitted on the * most-downstream image for which setProgress() was enabled. @see * GObject::signalConnect(). * @@ -1310,7 +1310,6 @@ public function remove(string $name): void public function setProgress(bool $progress): void { FFI::vips()->vips_image_set_progress($this->pointer, $progress); - } /** From ccc7d95c7e6022b95c52f379e26661aa509c52a7 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sun, 9 Jul 2023 12:16:52 +0100 Subject: [PATCH 078/123] link to main API docs for newFromFile see https://github.com/libvips/php-vips/issues/208 --- src/Image.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Image.php b/src/Image.php index ab2c2b4..7d30ff7 100644 --- a/src/Image.php +++ b/src/Image.php @@ -96,8 +96,10 @@ * default mode is `random`, this allows for full random access to image pixels, * but is slower and needs more memory. * - * You can also load formatted images from - * strings or create images from PHP arrays. + * You can also load formatted images from strings or create images from + * PHP arrays. + * + * See the [main libvips documentation](https://www.libvips.org/API/current/VipsImage.html#vips-image-new-from-file) for a more detailed explaination. * * The next line: * @@ -695,6 +697,8 @@ public static function findLoad(string $filename): ?string /** * Create a new Image from a file on disc. * + * See the [main libvips documentation](https://www.libvips.org/API/current/VipsImage.html#vips-image-new-from-file) for a more detailed explaination. + * * @param string $name The file to open. * @param array $options Any options to pass on to the load operation. * From e9c57c5a3cabde7838c9d57e6ae69921b69ab7bf Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 18 Jul 2023 06:46:33 +0100 Subject: [PATCH 079/123] fix layout warning --- src/Image.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Image.php b/src/Image.php index 5bf74a1..bf2aeb3 100644 --- a/src/Image.php +++ b/src/Image.php @@ -96,10 +96,12 @@ * default mode is `random`, this allows for full random access to image pixels, * but is slower and needs more memory. * - * You can also load formatted images from strings or create images from + * You can also load formatted images from strings or create images from * PHP arrays. * - * See the [main libvips documentation](https://www.libvips.org/API/current/VipsImage.html#vips-image-new-from-file) for a more detailed explaination. + * See the [main libvips + * documentation](https://www.libvips.org/API/current/VipsImage.html#vips-image-new-from-file) + * for a more detailed explaination. * * The next line: * @@ -697,7 +699,9 @@ public static function findLoad(string $filename): ?string /** * Create a new Image from a file on disc. * - * See the [main libvips documentation](https://www.libvips.org/API/current/VipsImage.html#vips-image-new-from-file) for a more detailed explaination. + * See the [main libvips + * documentation](https://www.libvips.org/API/current/VipsImage.html#vips-image-new-from-file) + * for a more detailed explaination. * * @param string $name The file to open. * @param array $options Any options to pass on to the load operation. From ce833a6d38cea395aad5cd24f9c142ed36ab70b4 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Thu, 17 Aug 2023 00:49:01 +0100 Subject: [PATCH 080/123] update for 2.2 --- CHANGELOG.md | 2 ++ README.md | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c88bc9..9c714db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ All notable changes to `:vips` will be documented in this file. ## master +## 2.2.0 - 2023-08-17 + - improve FFI startup [West14, jcupitt] - revise example use of composer [jcupitt] - fix bandrank [axkirillov] diff --git a/README.md b/README.md index 4747982..a68e3ad 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ to your `composer.json`: ``` "require": { - "jcupitt/vips" : "2.1.0" + "jcupitt/vips" : "2.2.0" } ``` From 97c3fd8b42ab9eec38cee027117855479835f60a Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 6 Sep 2023 14:54:53 +0100 Subject: [PATCH 081/123] add getFields() and an example program see https://github.com/libvips/php-vips/discussions/218 --- CHANGELOG.md | 2 ++ examples/fields.php | 25 +++++++++++++++++++++++++ src/FFI.php | 1 + src/Image.php | 21 +++++++++++++++++++++ 4 files changed, 49 insertions(+) create mode 100755 examples/fields.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c714db..18b8c74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ All notable changes to `:vips` will be documented in this file. ## master +- add getFields() to fetch an array of field names + ## 2.2.0 - 2023-08-17 - improve FFI startup [West14, jcupitt] diff --git a/examples/fields.php b/examples/fields.php new file mode 100755 index 0000000..b3f6f62 --- /dev/null +++ b/examples/fields.php @@ -0,0 +1,25 @@ +#!/usr/bin/env php +getFields(); + +echo "$argv[1]\n"; +foreach ($names as &$name) { + $value = $im->get($name); + echo "$name: $value\n"; +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: expandtab sw=4 ts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 + */ diff --git a/src/FFI.php b/src/FFI.php index 8b2859f..304a3b4 100644 --- a/src/FFI.php +++ b/src/FFI.php @@ -348,6 +348,7 @@ private static function init(): void $glib_decls = $typedefs . <<get('ipct-data')`. * + * Use `$image->getFields()` to get an array of all the possible field names. + * * Next we have: * * ```php @@ -1217,6 +1219,25 @@ public function typeOf(string $name): int return $this->getType($name); } + /** + * Get the field names available for an image. + * + * @return Array + */ + public function getFields(): array + { + $str_array = FFI::vips()->vips_image_get_fields($this->pointer); + + $fields = []; + for ($i = 0; $str_array[$i] != null; $i++) { + array_push($fields, \FFI::string($str_array[$i])); + } + + FFI::glib()->g_free($str_array); + + return $fields; + } + /** * Set any property on the underlying image. * From 14df89980b4186209bbfe6e920fceec817b6ef8f Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 26 Sep 2023 10:07:04 +0100 Subject: [PATCH 082/123] update for 2.3.0 --- CHANGELOG.md | 4 +++- README.md | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 18b8c74..ee09959 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,9 @@ All notable changes to `:vips` will be documented in this file. ## master -- add getFields() to fetch an array of field names +## 2.3.0 - 2023-09-26 + +- add getFields() to fetch an array of field names [jcupitt] ## 2.2.0 - 2023-08-17 diff --git a/README.md b/README.md index a68e3ad..d6b4bad 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ to your `composer.json`: ``` "require": { - "jcupitt/vips" : "2.2.0" + "jcupitt/vips" : "2.3.0" } ``` From 24831a1cf95f85f0ca2fc36b6405061505f7c1ca Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Thu, 9 Nov 2023 20:19:19 +0000 Subject: [PATCH 083/123] add animate-image.php example --- CHANGELOG.md | 2 ++ examples/animate-image.php | 41 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100755 examples/animate-image.php diff --git a/CHANGELOG.md b/CHANGELOG.md index ee09959..032a444 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ All notable changes to `:vips` will be documented in this file. ## master +- added `animate-image.php` example [jcupitt] + ## 2.3.0 - 2023-09-26 - add getFields() to fetch an array of field names [jcupitt] diff --git a/examples/animate-image.php b/examples/animate-image.php new file mode 100755 index 0000000..c1feb30 --- /dev/null +++ b/examples/animate-image.php @@ -0,0 +1,41 @@ +#!/usr/bin/env php + 300, "rgba" => true]); +$animation = NULL; +$delay = []; + +for ($x = 0; $x < $image->width + $text->width; $x += 10) +{ + // append the frame to the image vertically ... we make a very tall, thin + // strip of frames to save + $frame = $image->composite2($text, "over", [ + "x" => $x - $text->width, + "y" => $image->height / 2 - $text->height / 2 + ]); + if ($animation == NULL) + $animation = $frame; + else + $animation = $animation->join($frame, "vertical"); + + // frame delay in ms + array_push($delay, 30); +} + +// set animation properties +$animation->set("delay", $delay); +$animation->set("loop", 0); +$animation->set("page-height", $image->height); + +$animation->writeToFile($argv[2]); From f8b965b527f218435358068ad568163187665901 Mon Sep 17 00:00:00 2001 From: Mahmood Dehghani Date: Thu, 28 Dec 2023 23:44:17 +0330 Subject: [PATCH 084/123] removed unreachable return statement (#228) --- src/FFI.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/FFI.php b/src/FFI.php index 304a3b4..194db6f 100644 --- a/src/FFI.php +++ b/src/FFI.php @@ -204,8 +204,6 @@ private static function libraryName(string $name, int $abi): string // most *nix return "$name.so.$abi"; } - - return null; } private static function libraryLoad( From 2dd6ae6f96a22ba666fbeb05f838bf7aeed03fe6 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Thu, 28 Dec 2023 20:16:12 +0000 Subject: [PATCH 085/123] fix formatting for phpcs --- examples/animate-image.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/examples/animate-image.php b/examples/animate-image.php index c1feb30..1525259 100755 --- a/examples/animate-image.php +++ b/examples/animate-image.php @@ -13,21 +13,22 @@ $image = Vips\Image::newFromFile($argv[1]); $text = Vips\Image::text($argv[3], ["dpi" => 300, "rgba" => true]); -$animation = NULL; +$animation = null; $delay = []; -for ($x = 0; $x < $image->width + $text->width; $x += 10) -{ +for ($x = 0; $x < $image->width + $text->width; $x += 10) { // append the frame to the image vertically ... we make a very tall, thin // strip of frames to save $frame = $image->composite2($text, "over", [ "x" => $x - $text->width, "y" => $image->height / 2 - $text->height / 2 ]); - if ($animation == NULL) + if ($animation == null) { $animation = $frame; - else + } + else { $animation = $animation->join($frame, "vertical"); + } // frame delay in ms array_push($delay, 30); From bbeef608c6a6ac0f12172a463eee6b0b7d9d49e2 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Thu, 28 Dec 2023 20:20:19 +0000 Subject: [PATCH 086/123] another formatting fix --- examples/animate-image.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/animate-image.php b/examples/animate-image.php index 1525259..9d06d49 100755 --- a/examples/animate-image.php +++ b/examples/animate-image.php @@ -25,8 +25,7 @@ ]); if ($animation == null) { $animation = $frame; - } - else { + } else { $animation = $animation->join($frame, "vertical"); } From 85affa54a16aa8a0b292e7e99945a14826b2650f Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Mon, 22 Jan 2024 20:17:03 +0000 Subject: [PATCH 087/123] add flags support see https://github.com/libvips/php-vips/issues/229 --- CHANGELOG.md | 2 + examples/fields.php | 21 +++++---- examples/generate_phpdoc.py | 61 ++++++++++++++++++++++--- examples/keep.php | 41 +++++++++++++++++ src/{ImageType.php => ForeignKeep.php} | 19 ++++---- src/{Token.php => ForeignPngFilter.php} | 14 +++--- src/ImageAutodoc.php | 28 ++++++++++-- 7 files changed, 151 insertions(+), 35 deletions(-) create mode 100755 examples/keep.php rename src/{ImageType.php => ForeignKeep.php} (85%) rename src/{Token.php => ForeignPngFilter.php} (90%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 032a444..d4e6616 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ All notable changes to `:vips` will be documented in this file. ## master - added `animate-image.php` example [jcupitt] +- added flags support [jcupitt] +- added `keep.php` example [jcupitt] ## 2.3.0 - 2023-09-26 diff --git a/examples/fields.php b/examples/fields.php index b3f6f62..5678be7 100755 --- a/examples/fields.php +++ b/examples/fields.php @@ -5,16 +5,21 @@ use Jcupitt\Vips; -$im = Vips\Image::newFromFile($argv[1]); - -$names = $im->getFields(); - -echo "$argv[1]\n"; -foreach ($names as &$name) { - $value = $im->get($name); - echo "$name: $value\n"; +function printMetadata($im) +{ + foreach ($im->getFields() as &$name) { + $value = $im->get($name); + if (str_ends_with($name, "-data")) { + $len = strlen($value); + $value = "<$len bytes of binary data>"; + } + echo " $name: $value\n"; + } } +$im = Vips\Image::newFromFile($argv[1]); +printMetadata($im); + /* * Local variables: * tab-width: 4 diff --git a/examples/generate_phpdoc.py b/examples/generate_phpdoc.py index 632db36..5a7d310 100755 --- a/examples/generate_phpdoc.py +++ b/examples/generate_phpdoc.py @@ -1,12 +1,12 @@ #!/usr/bin/python3 +# needs pyvips 2.2.3 or later + from pyvips import Image, Introspect, GValue, Error, \ - ffi, values_for_enum, vips_lib, gobject_lib, \ + ffi, enum_dict, flags_dict, vips_lib, gobject_lib, \ type_map, type_name, type_from_name, nickname_find # This file generates the phpdoc comments for the magic methods and properties. -# It's in Python, since we use the whole of FFI, not just the -# small bit exposed by php-vips-ext. # Regenerate docs with something like: # @@ -292,6 +292,10 @@ def add_enum(gtype, a, b): type_map(type_from_name('GEnum'), add_enum) + # Filter internal enums + blacklist = ['VipsImageType', 'VipsToken'] + all_enums = [name for name in all_enums if name not in blacklist] + for name in all_enums: gtype = type_from_name(name) php_name = remove_prefix(name) @@ -310,14 +314,59 @@ def add_enum(gtype, a, b): f.write('abstract class {0}\n'.format(php_name)) f.write('{\n') - for value in values_for_enum(gtype): - php_name = value.replace('-', '_').upper() + for key, value in enum_dict(gtype).items(): + php_name = key.replace('-', '_').upper() + if php_name in reserved_php_names: + php_name = reserved_php_names[php_name] + f.write(' const {0} = \'{1}\';\n'.format(php_name, key)) + + f.write('}\n') + + +def generate_flags(): + all_flags = [] + + def add_flags(gtype, a, b): + nickname = type_name(gtype) + all_flags.append(nickname) + + type_map(gtype, add_flags) + + return ffi.NULL + + type_map(type_from_name('GFlags'), add_flags) + + # Filter internal flags + blacklist = ['VipsForeignFlags'] + all_flags = [name for name in all_flags if name not in blacklist] + + for name in all_flags: + gtype = type_from_name(name) + php_name = remove_prefix(name) + + print('Generating {0}.php ...'.format(php_name)) + + with open('{0}.php'.format(php_name), 'w') as f: + f.write(preamble) + f.write('\n') + f.write('namespace Jcupitt\\Vips;\n') + f.write('\n') + f.write('/**\n') + f.write(' * The {0} flags.\n'.format(php_name)) + f.write(class_header) + f.write(' */\n') + f.write('abstract class {0}\n'.format(php_name)) + f.write('{\n') + + for key, value in flags_dict(gtype).items(): + php_name = key.replace('-', '_').upper() if php_name in reserved_php_names: php_name = reserved_php_names[php_name] - f.write(' const {0} = \'{1}\';\n'.format(php_name, value)) + f.write(' const {0} = {1};\n'.format(php_name, value)) f.write('}\n') generate_auto_doc('ImageAutodoc.php') generate_enums() +generate_flags() diff --git a/examples/keep.php b/examples/keep.php new file mode 100755 index 0000000..84e5322 --- /dev/null +++ b/examples/keep.php @@ -0,0 +1,41 @@ +#!/usr/bin/env php +getFields() as &$name) { + $value = $im->get($name); + if (str_ends_with($name, "-data")) { + $len = strlen($value); + $value = "<$len bytes of binary data>"; + } + echo " $name: $value\n"; + } +} + +$im = Vips\Image::newFromFile($argv[1]); +echo "$argv[1]\n"; +printMetadata($im); + +echo "\nafter keep => icc\n"; +$buf = $im->tiffsave_buffer(['keep' => Vips\ForeignKeep::ICC]); +$im2 = Vips\Image::newFromBuffer($buf, ""); +printMetadata($im2); + +echo "\nafter keep => exif|xmp\n"; +$buf = $im->tiffsave_buffer(['keep' => Vips\ForeignKeep::ICC | Vips\ForeignKeep::XMP]); +$im2 = Vips\Image::newFromBuffer($buf, ""); +printMetadata($im2); + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: expandtab sw=4 ts=4 fdm=marker + * vim<600: expandtab sw=4 ts=4 + */ diff --git a/src/ImageType.php b/src/ForeignKeep.php similarity index 85% rename from src/ImageType.php rename to src/ForeignKeep.php index 3678d22..b7bec4b 100644 --- a/src/ImageType.php +++ b/src/ForeignKeep.php @@ -39,7 +39,7 @@ namespace Jcupitt\Vips; /** - * The ImageType enum. + * The ForeignKeep flags. * @category Images * @package Jcupitt\Vips * @author John Cupitt @@ -47,14 +47,13 @@ * @license https://opensource.org/licenses/MIT MIT * @link https://github.com/jcupitt/php-vips */ -abstract class ImageType +abstract class ForeignKeep { - const ERROR = 'error'; - const NONE = 'none'; - const SETBUF = 'setbuf'; - const SETBUF_FOREIGN = 'setbuf-foreign'; - const OPENIN = 'openin'; - const MMAPIN = 'mmapin'; - const MMAPINRW = 'mmapinrw'; - const OPENOUT = 'openout'; + const NONE = 0; + const EXIF = 1; + const XMP = 2; + const IPTC = 4; + const ICC = 8; + const OTHER = 16; + const ALL = 31; } diff --git a/src/Token.php b/src/ForeignPngFilter.php similarity index 90% rename from src/Token.php rename to src/ForeignPngFilter.php index 40df593..485c05d 100644 --- a/src/Token.php +++ b/src/ForeignPngFilter.php @@ -39,7 +39,7 @@ namespace Jcupitt\Vips; /** - * The Token enum. + * The ForeignPngFilter flags. * @category Images * @package Jcupitt\Vips * @author John Cupitt @@ -47,10 +47,12 @@ * @license https://opensource.org/licenses/MIT MIT * @link https://github.com/jcupitt/php-vips */ -abstract class Token +abstract class ForeignPngFilter { - const LEFT = 'left'; - const RIGHT = 'right'; - const STRING = 'string'; - const EQUALS = 'equals'; + const NONE = 8; + const SUB = 16; + const UP = 32; + const AVG = 64; + const PAETH = 128; + const ALL = 248; } diff --git a/src/ImageAutodoc.php b/src/ImageAutodoc.php index e8b80e7..c773791 100644 --- a/src/ImageAutodoc.php +++ b/src/ImageAutodoc.php @@ -160,7 +160,7 @@ * @throws Exception * @method Image convi(Image $mask, array $options = []) Int convolution operation. * @throws Exception - * @method Image convsep(Image $mask, array $options = []) Seperable convolution operation. + * @method Image convsep(Image $mask, array $options = []) Separable convolution operation. * @throws Exception * @method Image copy(array $options = []) Copy an image. * @throws Exception @@ -372,6 +372,18 @@ * @throws Exception * @method void jxlsave_target(Target $target, array $options = []) Save image in JPEG-XL format. * @throws Exception + * @method static Image kakaduload(string $filename, array $options = []) Load JPEG2000 image. + * @throws Exception + * @method static Image kakaduload_buffer(string $buffer, array $options = []) Load JPEG2000 image. + * @throws Exception + * @method static Image kakaduload_source(Source $source, array $options = []) Load JPEG2000 image. + * @throws Exception + * @method void kakadusave(string $filename, array $options = []) Save image in JPEG2000 format. + * @throws Exception + * @method string kakadusave_buffer(array $options = []) Save image in JPEG2000 format. + * @throws Exception + * @method void kakadusave_target(Target $target, array $options = []) Save image in JPEG2000 format. + * @throws Exception * @method Image labelregions(array $options = []) Label regions in an image. * @throws Exception * @method Image linear(float[]|float $a, float[]|float $b, array $options = []) Calculate (a * in + b). @@ -380,9 +392,9 @@ * @throws Exception * @method static Image logmat(float $sigma, float $min_ampl, array $options = []) Make a Laplacian of Gaussian image. * @throws Exception - * @method static Image magickload(string $filename, array $options = []) Load file with ImageMagick7. + * @method static Image magickload(string $filename, array $options = []) Load file with ImageMagick. * @throws Exception - * @method static Image magickload_buffer(string $buffer, array $options = []) Load buffer with ImageMagick7. + * @method static Image magickload_buffer(string $buffer, array $options = []) Load buffer with ImageMagick. * @throws Exception * @method void magicksave(string $filename, array $options = []) Save file with ImageMagick. * @throws Exception @@ -457,6 +469,12 @@ * @throws Exception * @method Image msb(array $options = []) Pick most-significant byte from an image. * @throws Exception + * @method static Image niftiload(string $filename, array $options = []) Load NIfTI volume. + * @throws Exception + * @method static Image niftiload_source(Source $source, array $options = []) Load NIfTI volumes. + * @throws Exception + * @method void niftisave(string $filename, array $options = []) Save image to nifti file. + * @throws Exception * @method static Image openexrload(string $filename, array $options = []) Load an OpenEXR image. * @throws Exception * @method static Image openslideload(string $filename, array $options = []) Load file with OpenSlide. @@ -481,9 +499,9 @@ * @throws Exception * @method static Image pngload_source(Source $source, array $options = []) Load png from source. * @throws Exception - * @method void pngsave(string $filename, array $options = []) Save image to file as PNG. + * @method void pngsave(string $filename, array $options = []) Save image to png file. * @throws Exception - * @method string pngsave_buffer(array $options = []) Save image to buffer as PNG. + * @method string pngsave_buffer(array $options = []) Save image to png buffer. * @throws Exception * @method void pngsave_target(Target $target, array $options = []) Save image to target as PNG. * @throws Exception From 7cf9d499fe43207ad2ac4e62238b939a7d401ab5 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 14 Feb 2024 12:45:17 +0000 Subject: [PATCH 088/123] use Target save to implement writeToBuffer (#207) if possible --- examples/streaming-bench.php | 0 examples/streaming.php | 0 src/GObject.php | 4 ++-- src/Image.php | 43 +++++++++++++++++++++++++++++------- 4 files changed, 37 insertions(+), 10 deletions(-) mode change 100644 => 100755 examples/streaming-bench.php mode change 100644 => 100755 examples/streaming.php diff --git a/examples/streaming-bench.php b/examples/streaming-bench.php old mode 100644 new mode 100755 diff --git a/examples/streaming.php b/examples/streaming.php old mode 100644 new mode 100755 diff --git a/src/GObject.php b/src/GObject.php index 964393a..40f34d9 100644 --- a/src/GObject.php +++ b/src/GObject.php @@ -223,7 +223,7 @@ private static function getMarshaler(string $name, callable $callback): ?Closure CData $hint, ?CData $data ) use (&$callback): void { - assert($numberOfParams === 0); + assert($numberOfParams === 1); /** * Signature: void(VipsTargetCustom* target, void* handle) */ @@ -242,7 +242,7 @@ private static function getMarshaler(string $name, callable $callback): ?Closure CData $hint, ?CData $data ) use (&$callback): void { - assert($numberOfParams === 0); + assert($numberOfParams === 1); /** * Signature: int(VipsTargetCustom* target, void* handle) */ diff --git a/src/Image.php b/src/Image.php index 10817e6..01b1494 100644 --- a/src/Image.php +++ b/src/Image.php @@ -993,18 +993,45 @@ public function writeToBuffer(string $suffix, array $options = []): string $filename = Utils::filenameGetFilename($suffix); $string_options = Utils::filenameGetOptions($suffix); - $saver = FFI::vips()->vips_foreign_find_save_buffer($filename); - if ($saver == "") { - throw new Exception(); + $saver = null; + + // see if we can save with the Target API ... we need 8.9 or later for + // Target, and we need this libvips to have a target saver for this + // format + if (FFI::atLeast(8, 9)) { + FFI::vips()->vips_error_freeze(); + $saver = FFI::vips()->vips_foreign_find_save_target($filename); + FFI::vips()->vips_error_thaw(); } - if (strlen($string_options) != 0) { - $options = array_merge([ - "string_options" => $string_options, - ], $options); + if ($saver !== null) { + $target = Target::newToMemory(); + if (strlen($string_options) != 0) { + $options = array_merge([ + "string_options" => $string_options, + ], $options); + } + + VipsOperation::call($saver, $this, [$target], $options); + + $buffer = $target->get("blob"); + } else { + // fall back to the old _buffer API + $saver = FFI::vips()->vips_foreign_find_save_buffer($filename); + if ($saver == "") { + throw new Exception(); + } + + if (strlen($string_options) != 0) { + $options = array_merge([ + "string_options" => $string_options, + ], $options); + } + + $buffer = VipsOperation::call($saver, $this, [], $options); } - return VipsOperation::call($saver, $this, [], $options); + return $buffer; } /** From 57b593a37f53f35b7cd4eadf969ccaa38953e2c3 Mon Sep 17 00:00:00 2001 From: Christian Sciberras Date: Wed, 14 Feb 2024 13:49:12 +0100 Subject: [PATCH 089/123] FFI class improvements (fixes #221) (#230) * FFI class improvements * Update missed heredoc; use generic lib location --- src/FFI.php | 59 +++++++++++++++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/src/FFI.php b/src/FFI.php index 194db6f..d8ac18a 100644 --- a/src/FFI.php +++ b/src/FFI.php @@ -180,10 +180,10 @@ public static function shutDown(): void public static function newGClosure(): \FFI\CData { - // GClosure measures 32-bit with the first few fields until marshal - // Marshal is a function pointer, thus platform-dependant. - // Data is a pointer, thus platform-dependant. - // Notifiers is an array-pointer, thus platform-dependant. + // GClosure measures 32-bit with the first few fields until marshal. + // - Marshal is a function pointer, thus platform-dependant. + // - Data is a pointer, thus platform-dependant. + // - Notifiers is an array-pointer, thus platform-dependant. // All in all it's basically 4 (bytes) + 3 * POINTER_SIZE // However, gobject wants 8 (bytes) + 3 * POINTER_SIZE. // I'm not sure where that extra byte comes from. Padding on 64-bit machines? @@ -210,7 +210,7 @@ private static function libraryLoad( array $libraryPaths, string $libraryName, string $interface - ): \FFI { + ): ?\FFI { Utils::debugLog("trying to open", ["libraryName" => $libraryName]); foreach ($libraryPaths as $path) { Utils::debugLog("trying path", ["path" => $path]); @@ -225,6 +225,7 @@ private static function libraryLoad( ]); } } + return null; } private static function init(): void @@ -234,7 +235,7 @@ private static function init(): void return; } - // the two usual install problems + // the two usual installation problems if (!extension_loaded('ffi')) { throw new Exception('FFI extension not loaded'); } @@ -267,16 +268,18 @@ private static function init(): void $libraryPaths[] = $vipshome . "/lib/"; } - if (PHP_OS_FAMILY === "OSX" || - PHP_OS_FAMILY === "Darwin") { - $libraryPaths[] = "/opt/homebrew/lib/"; // Homebrew on Apple Silicon + if (PHP_OS_FAMILY === "OSX" || PHP_OS_FAMILY === "Darwin") { + // Homebrew on Apple Silicon + $libraryPaths[] = "/opt/homebrew/lib/"; + // See https://github.com/Homebrew/brew/issues/13481#issuecomment-1207203483 + $libraryPaths[] = "/usr/local/lib/"; } - $vips = self::libraryLoad($libraryPaths, $vips_libname, << Date: Wed, 14 Feb 2024 13:55:02 +0100 Subject: [PATCH 090/123] Fix broken assertion; minor other fixes (#233) --- src/FFI.php | 6 +++--- src/GObject.php | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/FFI.php b/src/FFI.php index d8ac18a..1ac0585 100644 --- a/src/FFI.php +++ b/src/FFI.php @@ -163,9 +163,9 @@ public static function version(): string */ public static function atLeast(int $x, int $y, int $z = 0): bool { - return self::$library_major > $x || - self::$library_major == $x && self::$library_minor > $y || - self::$library_major == $x && self::$library_minor == $y && self::$library_micro >= $z; + return self::$library_major > $x + || (self::$library_major === $x && self::$library_minor > $y) + || (self::$library_major === $x && self::$library_minor === $y && self::$library_micro >= $z); } /** diff --git a/src/GObject.php b/src/GObject.php index 40f34d9..bf3b512 100644 --- a/src/GObject.php +++ b/src/GObject.php @@ -152,7 +152,7 @@ private static function getMarshaler(string $name, callable $callback): ?Closure ?CData $data ) use (&$callback): void { assert($numberOfParams === 3); - /* + /** * Signature: gint64(VipsSourceCustom* source, void* buffer, gint64 length, void* handle) */ $bufferLength = (int)FFI::gobject()->g_value_get_int64(\FFI::addr($params[2])); @@ -180,7 +180,7 @@ private static function getMarshaler(string $name, callable $callback): ?Closure ?CData $data ) use (&$callback): void { assert($numberOfParams === 3); - /* + /** * Signature: gint64(VipsSourceCustom* source, gint64 offset, int whence, void* handle) */ $offset = (int)FFI::gobject()->g_value_get_int64(\FFI::addr($params[1])); @@ -201,7 +201,7 @@ private static function getMarshaler(string $name, callable $callback): ?Closure ?CData $data ) use (&$callback): void { assert($numberOfParams === 3); - /* + /** * Signature: gint64(VipsTargetCustom* target, void* buffer, gint64 length, void* handle) */ $bufferPointer = FFI::gobject()->g_value_get_pointer(\FFI::addr($params[1])); From c365be7e94538f4bc7692ab20f3dbec3a598fb7a Mon Sep 17 00:00:00 2001 From: Christian Sciberras Date: Wed, 14 Feb 2024 13:57:30 +0100 Subject: [PATCH 091/123] Avoid PHP 8.3 FFI deprecations (fixes #226) (#231) * Avoid PHP 8.3 FFI::cast deprecation * Avoid PHP 8.3 FFI::new deprecation * Run CI tests on PHP 8.3 --- .github/workflows/ci.yml | 1 + src/Connection.php | 2 +- src/GObject.php | 4 ++-- src/GValue.php | 6 +++--- src/Image.php | 14 ++++++++------ src/Interpolate.php | 2 +- src/Introspect.php | 2 +- src/Source.php | 4 ++-- src/Target.php | 2 +- src/VipsObject.php | 4 ++-- 10 files changed, 22 insertions(+), 19 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5b7dd18..3025f83 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,6 +14,7 @@ jobs: - php: '8.0' - php: '8.1' - php: '8.2' + - php: '8.3' steps: - name: Setup PHP diff --git a/src/Connection.php b/src/Connection.php index ea38166..096a422 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -14,7 +14,7 @@ abstract class Connection extends VipsObject public function __construct(\FFI\CData $pointer) { - $this->pointer = \FFI::cast(FFI::ctypes('VipsConnection'), $pointer); + $this->pointer = FFI::vips()->cast(FFI::ctypes('VipsConnection'), $pointer); parent::__construct($pointer); } diff --git a/src/GObject.php b/src/GObject.php index bf3b512..2959e25 100644 --- a/src/GObject.php +++ b/src/GObject.php @@ -74,7 +74,7 @@ abstract class GObject */ public function __construct(CData $pointer) { - $this->pointer = \FFI::cast(FFI::ctypes("GObject"), $pointer); + $this->pointer = FFI::vips()->cast(FFI::ctypes("GObject"), $pointer); } public function __destruct() @@ -135,7 +135,7 @@ private static function getMarshaler(string $name, callable $callback): ?Closure $vi = FFI::gobject()->g_value_get_object(\FFI::addr($params[0])); FFI::gobject()->g_object_ref($vi); $image = new Image($vi); - $pr = \FFI::cast( + $pr = FFI::vips()->cast( FFI::ctypes('VipsProgress'), FFI::gobject()->g_value_get_pointer(\FFI::addr($params[1])) ); diff --git a/src/GValue.php b/src/GValue.php index c310e6a..2bd657e 100644 --- a/src/GValue.php +++ b/src/GValue.php @@ -147,7 +147,7 @@ public function set($value): void $value = [$value]; } $n = count($value); - $array = \FFI::new("int[$n]"); + $array = FFI::vips()->new("int[$n]"); for ($i = 0; $i < $n; $i++) { $array[$i] = $value[$i]; } @@ -160,7 +160,7 @@ public function set($value): void $value = [$value]; } $n = count($value); - $array = \FFI::new("double[$n]"); + $array = FFI::vips()->new("double[$n]"); for ($i = 0; $i < $n; $i++) { $array[$i] = $value[$i]; } @@ -187,7 +187,7 @@ public function set($value): void # we need to set the blob to a copy of the data that vips_lib # can own and free $n = strlen($value); - $memory = \FFI::new("char[$n]", false, true); + $memory = FFI::vips()->new("char[$n]", false, true); \FFI::memcpy($memory, $value, $n); FFI::vips()-> vips_value_set_blob_free($this->pointer, $memory, $n); diff --git a/src/Image.php b/src/Image.php index 01b1494..afc9b45 100644 --- a/src/Image.php +++ b/src/Image.php @@ -500,7 +500,7 @@ class Image extends ImageAutodoc implements \ArrayAccess */ public function __construct(\FFI\CData $pointer) { - $this->pointer = \FFI::cast(FFI::ctypes("VipsImage"), $pointer); + $this->pointer = FFI::vips()->cast(FFI::ctypes("VipsImage"), $pointer); parent::__construct($pointer); } @@ -807,7 +807,7 @@ public static function newFromArray( $width = count($array[0]); $n = $width * $height; - $a = \FFI::new("double[$n]", true, true); + $a = FFI::vips()->new("double[$n]", true, true); for ($y = 0; $y < $height; $y++) { for ($x = 0; $x < $width; $x++) { $a[$x + $y * $width] = $array[$y][$x]; @@ -921,7 +921,9 @@ public function newFromImage($value): Image */ public static function findLoadSource(Source $source): ?string { - return FFI::vips()->vips_foreign_find_load_source(\FFI::cast(FFI::ctypes('VipsSource'), $source->pointer)); + return FFI::vips()->vips_foreign_find_load_source( + FFI::vips()->cast(FFI::ctypes('VipsSource'), $source->pointer) + ); } /** @@ -1043,7 +1045,7 @@ public function writeToBuffer(string $suffix, array $options = []): string */ public function writeToMemory(): string { - $p_size = \FFI::new("size_t[1]"); + $p_size = FFI::vips()->new("size_t[1]"); $pointer = FFI::vips()-> vips_image_write_to_memory($this->pointer, $p_size); @@ -1084,7 +1086,7 @@ public function writeToMemory(): string */ public function writeToArray(): array { - $p_size = \FFI::new("size_t[1]"); + $p_size = FFI::vips()->new("size_t[1]"); $pointer = FFI::vips()-> vips_image_write_to_memory($this->pointer, $p_size); @@ -1095,7 +1097,7 @@ public function writeToArray(): array // wrap pointer up as a C array of the right type $type_name = FFI::ftypes($this->format); $n = $this->width * $this->height * $this->bands; - $array = \FFI::cast("{$type_name}[$n]", $pointer); + $array = FFI::vips()->cast("{$type_name}[$n]", $pointer); // copy to PHP memory as a flat array $result = []; diff --git a/src/Interpolate.php b/src/Interpolate.php index cb5eee5..e20ba12 100644 --- a/src/Interpolate.php +++ b/src/Interpolate.php @@ -62,7 +62,7 @@ class Interpolate extends VipsObject public function __construct(\FFI\CData $pointer) { - $this->pointer = \FFI::cast(FFI::ctypes("VipsInterpolate"), $pointer); + $this->pointer = FFI::vips()->cast(FFI::ctypes("VipsInterpolate"), $pointer); parent::__construct($pointer); } diff --git a/src/Introspect.php b/src/Introspect.php index ce3208c..489364d 100644 --- a/src/Introspect.php +++ b/src/Introspect.php @@ -105,7 +105,7 @@ public function __construct($operation_name) $p_flags = FFI::vips()->new("int*[1]"); $p_n_args = FFI::vips()->new("int[1]"); $result = FFI::vips()->vips_object_get_args( - \FFI::cast(FFI::ctypes("VipsObject"), $operation->pointer), + FFI::vips()->cast(FFI::ctypes("VipsObject"), $operation->pointer), $p_names, $p_flags, $p_n_args diff --git a/src/Source.php b/src/Source.php index 60975fe..9a028a7 100644 --- a/src/Source.php +++ b/src/Source.php @@ -14,7 +14,7 @@ class Source extends Connection public function __construct(\FFI\CData $pointer) { - $this->pointer = \FFI::cast(FFI::ctypes('VipsSource'), $pointer); + $this->pointer = FFI::vips()->cast(FFI::ctypes('VipsSource'), $pointer); parent::__construct($pointer); } @@ -67,7 +67,7 @@ public static function newFromMemory(string $data): self # we need to set the memory to a copy of the data that vips_lib # can own and free $n = strlen($data); - $memory = \FFI::new("char[$n]", false, true); + $memory = FFI::vips()->new("char[$n]", false, true); \FFI::memcpy($memory, $data, $n); $pointer = FFI::vips()->vips_source_new_from_memory($memory, $n); diff --git a/src/Target.php b/src/Target.php index 9b31e61..eda298d 100644 --- a/src/Target.php +++ b/src/Target.php @@ -14,7 +14,7 @@ class Target extends Connection public function __construct(\FFI\CData $pointer) { - $this->pointer = \FFI::cast(FFI::ctypes('VipsTarget'), $pointer); + $this->pointer = FFI::vips()->cast(FFI::ctypes('VipsTarget'), $pointer); parent::__construct($pointer); } diff --git a/src/VipsObject.php b/src/VipsObject.php index 74bb83b..e0b18a5 100644 --- a/src/VipsObject.php +++ b/src/VipsObject.php @@ -68,8 +68,8 @@ abstract class VipsObject extends GObject public function __construct(\FFI\CData $pointer) { - $this->pointer = \FFI::cast(FFI::ctypes("VipsObject"), $pointer); - $this->gObject = \FFI::cast(FFI::ctypes("GObject"), $pointer); + $this->pointer = FFI::vips()->cast(FFI::ctypes("VipsObject"), $pointer); + $this->gObject = FFI::vips()->cast(FFI::ctypes("GObject"), $pointer); parent::__construct($pointer); } From a2c7609089d3f0a83def80ea59d2934c6f83ceb4 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 14 Feb 2024 13:05:45 +0000 Subject: [PATCH 092/123] udpate CHANGELOG and a minor formatting fix --- CHANGELOG.md | 3 +++ src/FFI.php | 7 ++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d4e6616..efa435f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ All notable changes to `:vips` will be documented in this file. - added `animate-image.php` example [jcupitt] - added flags support [jcupitt] - added `keep.php` example [jcupitt] +- added streaming examples [jcupitt] +- fix php 8.3 compatibility [uuf6429] +- better library finding [uuf6429] ## 2.3.0 - 2023-09-26 diff --git a/src/FFI.php b/src/FFI.php index 1ac0585..3fc8a97 100644 --- a/src/FFI.php +++ b/src/FFI.php @@ -163,9 +163,10 @@ public static function version(): string */ public static function atLeast(int $x, int $y, int $z = 0): bool { - return self::$library_major > $x - || (self::$library_major === $x && self::$library_minor > $y) - || (self::$library_major === $x && self::$library_minor === $y && self::$library_micro >= $z); + return self::$library_major > $x || + (self::$library_major === $x && self::$library_minor > $y) || + (self::$library_major === $x && self::$library_minor === $y && + self::$library_micro >= $z); } /** From 90075e3ee4ede10805e16836f2f077e630172365 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 14 Feb 2024 14:39:21 +0000 Subject: [PATCH 093/123] remove php 8.3 from CI it's not working for some reason I don't understand --- .github/workflows/ci.yml | 5 ++--- src/VipsOperation.php | 3 +-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3025f83..9acd05d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,6 @@ jobs: - php: '8.0' - php: '8.1' - php: '8.2' - - php: '8.3' steps: - name: Setup PHP @@ -25,14 +24,14 @@ jobs: coverage: none - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install vips run: sudo apt install -y libvips --no-install-recommends - name: Install composer dependencies run: | - composer update --prefer-dist --no-interaction --no-progress --no-ansi + composer update --prefer-dist --no-interaction --no-progress --no-ansi - name: PHPUnit run: composer test diff --git a/src/VipsOperation.php b/src/VipsOperation.php index cf212af..46e4a45 100644 --- a/src/VipsOperation.php +++ b/src/VipsOperation.php @@ -304,8 +304,7 @@ public static function callBase( /* Build the operation */ - $pointer = FFI::vips()-> - vips_cache_operation_build($operation->pointer); + $pointer = FFI::vips()->vips_cache_operation_build($operation->pointer); if ($pointer == null) { $operation->unrefOutputs(); throw new Exception(); From 31515355f4de8f04f99ba7a2f1eac6a1ce1233e0 Mon Sep 17 00:00:00 2001 From: Kleis Auke Wolthuizen Date: Wed, 14 Feb 2024 15:40:48 +0100 Subject: [PATCH 094/123] Fix CI (#237) --- .github/workflows/ci.yml | 1 + tests/StreamingTest.php | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9acd05d..4af1d7c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,6 +20,7 @@ jobs: uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} + ini-values: zend.max_allowed_stack_size=-1 tools: composer:v2 coverage: none diff --git a/tests/StreamingTest.php b/tests/StreamingTest.php index 9d07184..689ea01 100644 --- a/tests/StreamingTest.php +++ b/tests/StreamingTest.php @@ -27,7 +27,7 @@ public function sourceAndTargetProvider(): Generator 'File' => fn() => Target::newToFile(tempnam(sys_get_temp_dir(), 'image')), 'Memory' => fn() => Target::newToMemory(), 'Resource' => fn() => new TargetResource(fopen('php://memory', 'wb+')), - 'Resource(Not Readable)' => fn() => new TargetResource(fopen('php://memory', 'wb')) + 'Resource (not readable)' => fn() => new TargetResource(fopen('php://memory', 'wb')) ]; foreach ($sources as $sourceName => $source) { From 50cb534445192d8c91fd28fc95cbc1b57e9eb679 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 14 Feb 2024 14:41:24 +0000 Subject: [PATCH 095/123] Revert "remove php 8.3 from CI" This reverts commit 90075e3ee4ede10805e16836f2f077e630172365. --- .github/workflows/ci.yml | 5 +++-- src/VipsOperation.php | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9acd05d..3025f83 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,6 +14,7 @@ jobs: - php: '8.0' - php: '8.1' - php: '8.2' + - php: '8.3' steps: - name: Setup PHP @@ -24,14 +25,14 @@ jobs: coverage: none - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v3 - name: Install vips run: sudo apt install -y libvips --no-install-recommends - name: Install composer dependencies run: | - composer update --prefer-dist --no-interaction --no-progress --no-ansi + composer update --prefer-dist --no-interaction --no-progress --no-ansi - name: PHPUnit run: composer test diff --git a/src/VipsOperation.php b/src/VipsOperation.php index 46e4a45..cf212af 100644 --- a/src/VipsOperation.php +++ b/src/VipsOperation.php @@ -304,7 +304,8 @@ public static function callBase( /* Build the operation */ - $pointer = FFI::vips()->vips_cache_operation_build($operation->pointer); + $pointer = FFI::vips()-> + vips_cache_operation_build($operation->pointer); if ($pointer == null) { $operation->unrefOutputs(); throw new Exception(); From 81d2281d38d45ee152e22c94fea23fe56225aa70 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 14 Feb 2024 16:50:29 +0000 Subject: [PATCH 096/123] check max_allowed_stack_size during startup see https://github.com/libvips/php-vips/pull/237 --- README.md | 12 ++++++++++++ src/FFI.php | 6 +++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d6b4bad..716fe50 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,18 @@ server can use it to call any native library they have access to. Of course if attackers are running their own PHP code on your webserver you are probably already toast, unfortunately. +Finally, on php 8.3 and later you need to disable stack overflow +tests. php-vips executes FFI callbacks off the main thread and this confuses +those checks, at least in php 8.3.0. + +Add: + +``` +zend.max_allowed_stack_size=-1 +``` + +To your `php.ini`. + ### Example ```php diff --git a/src/FFI.php b/src/FFI.php index 3fc8a97..56a30ae 100644 --- a/src/FFI.php +++ b/src/FFI.php @@ -236,13 +236,17 @@ private static function init(): void return; } - // the two usual installation problems + // detect the most common installation problems if (!extension_loaded('ffi')) { throw new Exception('FFI extension not loaded'); } if (!ini_get('ffi.enable')) { throw new Exception("ffi.enable not set to 'true'"); } + if (version_compare(PHP_VERSION, '8.3') && + ini_get('zend.max_allowed_stack_size') != '-1') { + throw new Exception("zend.max_allowed_stack_size not set to '-1'"); + } $vips_libname = self::libraryName("libvips", 42); if (PHP_OS_FAMILY === "Windows") { From 439b280d22213a2b5c7c76251d7af57734d0c3dc Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 14 Feb 2024 16:53:14 +0000 Subject: [PATCH 097/123] oop, for the condition --- src/FFI.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FFI.php b/src/FFI.php index 56a30ae..8f2ac0c 100644 --- a/src/FFI.php +++ b/src/FFI.php @@ -243,7 +243,7 @@ private static function init(): void if (!ini_get('ffi.enable')) { throw new Exception("ffi.enable not set to 'true'"); } - if (version_compare(PHP_VERSION, '8.3') && + if (version_compare(PHP_VERSION, '8.3', '>=') && ini_get('zend.max_allowed_stack_size') != '-1') { throw new Exception("zend.max_allowed_stack_size not set to '-1'"); } From dbfcee0c951e184cd3a686342b3fb85641cb9753 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 24 Feb 2024 19:21:20 +0000 Subject: [PATCH 098/123] try to improve support for 8.7 --- CHANGELOG.md | 1 + src/FFI.php | 15 ++++++++++----- tests/StreamingTest.php | 10 ++++++++++ 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index efa435f..71935e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ All notable changes to `:vips` will be documented in this file. - added streaming examples [jcupitt] - fix php 8.3 compatibility [uuf6429] - better library finding [uuf6429] +- fix compat with libvips before 8.9 [allanvb] ## 2.3.0 - 2023-09-26 diff --git a/src/FFI.php b/src/FFI.php index 8f2ac0c..70a399b 100644 --- a/src/FFI.php +++ b/src/FFI.php @@ -812,14 +812,19 @@ private static function init(): void "VipsOperation" => self::$vips->type("VipsOperation*"), "VipsImage" => self::$vips->type("VipsImage*"), "VipsInterpolate" => self::$vips->type("VipsInterpolate*"), - "VipsConnection" => self::$vips->type("VipsConnection*"), - "VipsSource" => self::$vips->type("VipsSource*"), - "VipsSourceCustom" => self::$vips->type("VipsSourceCustom*"), - "VipsTarget" => self::$vips->type("VipsTarget*"), - "VipsTargetCustom" => self::$vips->type("VipsTargetCustom*"), "VipsProgress" => self::$vips->type("VipsProgress*"), ]; + if (self::atLeast(8, 9)) { + self::$ctypes = array_merge(self::$ctypes, [ + "VipsConnection" => self::$vips->type("VipsConnection*"), + "VipsSource" => self::$vips->type("VipsSource*"), + "VipsSourceCustom" => self::$vips->type("VipsSourceCustom*"), + "VipsTarget" => self::$vips->type("VipsTarget*"), + "VipsTargetCustom" => self::$vips->type("VipsTargetCustom*"), + ]); + } + self::$gtypes = [ "gboolean" => self::$gobject->g_type_from_name("gboolean"), "gint" => self::$gobject->g_type_from_name("gint"), diff --git a/tests/StreamingTest.php b/tests/StreamingTest.php index 689ea01..7efcc90 100644 --- a/tests/StreamingTest.php +++ b/tests/StreamingTest.php @@ -3,6 +3,7 @@ namespace Jcupitt\Vips\Test; use Generator; +use Jcupitt\Vips\FFI; use Jcupitt\Vips\Exception; use Jcupitt\Vips\Image; use Jcupitt\Vips\Source; @@ -13,6 +14,15 @@ class StreamingTest extends TestCase { + protected function setUp(): void + { + parent::setUp(); + + if (!FFI::atLeast(8, 9)) { + $this->markTestSkipped('libvips too old for streaming tests'); + } + } + /** * @throws Exception */ From 69c343e0bc7bac6dc82f4f16f985d8d6a15eb05a Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 9 Apr 2024 10:27:18 +0100 Subject: [PATCH 099/123] update ready for 2.4 --- CHANGELOG.md | 4 +++- README.md | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 71935e3..1854df8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,11 @@ # Changelog -All notable changes to `:vips` will be documented in this file. +All notable changes to `php-vips` will be documented in this file. ## master +## 2.4.0 - 2024-04-09 + - added `animate-image.php` example [jcupitt] - added flags support [jcupitt] - added `keep.php` example [jcupitt] diff --git a/README.md b/README.md index 716fe50..39ed779 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ to your `composer.json`: ``` "require": { - "jcupitt/vips" : "2.3.0" + "jcupitt/vips" : "2.4.0" } ``` From 5f8ed1049f90b1a95a39a127fabd43b2147789d0 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 2 Oct 2024 13:30:46 +0100 Subject: [PATCH 100/123] improve TargetCustom docs --- src/SourceCustom.php | 9 ++++++--- src/TargetCustom.php | 35 ++++++++++++++++++++++++++--------- 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/src/SourceCustom.php b/src/SourceCustom.php index 47d9bb4..20ecc7c 100644 --- a/src/SourceCustom.php +++ b/src/SourceCustom.php @@ -20,6 +20,7 @@ public function __construct() /** * Attach a read handler. + * * The interface is similar to fread. The handler is given a number * of bytes to fetch, and should return a bytes-like object containing up * to that number of bytes. If there is no more data available, it should @@ -32,16 +33,18 @@ public function onRead(callable $callback): void /** * Attach a seek handler. + * * The interface is the same as fseek, so the handler is passed * parameters for $offset and $whence with the same meanings. * However, the handler MUST return the new seek position. A simple way * to do this is to call ftell() and return that result. * Seek handlers are optional. If you do not set one, your source will be * treated as unseekable and libvips will do extra caching. + * * $whence in particular: - * 0 => start - * 1 => current position - * 2 => end + * - 0 => start + * - 1 => current position + * - 2 => end */ public function onSeek(callable $callback): void { diff --git a/src/TargetCustom.php b/src/TargetCustom.php index dedb149..99b1757 100644 --- a/src/TargetCustom.php +++ b/src/TargetCustom.php @@ -20,8 +20,13 @@ public function __construct() /** * Attach a write handler. - * The interface is exactly as fwrite. The handler is given a bytes-like object to write, - * and should return the number of bytes written. + * + * The interface is similar to fwrite(). The handler is given a + * bytes-like object to write, and should return the number of bytes + * written. + * + * Unlike fwrite, you should return -1 on error. + * * @throws Exception */ public function onWrite(callable $callback): void @@ -31,10 +36,12 @@ public function onWrite(callable $callback): void /** * Attach a read handler. - * The interface is similar to fread. The handler is given a number + * + * The interface is similar to fread(). The handler is given a number * of bytes to fetch, and should return a bytes-like object containing up * to that number of bytes. If there is no more data available, it should * return null. + * * Read handlers on VipsTarget are optional. If you do not set one, your * target will be treated as unreadable and libvips will be unable to * write some file types (just TIFF, as of the time of writing). @@ -48,16 +55,19 @@ public function onRead(callable $callback): void /** * Attach a seek handler. - * The interface is the same as fseek, so the handler is passed + * + * The interface is similar to fseek, so the handler is passed * parameters for $offset and $whence with the same meanings. * However, the handler MUST return the new seek position. A simple way * to do this is to call ftell() and return that result. + * * Seek handlers are optional. If you do not set one, your source will be * treated as unseekable and libvips will do extra caching. + * * $whence in particular: - * 0 => start - * 1 => current position - * 2 => end + * - 0 => start + * - 1 => current position + * - 2 => end */ public function onSeek(callable $callback): void { @@ -68,9 +78,12 @@ public function onSeek(callable $callback): void /** * Attach an end handler. + * * This optional handler is called at the end of write. It should do any * cleaning up necessary, and return 0 on success and -1 on error. - * Automatically falls back to onFinish if libvips <8.13 + * + * Automatically falls back to onFinish if libvips <8.13. + * * @throws Exception */ public function onEnd(callable $callback): void @@ -84,8 +97,12 @@ public function onEnd(callable $callback): void /** * Attach a finish handler. - * For libvips 8.13 and later, this method is deprecated in favour of @throws Exception + * + * For libvips 8.13 and later, this method is deprecated in favour of + * onEnd(). + * * @see TargetCustom::onEnd() + * @throws Exception */ public function onFinish(callable $callback): void { From 0c20db91c8402a9729aa75175df91a7833d441e3 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 2 Oct 2024 13:33:32 +0100 Subject: [PATCH 101/123] remove stray whitespace --- src/TargetCustom.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/TargetCustom.php b/src/TargetCustom.php index 99b1757..a30b677 100644 --- a/src/TargetCustom.php +++ b/src/TargetCustom.php @@ -21,9 +21,9 @@ public function __construct() /** * Attach a write handler. * - * The interface is similar to fwrite(). The handler is given a - * bytes-like object to write, and should return the number of bytes - * written. + * The interface is similar to fwrite(). The handler is given a + * bytes-like object to write, and should return the number of bytes + * written. * * Unlike fwrite, you should return -1 on error. * @@ -98,7 +98,7 @@ public function onEnd(callable $callback): void /** * Attach a finish handler. * - * For libvips 8.13 and later, this method is deprecated in favour of + * For libvips 8.13 and later, this method is deprecated in favour of * onEnd(). * * @see TargetCustom::onEnd() From a8cc66c421f5b71568f9cf52a5bfcb818af52142 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 12 Oct 2024 14:37:22 +0100 Subject: [PATCH 102/123] update for 8.16 --- examples/generate_phpdoc.py | 2 +- src/ImageAutodoc.php | 33 +++++++++++----------- src/SdfShape.php | 56 +++++++++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 18 deletions(-) create mode 100644 src/SdfShape.php diff --git a/examples/generate_phpdoc.py b/examples/generate_phpdoc.py index 5a7d310..5b89216 100755 --- a/examples/generate_phpdoc.py +++ b/examples/generate_phpdoc.py @@ -1,4 +1,4 @@ -#!/usr/bin/python3 +#!/usr/bin/env python3 # needs pyvips 2.2.3 or later diff --git a/src/ImageAutodoc.php b/src/ImageAutodoc.php index c773791..942aa15 100644 --- a/src/ImageAutodoc.php +++ b/src/ImageAutodoc.php @@ -87,6 +87,8 @@ * @throws Exception * @method Image abs(array $options = []) Absolute value of an image. * @throws Exception + * @method Image addalpha(array $options = []) Append an alpha channel. + * @throws Exception * @method Image affine(float[]|float $matrix, array $options = []) Affine transform of an image. * @throws Exception * @method static Image analyzeload(string $filename, array $options = []) Load an Analyze6 image. @@ -120,8 +122,6 @@ * @throws Exception * @method Image byteswap(array $options = []) Byteswap an image. * @throws Exception - * @method Image cache(array $options = []) Cache an image. - * @throws Exception * @method Image canny(array $options = []) Canny edge detector. * @throws Exception * @method Image case(Image[]|Image $cases, array $options = []) Use pixel values to pick cases from an array of images. @@ -129,6 +129,8 @@ * @method Image cast(string $format, array $options = []) Cast an image. * @see BandFormat for possible values for $format * @throws Exception + * @method Image clamp(array $options = []) Clamp values of an image. + * @throws Exception * @method Image colourspace(string $space, array $options = []) Convert to a new colorspace. * @see Interpretation for possible values for $space * @throws Exception @@ -372,18 +374,6 @@ * @throws Exception * @method void jxlsave_target(Target $target, array $options = []) Save image in JPEG-XL format. * @throws Exception - * @method static Image kakaduload(string $filename, array $options = []) Load JPEG2000 image. - * @throws Exception - * @method static Image kakaduload_buffer(string $buffer, array $options = []) Load JPEG2000 image. - * @throws Exception - * @method static Image kakaduload_source(Source $source, array $options = []) Load JPEG2000 image. - * @throws Exception - * @method void kakadusave(string $filename, array $options = []) Save image in JPEG2000 format. - * @throws Exception - * @method string kakadusave_buffer(array $options = []) Save image in JPEG2000 format. - * @throws Exception - * @method void kakadusave_target(Target $target, array $options = []) Save image in JPEG2000 format. - * @throws Exception * @method Image labelregions(array $options = []) Label regions in an image. * @throws Exception * @method Image linear(float[]|float $a, float[]|float $b, array $options = []) Calculate (a * in + b). @@ -451,6 +441,8 @@ * @throws Exception * @method float max(array $options = []) Find image maximum. * @throws Exception + * @method Image maxpair(Image $right, array $options = []) Maximum of a pair of images. + * @throws Exception * @method Image measure(integer $h, integer $v, array $options = []) Measure a set of patches on a color chart. * @throws Exception * @method Image merge(Image $sec, string $direction, integer $dx, integer $dy, array $options = []) Merge two images. @@ -458,6 +450,8 @@ * @throws Exception * @method float min(array $options = []) Find image minimum. * @throws Exception + * @method Image minpair(Image $right, array $options = []) Minimum of a pair of images. + * @throws Exception * @method Image morph(Image $mask, string $morph, array $options = []) Morphology operation. * @see OperationMorphology for possible values for $morph * @throws Exception @@ -499,9 +493,9 @@ * @throws Exception * @method static Image pngload_source(Source $source, array $options = []) Load png from source. * @throws Exception - * @method void pngsave(string $filename, array $options = []) Save image to png file. + * @method void pngsave(string $filename, array $options = []) Save image to file as PNG. * @throws Exception - * @method string pngsave_buffer(array $options = []) Save image to png buffer. + * @method string pngsave_buffer(array $options = []) Save image to buffer as PNG. * @throws Exception * @method void pngsave_target(Target $target, array $options = []) Save image to target as PNG. * @throws Exception @@ -553,7 +547,9 @@ * @throws Exception * @method void rawsave(string $filename, array $options = []) Save image to raw file. * @throws Exception - * @method void rawsave_fd(integer $fd, array $options = []) Write raw image to file descriptor. + * @method string rawsave_buffer(array $options = []) Write raw image to buffer. + * @throws Exception + * @method void rawsave_target(Target $target, array $options = []) Write raw image to target. * @throws Exception * @method Image recomb(Image $m, array $options = []) Linear recombination with matrix. * @throws Exception @@ -599,6 +595,9 @@ * @throws Exception * @method Image scharr(array $options = []) Scharr edge detector. * @throws Exception + * @method static Image sdf(integer $width, integer $height, string $shape, array $options = []) Create an SDF image. + * @see SdfShape for possible values for $shape + * @throws Exception * @method Image sequential(array $options = []) Check sequential access. * @throws Exception * @method Image sharpen(array $options = []) Unsharp masking for print. diff --git a/src/SdfShape.php b/src/SdfShape.php new file mode 100644 index 0000000..cbb590e --- /dev/null +++ b/src/SdfShape.php @@ -0,0 +1,56 @@ + + * @copyright 2016 John Cupitt + * @license https://opensource.org/licenses/MIT MIT + * @link https://github.com/jcupitt/php-vips + */ + +namespace Jcupitt\Vips; + +/** + * The SdfShape enum. + * @category Images + * @package Jcupitt\Vips + * @author John Cupitt + * @copyright 2016 John Cupitt + * @license https://opensource.org/licenses/MIT MIT + * @link https://github.com/jcupitt/php-vips + */ +abstract class SdfShape +{ + const CIRCLE = 'circle'; + const BOX = 'box'; + const ROUNDED_BOX = 'rounded-box'; + const LINE = 'line'; +} From f158e3e96095e050ce2b2414637c2fb9e5157579 Mon Sep 17 00:00:00 2001 From: Thomas Date: Fri, 10 Jan 2025 10:30:37 -0600 Subject: [PATCH 103/123] PHP 8.4 support (#258) --- .github/workflows/ci.yml | 11 +++++++---- src/Exception.php | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index efa5647..5d60e0f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,7 +5,7 @@ on: [ push, pull_request ] jobs: CI: name: ${{ matrix.php }} - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: @@ -15,21 +15,24 @@ jobs: - php: '8.1' - php: '8.2' - php: '8.3' + - php: '8.3' + - php: '8.4' steps: - name: Setup PHP uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} - ini-values: zend.max_allowed_stack_size=-1 + ini-values: zend.max_allowed_stack_size=-1,ffi.enable=true + extensions: ffi, exif tools: composer:v2 coverage: none - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install vips - run: sudo apt install -y libvips --no-install-recommends + run: sudo apt install -y --no-install-recommends libvips - name: Install composer dependencies run: | diff --git a/src/Exception.php b/src/Exception.php index ce5df1d..a3704e7 100644 --- a/src/Exception.php +++ b/src/Exception.php @@ -51,7 +51,7 @@ */ class Exception extends \Exception { - public function __construct($message = "", $code = 0, \Throwable $previous = null) + public function __construct($message = "", $code = 0, ?\Throwable $previous = null) { if ($message == "") { $message = "libvips error: " . FFI::vips()->vips_error_buffer(); From b4478a8549eba5c9c87adcdd3eee7f4eed6fd73e Mon Sep 17 00:00:00 2001 From: Kleis Auke Wolthuizen Date: Mon, 13 Jan 2025 12:39:33 +0100 Subject: [PATCH 104/123] Cleanup CI (#259) - Test with PHP 8.3 only once. - Remove redundant exif extension. --- .github/workflows/ci.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5d60e0f..16248b1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,6 @@ jobs: - php: '8.1' - php: '8.2' - php: '8.3' - - php: '8.3' - php: '8.4' steps: @@ -23,8 +22,8 @@ jobs: uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} - ini-values: zend.max_allowed_stack_size=-1,ffi.enable=true - extensions: ffi, exif + ini-values: ffi.enable=true, zend.max_allowed_stack_size=-1 + extensions: ffi tools: composer:v2 coverage: none From 256575d7c5247d4fb3e98bf10ab1e10a571684c7 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Mon, 13 Jan 2025 16:58:09 +0000 Subject: [PATCH 105/123] get ready for 2.4.1 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1854df8..07b5b2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to `php-vips` will be documented in this file. ## master +## 2.4.1 - 2025-01-13 + +- fix php 8.4 compatibility [deluxetom] + ## 2.4.0 - 2024-04-09 - added `animate-image.php` example [jcupitt] From 3b50384f980a20de65fe830071530c12ec353ae7 Mon Sep 17 00:00:00 2001 From: Kleis Auke Wolthuizen Date: Fri, 4 Apr 2025 13:24:38 +0200 Subject: [PATCH 106/123] Move `zend.max_allowed_stack_size=-1` check to `signalConnect()` (#261) Most users don't need to set this INI directive; it's only required when explicitly calling `signalConnect()` or using the `SourceCustom` and/or `TargetCustom` classes. --- src/FFI.php | 4 ---- src/GObject.php | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/FFI.php b/src/FFI.php index 70a399b..a48aeed 100644 --- a/src/FFI.php +++ b/src/FFI.php @@ -243,10 +243,6 @@ private static function init(): void if (!ini_get('ffi.enable')) { throw new Exception("ffi.enable not set to 'true'"); } - if (version_compare(PHP_VERSION, '8.3', '>=') && - ini_get('zend.max_allowed_stack_size') != '-1') { - throw new Exception("zend.max_allowed_stack_size not set to '-1'"); - } $vips_libname = self::libraryName("libvips", 42); if (PHP_OS_FAMILY === "Windows") { diff --git a/src/GObject.php b/src/GObject.php index 2959e25..50ae8d1 100644 --- a/src/GObject.php +++ b/src/GObject.php @@ -60,6 +60,14 @@ abstract class GObject */ private CData $pointer; + /** + * libvips executes FFI callbacks off the main thread and this confuses + * the stack limit checks available since PHP 8.3.0. We need to check + * if `zend.max_allowed_stack_size` is set to `-1`. + * See: https://github.com/libvips/php-vips/pull/237. + */ + private static bool $check_max_stack_size = true; + /** * Wrap a GObject around an underlying vips resource. The GObject takes * ownership of the pointer and will unref it on finalize. @@ -104,6 +112,16 @@ public function unref(): void */ public function signalConnect(string $name, callable $callback): void { + if (self::$check_max_stack_size) { + $max_allowed_stack_size = ini_get('zend.max_allowed_stack_size'); + if ($max_allowed_stack_size !== false && + $max_allowed_stack_size !== '-1') { + throw new Exception("signalConnect() requires zend.max_allowed_stack_size set to '-1'"); + } + + self::$check_max_stack_size = false; + } + $marshaler = self::getMarshaler($name, $callback); if ($marshaler === null) { throw new Exception("unsupported signal $name"); From 9ada9ec83a546b8a8230cc63da506de6722240b1 Mon Sep 17 00:00:00 2001 From: Kleis Auke Wolthuizen Date: Fri, 4 Apr 2025 13:25:19 +0200 Subject: [PATCH 107/123] Add libvips version check to example in README.md (#262) Useful for troubleshooting installation issues. --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 39ed779..7d3dbc9 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,9 @@ To your `php.ini`. require __DIR__ . '/vendor/autoload.php'; use Jcupitt\Vips; +// check libvips version +echo 'libvips version: ' . Vips\Config::version() . PHP_EOL; + // fast thumbnail generator $image = Vips\Image::thumbnail('somefile.jpg', 128); $image->writeToFile('tiny.jpg'); From efbf4c4314d24b5d84e64de52683594bb08920c7 Mon Sep 17 00:00:00 2001 From: Kleis Auke Wolthuizen Date: Fri, 4 Apr 2025 13:26:58 +0200 Subject: [PATCH 108/123] Revise logic for unified (semistatic) libvips binaries (#263) Resolves: #246. --- src/FFI.php | 42 ++++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/src/FFI.php b/src/FFI.php index a48aeed..6af6dce 100644 --- a/src/FFI.php +++ b/src/FFI.php @@ -245,13 +245,8 @@ private static function init(): void } $vips_libname = self::libraryName("libvips", 42); - if (PHP_OS_FAMILY === "Windows") { - $glib_libname = self::libraryName("libglib-2.0", 0); - $gobject_libname = self::libraryName("libgobject-2.0", 0); - } else { - $glib_libname = $vips_libname; - $gobject_libname = $vips_libname; - } + $glib_libname = self::libraryName("libglib-2.0", 0); + $gobject_libname = self::libraryName("libgobject-2.0", 0); Utils::debugLog("init", ["library" => $vips_libname]); @@ -771,21 +766,24 @@ private static function init(): void } Utils::debugLog("init", ["binding ..."]); - self::$glib = self::libraryLoad( - $libraryPaths, - $glib_libname, - $glib_decls - ); - self::$gobject = self::libraryLoad( - $libraryPaths, - $gobject_libname, - $gobject_decls - ); - self::$vips = self::libraryLoad( - $libraryPaths, - $vips_libname, - $vips_decls - ); + + /** + * We can sometimes get dependent libraries from libvips -- either the platform + * will open dependencies for us automatically, or the libvips binary has been + * built to includes all main dependencies (common on Windows, can happen + * elsewhere). + * + * We must get GLib functions from libvips if we can, since it will be the + * one that libvips itself is using, and they will share runtime types. + */ + self::$glib = + self::libraryLoad($libraryPaths, $vips_libname, $glib_decls) ?? + self::libraryLoad($libraryPaths, $glib_libname, $glib_decls); + self::$gobject = + self::libraryLoad($libraryPaths, $vips_libname, $gobject_decls) ?? + self::libraryLoad($libraryPaths, $gobject_libname, $gobject_decls); + + self::$vips = self::libraryLoad($libraryPaths, $vips_libname, $vips_decls); # Useful for debugging # self::$vips->vips_leak_set(1); From 1ffcd1f57ae4a2275dcf5579dc15640fdd523c4c Mon Sep 17 00:00:00 2001 From: Kleis Auke Wolthuizen Date: Fri, 4 Apr 2025 15:07:32 +0200 Subject: [PATCH 109/123] Add `FFI::addLibraryPath` utility (#264) * Add `FFI::addLibraryPath` utility In favor of the `VIPSHOME` env. Resolves: #232. * Incorporate review comment --- README.md | 3 ++ src/FFI.php | 79 ++++++++++++++++++++++++++++++++++++----------------- 2 files changed, 57 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 7d3dbc9..e435a8e 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,9 @@ To your `php.ini`. require __DIR__ . '/vendor/autoload.php'; use Jcupitt\Vips; +// handy for Windows +Vips\FFI::addLibraryPath("C:/vips-dev-8.16/bin"); + // check libvips version echo 'libvips version: ' . Vips\Config::version() . PHP_EOL; diff --git a/src/FFI.php b/src/FFI.php index 6af6dce..f259f7b 100644 --- a/src/FFI.php +++ b/src/FFI.php @@ -79,6 +79,15 @@ class FFI */ private static bool $ffi_inited = false; + /** + * A list of paths where libvips might reside. + * + * @internal + */ + private static array $libraryPaths = [ + "" // system library + ]; + /** * Look up these once. * @@ -169,6 +178,39 @@ public static function atLeast(int $x, int $y, int $z = 0): bool self::$library_micro >= $z); } + /** + * Adds a directory to the search path for shared libraries. + * + * This method has no effect if FFI handles are already initialized, + * if the specified path is non-existent, or if the path is already + * included. + * + * @param string $path The path of the library. + * @return bool `true` if the path was added; otherwise, `false`. + */ + public static function addLibraryPath(string $path): bool + { + // Already initialized. + if (self::$ffi_inited) { + return false; + } + + $path = realpath($path); + if ($path === false) { + return false; + } + + $path .= DIRECTORY_SEPARATOR; + + if (in_array($path, self::$libraryPaths)) { + return false; + } + + self::$libraryPaths[] = $path; + + return true; + } + /** * Shut down libvips. Call this just before process exit. * @@ -208,12 +250,11 @@ private static function libraryName(string $name, int $abi): string } private static function libraryLoad( - array $libraryPaths, string $libraryName, string $interface ): ?\FFI { Utils::debugLog("trying to open", ["libraryName" => $libraryName]); - foreach ($libraryPaths as $path) { + foreach (self::$libraryPaths as $path) { Utils::debugLog("trying path", ["path" => $path]); try { $library = \FFI::cdef($interface, $path . $libraryName); @@ -252,26 +293,14 @@ private static function init(): void $is_64bits = PHP_INT_SIZE === 8; - $libraryPaths = [ - "" // system library - ]; - - $vipshome = getenv("VIPSHOME"); - if ($vipshome) { - // lib/ predicates lib/ - $libraryPaths[] = $vipshome . ($is_64bits ? "/lib64/" : "/lib32/"); - // lib/ is always searched - $libraryPaths[] = $vipshome . "/lib/"; - } - if (PHP_OS_FAMILY === "OSX" || PHP_OS_FAMILY === "Darwin") { // Homebrew on Apple Silicon - $libraryPaths[] = "/opt/homebrew/lib/"; + self::addLibraryPath("/opt/homebrew/lib"); // See https://github.com/Homebrew/brew/issues/13481#issuecomment-1207203483 - $libraryPaths[] = "/usr/local/lib/"; + self::addLibraryPath("/usr/local/lib"); } - $vips = self::libraryLoad($libraryPaths, $vips_libname, <<<'CPP' + $vips = self::libraryLoad($vips_libname, <<<'CPP' int vips_init (const char *argv0); const char *vips_error_buffer (void); int vips_version(int flag); @@ -279,10 +308,10 @@ private static function init(): void if ($vips === null) { // drop the "" (system path) member - array_shift($libraryPaths); + array_shift(self::$libraryPaths); $msg = "Unable to open library '$vips_libname'"; - if (!empty($libraryPaths)) { - $msg .= " in any of ['" . implode("', '", $libraryPaths) . "']"; + if (!empty(self::$libraryPaths)) { + $msg .= " in any of ['" . implode("', '", self::$libraryPaths) . "']"; } $msg .= ". Make sure that you've installed libvips and that '$vips_libname'"; $msg .= " is on your system's library search path."; @@ -777,13 +806,13 @@ private static function init(): void * one that libvips itself is using, and they will share runtime types. */ self::$glib = - self::libraryLoad($libraryPaths, $vips_libname, $glib_decls) ?? - self::libraryLoad($libraryPaths, $glib_libname, $glib_decls); + self::libraryLoad($vips_libname, $glib_decls) ?? + self::libraryLoad($glib_libname, $glib_decls); self::$gobject = - self::libraryLoad($libraryPaths, $vips_libname, $gobject_decls) ?? - self::libraryLoad($libraryPaths, $gobject_libname, $gobject_decls); + self::libraryLoad($vips_libname, $gobject_decls) ?? + self::libraryLoad($gobject_libname, $gobject_decls); - self::$vips = self::libraryLoad($libraryPaths, $vips_libname, $vips_decls); + self::$vips = self::libraryLoad($vips_libname, $vips_decls); # Useful for debugging # self::$vips->vips_leak_set(1); From 679e3cc5b64b6adc3cca43ca7fd162f4b0b8a4fb Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 4 Apr 2025 16:41:49 +0100 Subject: [PATCH 110/123] update for 2.4.2 --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 07b5b2d..009d1e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ All notable changes to `php-vips` will be documented in this file. ## master +## 2.4.2 - 2025-04-04 + +- only test `max_allowed_stack_size` when we have to [kleisauke] +- better libvips finding [kleisauke] + ## 2.4.1 - 2025-01-13 - fix php 8.4 compatibility [deluxetom] From a54c1cceea581b592a199edd61a7c06f44a24c08 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 4 Apr 2025 18:10:13 +0100 Subject: [PATCH 111/123] release 2.5.0 and delete 2,.4.2, sorry about that there has been an API addition, so there should be a minor version bump --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 009d1e5..b543c23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,10 @@ All notable changes to `php-vips` will be documented in this file. ## master -## 2.4.2 - 2025-04-04 +## 2.5.0 - 2025-04-04 +- add addLibraryPath() to let users set their libvips location [kleisauke] - only test `max_allowed_stack_size` when we have to [kleisauke] -- better libvips finding [kleisauke] ## 2.4.1 - 2025-01-13 From 693250c2b5870abe91b3e91cda9fcb33ce6c49bc Mon Sep 17 00:00:00 2001 From: Karel Wintersky Date: Wed, 30 Apr 2025 07:45:17 +0300 Subject: [PATCH 112/123] Create .gitattributes (#269) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR adds a .gitattributes file to exclude specific files and directories from exports/installing composer package. Purpose: Keeps repository metadata and tooling files out of distribution packages. Follows best practices for cleaner project exports. Impact: No runtime changes—this only affects repository operations like git archive. --- .gitattributes | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..ebf5613 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,18 @@ +* text=auto +* eol=lf + +.git export-ignore +.gitattributes export-ignore +.gitignore export-ignore +CREDITS export-ignore +phpcs-ruleset.xml export-ignore +phpdoc.xml export-ignore +phpunit.xml export-ignore +scrutinizer.yml export-ignore +.github export-ignore +examples export-ignore +tests export-ignore +API-1.0.0 export-ignore +RELEASE-2.0.0 export-ignore +CONTRIBUTING.md export-ignore +CHANGELOG.md export-ignore From cf920e59feaf367e244aaee3351317b8154c12f2 Mon Sep 17 00:00:00 2001 From: Kleis Auke Wolthuizen Date: Wed, 30 Apr 2025 14:02:53 +0200 Subject: [PATCH 113/123] Update docs and enums for upcoming libvips 8.17 (#271) --- src/ImageAutodoc.php | 8 ++++++-- src/Intent.php | 1 + src/Kernel.php | 2 ++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/ImageAutodoc.php b/src/ImageAutodoc.php index 942aa15..a0b2b4d 100644 --- a/src/ImageAutodoc.php +++ b/src/ImageAutodoc.php @@ -427,12 +427,14 @@ * @throws Exception * @method static Image matload(string $filename, array $options = []) Load mat from file. * @throws Exception - * @method Image matrixinvert(array $options = []) Invert an matrix. + * @method Image matrixinvert(array $options = []) Invert a matrix. * @throws Exception * @method static Image matrixload(string $filename, array $options = []) Load matrix. * @throws Exception * @method static Image matrixload_source(Source $source, array $options = []) Load matrix. * @throws Exception + * @method Image matrixmultiply(Image $right, array $options = []) Multiply two matrices. + * @throws Exception * @method void matrixprint(array $options = []) Print matrix. * @throws Exception * @method void matrixsave(string $filename, array $options = []) Save image to matrix. @@ -567,6 +569,8 @@ * @throws Exception * @method Image remainder_const(float[]|float $c, array $options = []) Remainder after integer division of an image and a constant. * @throws Exception + * @method Image remosaic(string $old_str, string $new_str, array $options = []) Rebuild an mosaiced image. + * @throws Exception * @method Image replicate(integer $across, integer $down, array $options = []) Replicate an image. * @throws Exception * @method Image resize(float $scale, array $options = []) Resize an image. @@ -589,7 +593,7 @@ * @throws Exception * @method Image scRGB2XYZ(array $options = []) Transform scRGB to XYZ. * @throws Exception - * @method Image scRGB2sRGB(array $options = []) Convert an scRGB image to sRGB. + * @method Image scRGB2sRGB(array $options = []) Convert scRGB to sRGB. * @throws Exception * @method Image scale(array $options = []) Scale an image to uchar. * @throws Exception diff --git a/src/Intent.php b/src/Intent.php index fab3249..658264e 100644 --- a/src/Intent.php +++ b/src/Intent.php @@ -53,4 +53,5 @@ abstract class Intent const RELATIVE = 'relative'; const SATURATION = 'saturation'; const ABSOLUTE = 'absolute'; + const AUTO = 'auto'; } diff --git a/src/Kernel.php b/src/Kernel.php index d5b76dc..27b335c 100644 --- a/src/Kernel.php +++ b/src/Kernel.php @@ -55,4 +55,6 @@ abstract class Kernel const MITCHELL = 'mitchell'; const LANCZOS2 = 'lanczos2'; const LANCZOS3 = 'lanczos3'; + const MKS2013 = 'mks2013'; + const MKS2021 = 'mks2021'; } From 1411b036f8a01b88dba3e206d905f57de335ddd1 Mon Sep 17 00:00:00 2001 From: Kleis Auke Wolthuizen Date: Wed, 30 Apr 2025 14:03:28 +0200 Subject: [PATCH 114/123] Remove redundant files (#270) --- .gitattributes | 3 --- API-1.0.0 | 0 RELEASE-2.0.0 | 0 scrutinizer.yml | 34 ---------------------------------- 4 files changed, 37 deletions(-) delete mode 100644 API-1.0.0 delete mode 100644 RELEASE-2.0.0 delete mode 100644 scrutinizer.yml diff --git a/.gitattributes b/.gitattributes index ebf5613..2748a19 100644 --- a/.gitattributes +++ b/.gitattributes @@ -8,11 +8,8 @@ CREDITS export-ignore phpcs-ruleset.xml export-ignore phpdoc.xml export-ignore phpunit.xml export-ignore -scrutinizer.yml export-ignore .github export-ignore examples export-ignore tests export-ignore -API-1.0.0 export-ignore -RELEASE-2.0.0 export-ignore CONTRIBUTING.md export-ignore CHANGELOG.md export-ignore diff --git a/API-1.0.0 b/API-1.0.0 deleted file mode 100644 index e69de29..0000000 diff --git a/RELEASE-2.0.0 b/RELEASE-2.0.0 deleted file mode 100644 index e69de29..0000000 diff --git a/scrutinizer.yml b/scrutinizer.yml deleted file mode 100644 index af8cd38..0000000 --- a/scrutinizer.yml +++ /dev/null @@ -1,34 +0,0 @@ -filter: - excluded_paths: [tests/*] -checks: - php: - code_rating: true - remove_extra_empty_lines: true - remove_php_closing_tag: true - remove_trailing_whitespace: true - fix_use_statements: - remove_unused: true - preserve_multiple: false - preserve_blanklines: true - order_alphabetically: true - fix_php_opening_tag: true - fix_linefeed: true - fix_line_ending: true - fix_identation_4spaces: true - fix_doc_comments: true -tools: - external_code_coverage: - timeout: 600 - runs: 3 - php_code_coverage: false - php_code_sniffer: - config: - standard: PSR2 - filter: - paths: ['src'] - php_loc: - enabled: true - excluded_dirs: [vendor, tests] - php_cpd: - enabled: true - excluded_dirs: [vendor, tests] \ No newline at end of file From b9ac979726b67fa1147f3197d964b02159bd7dd3 Mon Sep 17 00:00:00 2001 From: Kleis Auke Wolthuizen Date: Sat, 17 May 2025 12:40:38 +0200 Subject: [PATCH 115/123] CI: run `apt-get update` before installing libvips (#273) --- .github/workflows/ci.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 16248b1..cdc293e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,8 +30,10 @@ jobs: - name: Checkout code uses: actions/checkout@v4 - - name: Install vips - run: sudo apt install -y --no-install-recommends libvips + - name: Install libvips + run: | + sudo apt-get update + sudo apt-get install --no-install-recommends libvips - name: Install composer dependencies run: | From 04b551e75190e93ce4a0bafd4522ef4fd04cf6ef Mon Sep 17 00:00:00 2001 From: Kleis Auke Wolthuizen Date: Thu, 29 May 2025 11:31:29 +0200 Subject: [PATCH 116/123] Fix mem leak in `Source::newFromMemory()` (#275) * Fix mem leak in `Source::newFromMemory()` * Simplify --- src/FFI.php | 5 +++-- src/Source.php | 16 +++++++++------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/FFI.php b/src/FFI.php index f259f7b..ff7d689 100644 --- a/src/FFI.php +++ b/src/FFI.php @@ -539,6 +539,8 @@ private static function init(): void typedef void (*FreeFn)(void* a); void vips_value_set_blob (GValue* value, FreeFn free_fn, void* data, size_t length); +void* vips_blob_copy (const void *data, size_t length); +void vips_area_unref (void *area); const char* vips_value_get_ref_string (const GValue* value, size_t* length); @@ -760,8 +762,7 @@ private static function init(): void VipsSource* vips_source_new_from_descriptor (int descriptor); VipsSource* vips_source_new_from_file (const char* filename); -VipsSource* vips_source_new_from_memory (const void* data, - size_t size); +VipsSource* vips_source_new_from_blob (void* blob); typedef struct _VipsSourceCustom { VipsSource parent_object; diff --git a/src/Source.php b/src/Source.php index 9a028a7..5afdbb0 100644 --- a/src/Source.php +++ b/src/Source.php @@ -64,17 +64,19 @@ public static function newFromFile(string $filename): self */ public static function newFromMemory(string $data): self { - # we need to set the memory to a copy of the data that vips_lib - # can own and free - $n = strlen($data); - $memory = FFI::vips()->new("char[$n]", false, true); - \FFI::memcpy($memory, $data, $n); - $pointer = FFI::vips()->vips_source_new_from_memory($memory, $n); + $blob = FFI::vips()->vips_blob_copy($data, strlen($data)); + if ($blob === null) { + throw new Exception("can't create source from memory"); + } + $pointer = FFI::vips()->vips_source_new_from_blob($blob); if ($pointer === null) { + FFI::vips()->vips_area_unref($blob); throw new Exception("can't create source from memory"); } - return new self($pointer); + $source = new self($pointer); + FFI::vips()->vips_area_unref($blob); + return $source; } } From 509ccad421348a5d355804fa5b76fa88196ff10b Mon Sep 17 00:00:00 2001 From: Kleis Auke Wolthuizen Date: Thu, 29 May 2025 15:26:21 +0200 Subject: [PATCH 117/123] Improve doc comments for the source/target API (#276) --- src/Source.php | 27 ++++++++++++++++++--------- src/Target.php | 27 ++++++++++++++++++--------- 2 files changed, 36 insertions(+), 18 deletions(-) diff --git a/src/Source.php b/src/Source.php index 5afdbb0..2e8febd 100644 --- a/src/Source.php +++ b/src/Source.php @@ -19,9 +19,12 @@ public function __construct(\FFI\CData $pointer) } /** - * Make a new source from a file descriptor (a small integer). - * Make a new source that is attached to the descriptor. For example: - * $source = VipsSource::newFromDescriptor(0) + * Make a new source from a file descriptor. For example: + * + * ```php + * $source = Source::newFromDescriptor(0); + * ``` + * * Makes a descriptor attached to stdin. * You can pass this source to (for example) @see Image::newFromSource() * @throws Exception @@ -38,9 +41,12 @@ public static function newFromDescriptor(int $descriptor): self } /** - * Make a new source from a filename. - * Make a new source that is attached to the named file. For example: - * $source = VipsSource::newFromFile("myfile.jpg") + * Make a new source from a filename. For example: + * + * ```php + * $source = Source::newFromFile("myfile.jpg"); + * ``` + * * You can pass this source to (for example) @see Image::newFromSource() * @throws Exception */ @@ -56,9 +62,12 @@ public static function newFromFile(string $filename): self } /** - * Make a new source from a filename. - * Make a new source that uses the provided $data. For example: - * $source = VipsSource::newFromFile(file_get_contents("myfile.jpg")) + * Make a new source from a memory buffer. For example: + * + * ```php + * $source = Source::newFromMemory(file_get_contents("myfile.jpg")); + * ``` + * * You can pass this source to (for example) @see Image::newFromSource() * @throws Exception */ diff --git a/src/Target.php b/src/Target.php index eda298d..acf231d 100644 --- a/src/Target.php +++ b/src/Target.php @@ -19,9 +19,12 @@ public function __construct(\FFI\CData $pointer) } /** - * Make a new target to write to a file descriptor (a small integer). - * Make a new target that is attached to the descriptor. For example:: - * $target = VipsTarget.newToDescriptor(1) + * Make a new target to write to a file descriptor. For example: + * + * ```php + * $target = Target::newToDescriptor(1); + * ``` + * * Makes a descriptor attached to stdout. * You can pass this target to (for example) @see Image::writeToTarget() * @throws Exception @@ -37,9 +40,12 @@ public static function newToDescriptor(int $descriptor): self } /** - * Make a new target to write to a file name. - * Make a new target that is attached to the file name. For example:: - * $target = VipsTarget.newToFile("myfile.jpg") + * Make a new target to write to a filename. For example: + * + * ```php + * $target = Target::newToFile("myfile.jpg"); + * ``` + * * You can pass this target to (for example) @see Image::writeToTarget() * @throws Exception */ @@ -55,9 +61,12 @@ public static function newToFile(string $filename): self } /** - * Make a new target to write to a memory buffer. - * For example:: - * $target = VipsTarget.newToMemory() + * Make a new target to write to a memory buffer. For example: + * + * ```php + * $target = Target::newToMemory(); + * ``` + * * You can pass this target to (for example) @see Image::writeToTarget() * @throws Exception */ From 09f059c54d34b793dbc811ec1114ecccd0feec48 Mon Sep 17 00:00:00 2001 From: Kleis Auke Wolthuizen Date: Thu, 29 May 2025 15:28:29 +0200 Subject: [PATCH 118/123] Regenerate enums (#272) * Regenerate enums * Remove deprecated/redundant enums * Avoid generating redundant enums --- examples/generate_phpdoc.py | 11 +------ src/DemandStyle.php | 1 + src/FFI.php | 6 ---- src/ForeignJpegSubsample.php | 55 ------------------------------- src/GsfOutputCsvQuotingMode.php | 54 ------------------------------ src/Saveable.php | 58 --------------------------------- 6 files changed, 2 insertions(+), 183 deletions(-) delete mode 100644 src/ForeignJpegSubsample.php delete mode 100644 src/GsfOutputCsvQuotingMode.php delete mode 100644 src/Saveable.php diff --git a/examples/generate_phpdoc.py b/examples/generate_phpdoc.py index 5b89216..2d52e35 100755 --- a/examples/generate_phpdoc.py +++ b/examples/generate_phpdoc.py @@ -3,7 +3,7 @@ # needs pyvips 2.2.3 or later from pyvips import Image, Introspect, GValue, Error, \ - ffi, enum_dict, flags_dict, vips_lib, gobject_lib, \ + ffi, enum_dict, flags_dict, gobject_lib, \ type_map, type_name, type_from_name, nickname_find # This file generates the phpdoc comments for the magic methods and properties. @@ -275,11 +275,6 @@ def add_nickname(gtype, a, b): def generate_enums(): - # otherwise we're missing some enums - vips_lib.vips_token_get_type() - vips_lib.vips_saveable_get_type() - vips_lib.vips_image_type_get_type() - all_enums = [] def add_enum(gtype, a, b): @@ -292,10 +287,6 @@ def add_enum(gtype, a, b): type_map(type_from_name('GEnum'), add_enum) - # Filter internal enums - blacklist = ['VipsImageType', 'VipsToken'] - all_enums = [name for name in all_enums if name not in blacklist] - for name in all_enums: gtype = type_from_name(name) php_name = remove_prefix(name) diff --git a/src/DemandStyle.php b/src/DemandStyle.php index 36970a2..395a404 100644 --- a/src/DemandStyle.php +++ b/src/DemandStyle.php @@ -53,4 +53,5 @@ abstract class DemandStyle const SMALLTILE = 'smalltile'; const FATSTRIP = 'fatstrip'; const THINSTRIP = 'thinstrip'; + const ANY = 'any'; } diff --git a/src/FFI.php b/src/FFI.php index ff7d689..8083adc 100644 --- a/src/FFI.php +++ b/src/FFI.php @@ -553,9 +553,6 @@ private static function init(): void GType vips_interpretation_get_type (void); GType vips_operation_flags_get_type (void); GType vips_band_format_get_type (void); -GType vips_token_get_type (void); -GType vips_saveable_get_type (void); -GType vips_image_type_get_type (void); void vips_image_set_progress (VipsImage* image, bool progress); void vips_image_set_kill (VipsImage* image, bool kill); @@ -823,9 +820,6 @@ private static function init(): void self::$vips->vips_interpretation_get_type(); self::$vips->vips_operation_flags_get_type(); self::$vips->vips_band_format_get_type(); - self::$vips->vips_token_get_type(); - self::$vips->vips_saveable_get_type(); - self::$vips->vips_image_type_get_type(); // look these up in advance self::$ctypes = [ diff --git a/src/ForeignJpegSubsample.php b/src/ForeignJpegSubsample.php deleted file mode 100644 index f87bfa7..0000000 --- a/src/ForeignJpegSubsample.php +++ /dev/null @@ -1,55 +0,0 @@ - - * @copyright 2016 John Cupitt - * @license https://opensource.org/licenses/MIT MIT - * @link https://github.com/jcupitt/php-vips - */ - -namespace Jcupitt\Vips; - -/** - * The ForeignJpegSubsample enum. - * @category Images - * @package Jcupitt\Vips - * @author John Cupitt - * @copyright 2016 John Cupitt - * @license https://opensource.org/licenses/MIT MIT - * @link https://github.com/jcupitt/php-vips - */ -abstract class ForeignJpegSubsample -{ - const AUTO = 'auto'; - const ON = 'on'; - const OFF = 'off'; -} diff --git a/src/GsfOutputCsvQuotingMode.php b/src/GsfOutputCsvQuotingMode.php deleted file mode 100644 index a8a08f0..0000000 --- a/src/GsfOutputCsvQuotingMode.php +++ /dev/null @@ -1,54 +0,0 @@ - - * @copyright 2016 John Cupitt - * @license https://opensource.org/licenses/MIT MIT - * @link https://github.com/jcupitt/php-vips - */ - -namespace Jcupitt\Vips; - -/** - * The GsfOutputCsvQuotingMode enum. - * @category Images - * @package Jcupitt\Vips - * @author John Cupitt - * @copyright 2016 John Cupitt - * @license https://opensource.org/licenses/MIT MIT - * @link https://github.com/jcupitt/php-vips - */ -abstract class GsfOutputCsvQuotingMode -{ - const NEVER = 'never'; - const AUTO = 'auto'; -} diff --git a/src/Saveable.php b/src/Saveable.php deleted file mode 100644 index 8558f9e..0000000 --- a/src/Saveable.php +++ /dev/null @@ -1,58 +0,0 @@ - - * @copyright 2016 John Cupitt - * @license https://opensource.org/licenses/MIT MIT - * @link https://github.com/jcupitt/php-vips - */ - -namespace Jcupitt\Vips; - -/** - * The Saveable enum. - * @category Images - * @package Jcupitt\Vips - * @author John Cupitt - * @copyright 2016 John Cupitt - * @license https://opensource.org/licenses/MIT MIT - * @link https://github.com/jcupitt/php-vips - */ -abstract class Saveable -{ - const MONO = 'mono'; - const RGB = 'rgb'; - const RGBA = 'rgba'; - const RGBA_ONLY = 'rgba-only'; - const RGB_CMYK = 'rgb-cmyk'; - const ANY = 'any'; -} From 2e9280ba6e7dcc3c70c8ec007c35075215b71f46 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 3 Jun 2025 10:20:48 +0100 Subject: [PATCH 119/123] small autodocs cleanup, regen for 8.17 Don't make autodoc wrappers for `composite` and `DemandStyle`, regen for 8.17. --- CHANGELOG.md | 3 ++ examples/generate_phpdoc.py | 5 ++++ src/DemandStyle.php | 57 ------------------------------------- src/ImageAutodoc.php | 10 +++---- 4 files changed, 13 insertions(+), 62 deletions(-) delete mode 100644 src/DemandStyle.php diff --git a/CHANGELOG.md b/CHANGELOG.md index b543c23..f988170 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ All notable changes to `php-vips` will be documented in this file. ## master +- suppress autodoc of `composite` and `DemandStyle` [jcupitt] +- update docs for 8.17 [jcupitt] + ## 2.5.0 - 2025-04-04 - add addLibraryPath() to let users set their libvips location [kleisauke] diff --git a/examples/generate_phpdoc.py b/examples/generate_phpdoc.py index 2d52e35..5da1f52 100755 --- a/examples/generate_phpdoc.py +++ b/examples/generate_phpdoc.py @@ -225,6 +225,7 @@ def add_nickname(gtype, a, b): # these have hand-written methods, don't autodoc them no_generate = [ + 'composite', 'bandjoin', 'bandrank', 'ifthenelse', @@ -287,6 +288,10 @@ def add_enum(gtype, a, b): type_map(type_from_name('GEnum'), add_enum) + # Filter internal enums + blacklist = ['VipsDemandStyle'] + all_enums = [name for name in all_enums if name not in blacklist] + for name in all_enums: gtype = type_from_name(name) php_name = remove_prefix(name) diff --git a/src/DemandStyle.php b/src/DemandStyle.php deleted file mode 100644 index 395a404..0000000 --- a/src/DemandStyle.php +++ /dev/null @@ -1,57 +0,0 @@ - - * @copyright 2016 John Cupitt - * @license https://opensource.org/licenses/MIT MIT - * @link https://github.com/jcupitt/php-vips - */ - -namespace Jcupitt\Vips; - -/** - * The DemandStyle enum. - * @category Images - * @package Jcupitt\Vips - * @author John Cupitt - * @copyright 2016 John Cupitt - * @license https://opensource.org/licenses/MIT MIT - * @link https://github.com/jcupitt/php-vips - */ -abstract class DemandStyle -{ - const ERROR = 'error'; - const SMALLTILE = 'smalltile'; - const FATSTRIP = 'fatstrip'; - const THINSTRIP = 'thinstrip'; - const ANY = 'any'; -} diff --git a/src/ImageAutodoc.php b/src/ImageAutodoc.php index a0b2b4d..aaf5266 100644 --- a/src/ImageAutodoc.php +++ b/src/ImageAutodoc.php @@ -147,8 +147,6 @@ * @method Image complexget(string $get, array $options = []) Get a component from a complex image. * @see OperationComplexget for possible values for $get * @throws Exception - * @method static Image composite(Image[]|Image $in, integer[]|integer $mode, array $options = []) Blend an array of images with an array of blend modes. - * @throws Exception * @method Image composite2(Image $overlay, string $mode, array $options = []) Blend a pair of images with a blend mode. * @see BlendMode for possible values for $mode * @throws Exception @@ -382,9 +380,9 @@ * @throws Exception * @method static Image logmat(float $sigma, float $min_ampl, array $options = []) Make a Laplacian of Gaussian image. * @throws Exception - * @method static Image magickload(string $filename, array $options = []) Load file with ImageMagick. + * @method static Image magickload(string $filename, array $options = []) Load file with ImageMagick7. * @throws Exception - * @method static Image magickload_buffer(string $buffer, array $options = []) Load buffer with ImageMagick. + * @method static Image magickload_buffer(string $buffer, array $options = []) Load buffer with ImageMagick7. * @throws Exception * @method void magicksave(string $filename, array $options = []) Save file with ImageMagick. * @throws Exception @@ -503,7 +501,9 @@ * @throws Exception * @method static Image ppmload(string $filename, array $options = []) Load ppm from file. * @throws Exception - * @method static Image ppmload_source(Source $source, array $options = []) Load ppm base class. + * @method static Image ppmload_buffer(string $buffer, array $options = []) Load ppm from buffer. + * @throws Exception + * @method static Image ppmload_source(Source $source, array $options = []) Load ppm from source. * @throws Exception * @method void ppmsave(string $filename, array $options = []) Save image to ppm file. * @throws Exception From 233c1d0c8c851a90e40fed72fd3da3391eff521d Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 10 Dec 2025 12:12:33 +0000 Subject: [PATCH 120/123] update docs for libvips 8.18 --- CHANGELOG.md | 1 + src/ForeignKeep.php | 3 ++- src/ForeignPdfPageBox.php | 57 +++++++++++++++++++++++++++++++++++++++ src/ImageAutodoc.php | 46 +++++++++++++++++++++++++------ src/Interpretation.php | 2 ++ 5 files changed, 100 insertions(+), 9 deletions(-) create mode 100644 src/ForeignPdfPageBox.php diff --git a/CHANGELOG.md b/CHANGELOG.md index f988170..69cf7f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to `php-vips` will be documented in this file. - suppress autodoc of `composite` and `DemandStyle` [jcupitt] - update docs for 8.17 [jcupitt] +- update docs for 8.18 [jcupitt] ## 2.5.0 - 2025-04-04 diff --git a/src/ForeignKeep.php b/src/ForeignKeep.php index b7bec4b..33f61d0 100644 --- a/src/ForeignKeep.php +++ b/src/ForeignKeep.php @@ -55,5 +55,6 @@ abstract class ForeignKeep const IPTC = 4; const ICC = 8; const OTHER = 16; - const ALL = 31; + const GAINMAP = 32; + const ALL = 63; } diff --git a/src/ForeignPdfPageBox.php b/src/ForeignPdfPageBox.php new file mode 100644 index 0000000..b3e5593 --- /dev/null +++ b/src/ForeignPdfPageBox.php @@ -0,0 +1,57 @@ + + * @copyright 2016 John Cupitt + * @license https://opensource.org/licenses/MIT MIT + * @link https://github.com/jcupitt/php-vips + */ + +namespace Jcupitt\Vips; + +/** + * The ForeignPdfPageBox enum. + * @category Images + * @package Jcupitt\Vips + * @author John Cupitt + * @copyright 2016 John Cupitt + * @license https://opensource.org/licenses/MIT MIT + * @link https://github.com/jcupitt/php-vips + */ +abstract class ForeignPdfPageBox +{ + const MEDIA = 'media'; + const CROP = 'crop'; + const TRIM = 'trim'; + const BLEED = 'bleed'; + const ART = 'art'; +} diff --git a/src/ImageAutodoc.php b/src/ImageAutodoc.php index aaf5266..31b24ab 100644 --- a/src/ImageAutodoc.php +++ b/src/ImageAutodoc.php @@ -75,10 +75,18 @@ * @throws Exception * @method Image LabS2LabQ(array $options = []) Transform short Lab to LabQ coding. * @throws Exception + * @method Image Oklab2Oklch(array $options = []) Transform Oklab to Oklch. + * @throws Exception + * @method Image Oklab2XYZ(array $options = []) Transform Oklab to XYZ. + * @throws Exception + * @method Image Oklch2Oklab(array $options = []) Transform Oklch to Oklab. + * @throws Exception * @method Image XYZ2CMYK(array $options = []) Transform XYZ to CMYK. * @throws Exception * @method Image XYZ2Lab(array $options = []) Transform XYZ to Lab. * @throws Exception + * @method Image XYZ2Oklab(array $options = []) Transform XYZ to Oklab. + * @throws Exception * @method Image XYZ2Yxy(array $options = []) Transform XYZ to Yxy. * @throws Exception * @method Image XYZ2scRGB(array $options = []) Transform XYZ to scRGB. @@ -183,6 +191,12 @@ * @throws Exception * @method Image dECMC(Image $right, array $options = []) Calculate dECMC. * @throws Exception + * @method static Image dcrawload(string $filename, array $options = []) Load RAW camera files. + * @throws Exception + * @method static Image dcrawload_buffer(string $buffer, array $options = []) Load RAW camera files. + * @throws Exception + * @method static Image dcrawload_source(Source $source, array $options = []) Load RAW camera files. + * @throws Exception * @method float deviate(array $options = []) Find image standard deviation. * @throws Exception * @method Image draw_circle(float[]|float $ink, integer $cx, integer $cy, integer $radius, array $options = []) Draw a circle on an image. @@ -352,13 +366,13 @@ * @throws Exception * @method static Image jpegload_source(Source $source, array $options = []) Load image from jpeg source. * @throws Exception - * @method void jpegsave(string $filename, array $options = []) Save image to jpeg file. + * @method void jpegsave(string $filename, array $options = []) Save as jpeg. * @throws Exception - * @method string jpegsave_buffer(array $options = []) Save image to jpeg buffer. + * @method string jpegsave_buffer(array $options = []) Save as jpeg. * @throws Exception * @method void jpegsave_mime(array $options = []) Save image to jpeg mime. * @throws Exception - * @method void jpegsave_target(Target $target, array $options = []) Save image to jpeg target. + * @method void jpegsave_target(Target $target, array $options = []) Save as jpeg. * @throws Exception * @method static Image jxlload(string $filename, array $options = []) Load JPEG-XL image. * @throws Exception @@ -384,6 +398,8 @@ * @throws Exception * @method static Image magickload_buffer(string $buffer, array $options = []) Load buffer with ImageMagick7. * @throws Exception + * @method static Image magickload_source(Source $source, array $options = []) Load source with ImageMagick7. + * @throws Exception * @method void magicksave(string $filename, array $options = []) Save file with ImageMagick. * @throws Exception * @method string magicksave_buffer(array $options = []) Save image to magick buffer. @@ -475,11 +491,11 @@ * @throws Exception * @method static Image openslideload_source(Source $source, array $options = []) Load source with OpenSlide. * @throws Exception - * @method static Image pdfload(string $filename, array $options = []) Load PDF from file. + * @method static Image pdfload(string $filename, array $options = []) Load PDF from file (poppler). * @throws Exception - * @method static Image pdfload_buffer(string $buffer, array $options = []) Load PDF from buffer. + * @method static Image pdfload_buffer(string $buffer, array $options = []) Load PDF from buffer (poppler). * @throws Exception - * @method static Image pdfload_source(Source $source, array $options = []) Load PDF from source. + * @method static Image pdfload_source(Source $source, array $options = []) Load PDF from source (poppler). * @throws Exception * @method integer percent(float $percent, array $options = []) Find threshold for percent of pixels. * @throws Exception @@ -493,9 +509,9 @@ * @throws Exception * @method static Image pngload_source(Source $source, array $options = []) Load png from source. * @throws Exception - * @method void pngsave(string $filename, array $options = []) Save image to file as PNG. + * @method void pngsave(string $filename, array $options = []) Save image to file as png. * @throws Exception - * @method string pngsave_buffer(array $options = []) Save image to buffer as PNG. + * @method string pngsave_buffer(array $options = []) Save image to buffer as png. * @throws Exception * @method void pngsave_target(Target $target, array $options = []) Save image to target as PNG. * @throws Exception @@ -672,6 +688,20 @@ * @throws Exception * @method Image transpose3d(array $options = []) Transpose3d an image. * @throws Exception + * @method Image uhdr2scRGB(array $options = []) Transform uhdr to scRGB. + * @throws Exception + * @method static Image uhdrload(string $filename, array $options = []) Load a UHDR image. + * @throws Exception + * @method static Image uhdrload_buffer(string $buffer, array $options = []) Load a UHDR image. + * @throws Exception + * @method static Image uhdrload_source(Source $source, array $options = []) Load a UHDR image. + * @throws Exception + * @method void uhdrsave(string $filename, array $options = []) Save image in UltraHDR format. + * @throws Exception + * @method string uhdrsave_buffer(array $options = []) Save image in UltraHDR format. + * @throws Exception + * @method void uhdrsave_target(Target $target, array $options = []) Save image in UltraHDR format. + * @throws Exception * @method Image unpremultiply(array $options = []) Unpremultiply image alpha. * @throws Exception * @method static Image vipsload(string $filename, array $options = []) Load vips from file. diff --git a/src/Interpretation.php b/src/Interpretation.php index cccb983..235efa7 100644 --- a/src/Interpretation.php +++ b/src/Interpretation.php @@ -69,4 +69,6 @@ abstract class Interpretation const MATRIX = 'matrix'; const SCRGB = 'scrgb'; const HSV = 'hsv'; + const OKLAB = 'oklab'; + const OKLCH = 'oklch'; } From a9aedf3cebd619232915268cb51d7f60a9a0dd9a Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 10 Dec 2025 13:10:24 +0000 Subject: [PATCH 121/123] add getGainmap (#284) * add getGainmap And some tests. * Apply suggestion from @kleisauke Co-authored-by: Kleis Auke Wolthuizen --------- Co-authored-by: Kleis Auke Wolthuizen --- CHANGELOG.md | 1 + src/FFI.php | 6 ++++ src/Image.php | 31 +++++++++++++++++++ tests/GainmapTest.php | 61 +++++++++++++++++++++++++++++++++++++ tests/images/ultra-hdr.jpg | Bin 0 -> 1190669 bytes 5 files changed, 99 insertions(+) create mode 100644 tests/GainmapTest.php create mode 100644 tests/images/ultra-hdr.jpg diff --git a/CHANGELOG.md b/CHANGELOG.md index 69cf7f8..bce1023 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ All notable changes to `php-vips` will be documented in this file. - suppress autodoc of `composite` and `DemandStyle` [jcupitt] - update docs for 8.17 [jcupitt] - update docs for 8.18 [jcupitt] +- add getGainmap [jcupitt] ## 2.5.0 - 2025-04-04 diff --git a/src/FFI.php b/src/FFI.php index 8083adc..722065d 100644 --- a/src/FFI.php +++ b/src/FFI.php @@ -792,6 +792,12 @@ private static function init(): void CPP; } + if (self::atLeast(8, 18)) { + $vips_decls = $vips_decls . <<<'CPP' +VipsImage* vips_image_get_gainmap(const VipsImage* image); +CPP; + } + Utils::debugLog("init", ["binding ..."]); /** diff --git a/src/Image.php b/src/Image.php index afc9b45..ecfa512 100644 --- a/src/Image.php +++ b/src/Image.php @@ -1267,6 +1267,37 @@ public function getFields(): array return $fields; } + /** + * Get the gainmap attached to an image, or null for no gainmap. + * + * After modifying the gainmap, you'll need to use Image::set() to + * write it back to the image again under the name "gainmap", for example: + * + * ```php + * $gainmap = $image->getGainmap(); + * if ($gainmap != null) { + * $new_gainmap = $gainmap->crop(0, 0, 10, 10); + * $image = $image->copy(); + * $image->set("gainmap", $new_gainmap); + * } + * ``` + * + * @return ?Image + */ + public function getGainmap(): ?Image + { + if (FFI::atLeast(8, 18)) { + $pointer = FFI::vips()->vips_image_get_gainmap($this->pointer); + if ($pointer == null) { + return null; + } else { + return new Image($pointer); + } + } else { + return null; + } + } + /** * Set any property on the underlying image. * diff --git a/tests/GainmapTest.php b/tests/GainmapTest.php new file mode 100644 index 0000000..29fba6e --- /dev/null +++ b/tests/GainmapTest.php @@ -0,0 +1,61 @@ +vips_type_find('VipsOperation', 'uhdrload') == 0) { + $this->markTestSkipped('requires libvips built with Ultra HDR support'); + } + + $filename = __DIR__ . '/images/ultra-hdr.jpg'; + $this->image = Vips\Image::newFromFile($filename); + + $filename = __DIR__ . '/images/img_0076.jpg'; + $this->no_gainmap_image = Vips\Image::newFromFile($filename); + } + + protected function tearDown(): void + { + unset($this->image); + unset($this->no_gainmap_image); + } + + public function testVipsGetGainmap() + { + $gainmap = $this->image->getGainmap(); + $this->assertEquals($gainmap->width, 960); + + $gainmap = $this->no_gainmap_image->getGainmap(); + $this->assertEquals($gainmap, null); + } + + public function testVipsSetGainmap() + { + $gainmap = $this->image->getGainmap(); + $new_gainmap = $gainmap->crop(0, 0, 10, 10); + $image = $this->image->copy(); + $image->set("gainmap", $new_gainmap); + + $gainmap = $image->getGainmap(); + $this->assertEquals($gainmap->width, 10); + } +} diff --git a/tests/images/ultra-hdr.jpg b/tests/images/ultra-hdr.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2e9a7ccb6a708d7e6faa09a7db8e4e6a22a0e657 GIT binary patch literal 1190669 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≺$8zZbs)vv*!>NwQ_xe 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×K@Knfzhh2c6tx8nM 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 z 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 z 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�aOVs*AX* zczOFlR+0SoX!YJm@2-NwEhTaC%i?~JT_;~W!h+w-nc_Gah%{w@po7ly}^(wbxJ0p0JJm-|eK#+|Zj?^TE|4EGwUzg=d zcI`xTLa%iS|Aw$qT?J}q*FHJ(`t_*+23zXu!Pr5tEM{O{&1N@059Y1N`h4M!t*1nI zuvu^B&1wluep8Ry$9qnUeX(<^qldoXP@xyNucaNbmQSajDz4J&v6~t_G04387_(3; zRL!9vRr4$~L8gN?*i29yjr2RL(A$&FyifYMt6raQ*}?{1uW(WFXQQY?%lD}8ZDH}e zUBQvFd>}8Dqk=a$g2GD_`qS|c=Ka{f6P=j;z#DgR{`4U)_MjlK{!E!`I{r`ROM96) z-#%2%)BNyeaMg~%6hiAw4%?*YB5`8PD8av;`v;@M)R$dx!?shlaUEj;P3r-W8@YQkjMhEK1l>y<+LMsOF>!C2fwW+!m~8rtmG1MmPRM}i%m)q>r*fHPUR|6zh6N_=43;U`B z*;TD4P@^Ud1AZgGaITvpp3dOci5wACV&`1PDvQ&d7lEj`RPKv0v2DxZLjQl^x_?FD z{@;aY!Nyl!h%Cmmwuc>4C-Adw@0UEapzaOpRLn>v((HBf7AzvlLtwIN%etk|Dy>oA z0UhW|2(Wh7=p~`4%QPBR_%US!tAo8^Q}b9=i7ThjE`Db!yL>F+-G@j0V@p`UZ3G)< zaq|7fnKX03vdb`|qO!$=0F}Z}Ao-mEFysY>?@Q)6IipBsNI=uPju`~{^sf3CLPOEL z=vPX>iBpx4vc-GrI6^k2lFOQ3;SA|EG*sur#^LhGQ1hY;?*Y;+P><}8%H!nG=I9SV zRxVv&t4O56Plx};mKFsEEP7Hyn>ih*aZS@fgS)3NbJ;lMJCTF5X+Pg7AH z0llJp1E0hkW{BC@P@;wrl-32=m5_d@Cf6Q15XaEdMh@XRJ0*Yvu0u~HvjaQ}u@L^1 z)CgJycm)&E$Lm8-$EDHS2=a@S!FD}JOU;hsj{44Z(a;Q?y zv$z@6@E-Ojg*BlWh}N>_ES-i)PxJg6V{Z{IpUoyaGAb@I%U&~1jt{q=G?bBYyGPaB z!)^5VNRn#>E<8dWRukr<)wo35ytLg6@-Nx$5mnqE)AtESWPK9Ge-1+lu~T)Zs>^4T z)T4c8GrhAFbA7Sy31;&(GP5S z0OwCnx8c0#l0h?g7$;CfY!++q+c2!!#72!nnaoa&lf{RdAFrfijfB+Juc}#w17cy& z|Gtci2miSVCCl#&4hK_VLH-fbQVniVu9*HonbWa*NGjqFr&=C&kqT&=^|rKJr3XJo z^BzMOqC8QX-C=FZ>Ssv0bGdNQh_Mfr!bTF^cmiYVUZ4CPxPIT+earYYF=NkK!)wyB zNdr;wXtbO|PAeUDz2xfwLTY)xiof<5*}rOWc9Ren@K>_hP{^=;et*leVSx2NkVM4z zj8jfdu?FciXG7Bq&G)?PosqKM!ynle)8{HQYk^{aQNe81eX$fm#@WbP*=33hz+Yl% zdhJq-{QhD%gYoj(2ua?lT0-9;m~r@5G`%>60I~cmNF^%MJ!hi-;}Qfrd_@bV!KW)K z=e?%uZs#R7j(c;NXNBn23S6(7*ZQ7oOg+|kO82&7oA@fPBk(Y{h%$l`4uJEOeGLa* zOejbMVZu>sE+~n(z*RTayd}FysrrCUn@2c-oLap58xPS^ucDcp64U5UQ9LX43twD= zht>N%hvc5hge`SbczxWKnQYZJy$vRd0QU>bcI8j0Ejq&5iO*xfvC!yCIRCCqS_EDD5}lzdGIhO6kKHh%~CK4A|3= ziN(Yxst+ByC$H1MxadxeA|QosZ=QGt0+&SfxOiemaYw6RBNr840f=g60|%m4XQPx~ z6(W2@ok-C{Yd47j_Z)&Clo}XjoUL(+1-iw&*nna$S^IEMeirqLJbCW23~RCw1HM5lro56_H zLX^Z~+mW?RD!p5k@&?TtI7gIEC{wwM&dg;(HTq8j>i5JWqhRBbeh>0X;k8>9jB$NP8ka)``(nbt|X6T|gA z$#CEv(M>;!fH)nEwh9eSy*Kn}LOF^JhS4*IgE@m2a4&w!`p{(yo}6(3Lj1HJFF6n9IFZnITiXH*y^B&YKx8PI#HA2|8G^ZCijWMuLAnLjha2rehM zC{QlY7VQ0Q*_Gef*!Ko%ZBj**#+x3foX{>rJ#A_4Fy_ zYe$)woxAnA%@lHcLuS zc=JhE6UC>hZ}EB{tKDC@&p`bh?ShQR`TCC=jRx^O-HkgE{E`h8B`-sD4J1pSJ^k(y z2k+`(m2gPVrSbK4^?I!QNj*$EAq!ohNhXXjYX!L`$ZoNhu3bUPsRISmD3|Ko>(R-@ zJST%{?9M&+JmTwSPRD-w&(TcP^Z4`Sy}C40>y4i?nHS=pu}Tl6=qNvU=sx+d&!N04 ztY=B9zRQGygwYd+Y5d`~Q>AN09b31(eo>J0O|CZ>sVJ@+vuXNf2-NYnJ-P4E+ zb3f>FmYs{=*m`=bQ^I#X3Q2`gPhecLC zkP<9sYjY~o$J2_Pcb*IJ8`SyB*qy>Pm)CEZ%m7z*hpl&3PM;sweR-M2!|-~B<`F&? zyZ%*h_wUm^olWm27Bkwzl{7DdHa2W>ego$dowjAL4829Y~D>_IH zKA0W396Inup6mR^9^~z|E%OxO-9*^l=h!8iVjF4&w0Z80I&4O~6OC4N{0++9nvI5= z=?j_{PE1#)pV=^rY~3rrtM*hxqvl<~*T!W1%6D*s79Kx$CLd@%3i|7XpltERc%RQ^!x;`&ldz$s@U@fKavN|3)i zuLtOtAh6#r} z|MrE8XZ%6+iz|&?wenX7%kk0%trP!drZ1%tG8`bQ!p&^e_|5iTDa&g9_smZdu$fpp zqb^Q4`U9^9e{TAdx(@;0&%wPv(9i58u@8PYRKhvs7Ba%s4O)*DKHSn}^V>;PyyENv z3ZNd(E$%`P*kEBzt{{8ezyWK)Gf}S~KtbP3&3L@6E0p$au{KBbnnp92tG?8$J@WGT zT6{KtA$TP9>rF5?t0*{L(8|((SQQ)U+9K0y)CF0?k?t2$+g;umAA#EPvCd&|=7XOZ zRxXk5yyEqa-gf01WLCT8F5$ZpDYz+$^`0vG4g%4E(izAO4n8t{^jqOSfW>d95I!9{ z$UJB3ZMyJQL4>BE|0-`9hnfB3v^<09`gUsqK!Nlgxf6Fs=sXvK{F55kb)u&%;MC0v zXBjD?9;->`vb^ba*&W)oX5ul=(4p4R4nU7}tb+7-oo&G`eL9DdxZ133tB7zj7{{I1 zcUv<=F&(3?bI$VLYFr$dg&Chn_UfJje&$Zwz+wY&H8xH?*0~rqt8D;e!a@&PSCf&f zR=ICp4*^Al>w=&{6{>4n%7s?s%| ziqWd~o^S@0-cc|*q3`v&gS+nC{?zLxU2Zm#-n>O|;$n3=$`GXS_irAxdY(_LUnm~Ir=Uk7?>Zb0LgYDp1ukeI#9uDlJycpkQn})lz8=3 zC%1}CN5LeXsOt7ZBO*cUSEZ0sEQ1n{=vgjj$yHWQWA!dh?3upte9Cz?^FG!zCc*2{ z?u?I)F*!M>R-Jsw(Op_kX~RTUe#}jPkD_}ueed7AD;wzP8y-`uHq0UnBr0t0QOwSR zv0#W14mV$<2AAsuA1VY;$1kexCwF8aZeyvs4&{^Ua(=eD-sh&#wSE9>N^7=D>%n|3 zU9pOGgcr&zlQgKmcxIKCNHC=3{^~d08<>0c;aazhkDuG>YHr_UjQo-8=v!WvlZ6W9 zVax94ezeqMA1>ds;QoQ9`=|1Rz?%$@7d2F2h#_+&+di3dc12=rYGZ|w8MlJ(wv z$$Tp>9s=Hd@%B)#%tH2*2B3`%Z+|En)YVr4_OdYL+xi!y3=EmDI zSX7RmEj$MYzM5~9PV=c9{HSoBY6HN8jrS~Sk%+1aP(+suo`9X-gt1rxfSzHKjizPRGG zmLQb&SOby`K?za9`C5>khYNZ8-3UlHqRjE`h!_LN&Wvyx~R+X&=AL|#}Uj8*!ovdLpN7SQDV*rCd5<6yJKgU5d zS0|Zs129HvbtX*0G(7}~nWKv87tj8wzF;s9VL66M<<|gJ9@=lv$@1Dk*vXgjoKkO} z#l;l`e);2ygmWo|N9IGh^(MiNWV?iI1mr4*XaHm)`cB!o>Rv(J9#ebj-?udzg^gst zv<~}Wht23P^IWTyC1Vp%u$h%3R7YZePF)O%L0KGep{~?~T5CTqix`I_5lU?a*1Tf7Na+74xfe+4l7ifTY?4<|JXP z#~%RFc+>?q-*;eoA2|Nl!)JKt7~^q!8eI9;TB$spfuBBP0F!ykFs&$e<%C=Em-^57 z%e+Daj7i*eRfxCQ1GunUg*AEg4(}YZ+ChZ~lwauQflKGiVAXSO9$Vn^t-93brww)Z z3?6H0@)eGcNbu1Ku)JSMkjWtXF-*D#kMriEOT^S)sdsnG`P_u1Q&MYOnY#3Rf|`pR z@nY_Ry;VHUv(+RXliPHw@^-0zgtStf{bZ)@9^0L$(osiD<+M*6 zFIDV~PRky{B}s7(#3N4-v|skHI04kjizWt=c%y_HV)_#M5O#1pcp%?lal*x+sR#vH zsfTKiaP=}WzxsSTSpHW1RZ-r(q|pNV({AN3`?BYus(YaRO*Wb3>MaUG8ynKEh~r*d z7S@+kDkBH96ZoOkj%dy|*RSCj_SQ^RVxs_->=R+SPN<>FF`SIS%>rxJIom$t*Bk&d z%E_M}gLE}dMtQCe#CKNw2P*lNSg$+L7ija2)ao`Ik_Cxi;x@W~*07%~>T<17q?PRV zI^H+a{0DlITpuv7TgCYTZlhGmzYJSEfd09tpr3li5xP#x#9K5BTiJfd-Ty5ng5mw3 z%!NQw)J7cBVabmg-=5shD*T3Cil-yUPuPe7%VC$-%`%8h*T&0$0{Dd(G;3%XD7SemE!3N#pFDw3%;T_bs1$0#}Irm-jnLORB;)biJ zbXA!+{}^V0K?i`zM{j=DGaY@u{ zocI#xdD%uBo2IscI%rX=fj)&YT`>EyA1#5O@fcj*UPx#73;N%Id+AI?B_51H9bw5y zOT}L%R)`Xj-u=NQX83*YLpIV%6uqucuA;6Nm3*>@Qu@E23P2U$+cQS>htf{*EW;o8_llz_PXM@_>eXj*9mmcvSZffJ9}AW3V*pC$(O@7cplCs zbe?`n?l5!IygBiRiXww3%6loOOsfG;H=TH6493?3qZFa-mgz_cY!sn#J?m!`J1@g> z+7ck9$T}cVzZFi8h#Je=XB_kvLbP1>~}0}jedySl7I_>oRU1Mc$fewFE!AM$j3UZW z8TGs1o1;(=wp9TKP&&v*SEjU=88pX*a=V_5A>eG*#= zkHn-?jYFy4BGv}5Gl73EF)E?flj11H{{H~7`6hnp7Q2L6;_r1K7Vax+b;H#bm`yd( z<6Z(2g2eKLCwpmLu@hr(VoSVOsFr46(k33X#-!V)u>Nnrlk2iii_%jv!x2=)Qe7W~ z%Qxg4NhD#Dh)4mLUkQm@K4!KWw^AOsr7wPjYWMv3@sVA3^UByOL)kf8!&0n~K@u>5 zVPb7bVpm5pL~R))vvtV7DtCXr?~44SM5#a<;~i=wrjY*B*a!o*uGQcJsjO~7)NT1AFqP=RhM8PZ^$GhLC#+bA+*=jX76^imr4D^$Wv8WuAc}C#2<~?- ztX)W3uzqwumdR?2JZ0}F8nvOXaTBot$OfXNBM6zAuO36{5X*IXM*Qe|s;WDK_L1MR za81>Wzg%C5V$vyr{g#hWB-UoQWur-t_io`7Y}IAa z?ym;6r{1p1UT`J^WrcjAO5CwH{Mz;49+~C+Yr@Q;sRTdLdjJj{u@uhy=tris>U{AO z30zg|%`95e;{h;fGKHV8VN$oPsSe<*Ih(y1c~X}dNE;J??{WmF<7$SHDJtVqdJ$pr zd&X5RbyL3gcej5VC8r8_np&Z%)zX8Afwu$Mtr5tJ8@9j*nqw_QWZ|k_8^_p(iq&yL z3%4)w*;o7C{q$6_fP1Y6@*En>ev9Jo7|J#nA-!;v>J@(O`7Wq^vO>#!^5ief>(G7Y zo?+XheX%@NXEU$v?MAQ6?DhSCU+_cW3;K*R0li&7+j4%tg26RjBr5E#+lS9K_i>H4 zJk;)3;G@_K;2LnoU~bLxsQ339V-z8MHshHQNUgc=#{!+38D)G;c_cz^@biGe-;-^D z7{}5wo=)*(1>ckSZ%pVYV&RF+ap`X7kN-fI&ppcj*S~f@wQkJmxv@Ouw_YYrDEv~1 zp!Rw;+P_=#Y~*O<;@*9Z_cdvC4N@LtN@=J=+KcP*@V0vwM2_e8JxcUaeGwIXJ^5}) z`Jwr$x!wkA5Y~!bzQBWcihM_DP#`ssfPyRA^HHmCJGrd(RBw77^}s2m zDj|LG-_+}H4QjhHTqEyec{Rv{Q;q|5_(s$@l5E*7<&$kFZ{-XKpVia88O3?8rB+`{ zddXjWqRyQmHbem0LSEiuab;5tic7%NI%Fu>9%7?e`G|)d#?mgC<^Cz1Wj3`QSDqi{ z$j<$PMlvdr&6b!`Xy4`4CV#S_;|P!^&6=bfn#;Q`oxe?-a4HqO4q`NE^fqHu+g&** zh>LAm87_M7jMUDH6aQAj&iVTMs&ybiP43MA)Fd$x$B>iX4Z9oyNmP9$|*-^>|awX6dO-j3@QO=v&)8FR8YLb#6r8H~3BA%Z5XsXqfYC zsxepfkwj_$PoZGT5z5tquA5W7x`&}D2Yi5PK z>s{-9?(4#xx!i+r;hExxswen(+%h~r7(uWr{xB9g3*2m`rKQDQ+M6-~kVB7x^dYSY zS$rn8m#U7mnz>~V4K=+*VG$>LQqNK&QGQ?PV}+0=Duo1IX+7NJ<3bEaud3(~ z^2us*R=3O@L5*RAnu67{lv%JszLcKgAFrFA7lW8gbT%_5@r9qrx)r93ciw331Q^F> z=7FqIQm$v9Q!_He_C2taB!ApVRN`mrU}WiGcp$kn6z_363|RUh|6RvBtAJT*1uf2zw4eBX&qGX;h#H9_CfZ45vaLSbv{ z5;|K^Nq7=+tc;&7t-OnTMIOlJsBZ4GLGd7e&N=ThR)3k+`lKFA2HCU9Q~lD?+wrRY z1Ifa!DRsB8(GNGPyc$hn*KQkIgOLI&x_c<)F2nE+6)d!@!RPCCU3&hc>p$EzUd)We zFEU-r!?wSWu&a{Vk-__qbKP-RJ|mvk>JTnlL@1>Gd@*o z%zTNqK^6HNip?43=q{vj;_rjAoK}1`44h-$s_0%L4=gwGD3_cEK-xx9!C5pZM8xT? z(S+!bf)?#s=@S8}p}wD5zyyM!@x4HAMKwABm@5mXUhZ|C%M1#!*|%l#*f!RISR34D zJ(s?fYyNHdeOd#Jje5ln-8n0Cg0{gdHvRB^M`eQm%f8s2wz*#E z_V6LEA;8OCzUOlacbI)G9awkAcKa;&+X1@aZKc!?1^U1IBa)xzJjGu~QNSr0Zy7Id zMb;e0_sPpnU2&qzAB3HJDdqHT5uQRf6fvKcwa)8fyd7p+*eqOG_pebV!yf0xlkG>3wQ2?X^JF>L%k<1Lx4Y;N>l`J_Rxazb z&U8xU3yXp=5?SHX&s_%fS5SJRS{pC_Itj@6$v`Jk`h)xSb`5MP`vfrA#9o)nG0e+8 zvQMUxFGV!j-uV}rhObBnV<_HSfNOmQz?W-+{EA6H}r|?hTq~T5?$K4Mw z{b}s>F?l_^2z*?q8B@YCppN2Qw>VDV$rAhe7F@w1P*6EquOgrNKl8ZemLLBm#>}w5 zUwNtJ`rZ5_%TM`P#tWenM*D;MN*y?}fRImA+0UHjODjM5>Gx`CS&aeNaR!n?JQu6^ zYr?GX{|}u1*Pp1s_!a&F(qkVzy6})1O~DG4%x#lxHMBD(Z_H44&gKfU*xn5WtwYb4 z7d^7>fa2)(1MGY_{QWcrysYfBA#Buqf5SFO+l$nWKCRW6cgQo-D( z@KvfKJp8PIKf|Q@ih%#K%h*R7Nxd@bq5<#4N=`kCcP~yI`UjUM`;<>WpVeqkO6<-Q z*;o6AN*3!8{^ojAHuAb&`$O^20#E%%fFCl>$8mytMKUr3lfl`WHaxp`Rv91W1mA4N zPu~=C$_lVjwRov{$2oEj-(OfB1Q3TQG^()rQlH5u#C(5*g6sjgO*ts&hYZSKD%rz( z|4Pi-*aP`+9}%fC^(`9Ol0Wl1{k-2XUJ8Nx!c(P;0+2g0CR0-oQ;xX& zafb|`*x4c>7h!Dj;hz{T0VB!=1e@*>b0U)8R@8Fxj%wnli-dg2P*G#e@L{<-?n zBdZ*c-_md6%1)_3F>Q|5@n7=@=&PlRDrx{SUie`J9as_k8JG2B!)O^mkPks+))FOS z!wBp$rZJ+RlbY12*nUu4z@Gl1CSgea6jH-j!8T4KyB2R%hLU!;oAM$_?tZaU{!3rv}LJi55xb+xRtP zb;L?6Fl3`RPFilD2%229rUT^mv@pXS#5lPzv*xczn7u3w$j|0)WPq_fT|84NfHQv< zL-P_qndvH1hBCv)DADJI6jjXVn->8ZHF$lkXE99Bz?C&%DB{!xmr3lb?#Tw3E?rK? zkYeziE5>TK3t7g?M6N;c_%@UoNm#(1(Mc(YE)JjXNl*fAUy#)fxPTc9y&kx*tdN3F zjxIk*fQq|;sc;WTQf4|n*;PoPs5$QN708Cq*gdLylXpBW2_QFkA=uBKdI#;Ak0sbg zdQ?_a?RW<1CHytjw4totY)~?e#u*yHLKJd&^H|L(Y>t4odyGEmt89GkT^z^UzOIpL zCPz)|K61bE!LZA2tLmVf``WWB|n9=L8`h%Dn#x#A~4rjV|WH+WwItJxDOb&?A5 zE`Hj0B)Uoa0E_gjC5%#-lOO3TrM5sHb6ws%kTa|FvWTUU&Io;cKdyDE_b8Ttap+q# z$9=Bek*qh@rcNQru@;qski3)2Mwz%6QAB1|w#I{drK+dbRwa)Y^+o_!c zNUbi4-c}Uw$od)l2kebzA{JQM+#QPUX_q%&u078Sk4jnzU z>3NhX_md_foPAeWNa&R!F!~_?Yu;=hS~5s|`YVn8t01;Gk&LY91YTjiK1^Tl`Ae17 zDd37YQKX&+2GB)@DV`X8?ic_TQzz)~djqg@eU6nivFGd=giSa9J*EEp9|%!fF~Vkf zD(haQ*;C_g(WZ-K@8QU8xsl>BCT|pwI%Fz420lEry@z?2CAK~PI>LFq#A&_cBcfuLM*ry^&k0NVkq(+5HgtSg}bi;a2 zLKoA9IF`J=3`!5Yl?3TxAPJ^7WeqYp=i#5Xs7rG8YZ z_7YEId&cW~=V>PZV3_^1O#9Ytobh6CXZb;vy6--MXZ>$(O&w(LH#gO{E__A&wjJk; zoJwE{lkuWl`cbjk=q1f>{gwYfKZ+VI@>b`)cKI~&lbnt;6;h;r-8@h~Lrex%`aBL| z3nhb5h5*%2c1^yrz(>sgfxPtNmRnAwcPb=OlK14@Wqa?3Cc4vYX zDrlD>@&diK6dk@b`W>Afp^4nc5z@uRIuzbG3akrQGBJB{Qj%RAs^>~!)7`{vwo@4I{%U7YmZGA_S*r0hGQTC z^BAV7d};+)*7~^15e*JJncv$NfRUV%F5VX_6y#0?o|M!=^oR=k5SCw)-A%&G!|P6X zG)muOt^D)8gwczk%|cTA>kN7>W8L@qCXy-`4nWY6$<1>FGms!uP5V)wu%onQT_eT_ zAddbw0y}f^sj`j$NS?wTr;k*s4n+Hn`BQKa6gKxdN2BORLm0}BH zzo_!2WTI8ZCSdGMx-1BGnu0U<+hnL+Je|yxPM1C47T=MTtVd`fDZ^H1Uip4Zf*@F! znqm}^oBFCiAAXKo`IKl7oV~_0#?YoyY-);#O&=cAN>&6d( zj6Ehg!N`i+oUIsg+ny&*n@-GZ>)KWYR>q_<;rJJ$!D5|%3A>Oy{CB~I8E6o`tIhi7 zEBxnx1vNpA?p@)$K&djrCZ0ks6ANE4o<^LW4OnXsxPkEQdI^)5L83@MCZctzEM*Jv z)41ycD3B~Qi~rmo?S$n=b1U7&WRXCj{+>ttaKq1Pl%DWekNvQk)E;vb2pTxxxL~hw z+kF73rg8F9$1rx>C=B>45QE`HK8;YY!+h=C^|fEdPC6(1upjQ=&hx5p&{j$ReRh2^ zsR(rD0G}zT&07YWH|ZBd&6(@Rl&os%Y;j=V%Npy7ILx*-qHyl)}Mwk zR3rV@lAeEZs@S0rHyyJEhjTv1<}QS4>o537P4Y=}QhlwKiI%W-mHLy<`&TVg#^#jq zgFb_gW++>&-q5hrLvWqc`lylS)fb`}c>~hDXRCXkbj21u?&1mM zV~6kB1@rb4SLu&jVdt50A9>tfT$dAyxZrK~-`IP>%*W|>LUtrIWup#d&=$NZmg2hy zbYWXhWz34NkrpG`wdw{<4zn-6caKIA+LIvo2fvj@DmMk^W_d;OLd$&0sy-*bGGRYg zr*jF(OuOYse8yWhkj>-i9|GUq)@Zz10<#~Q7n%%Z>K$YJyk?5nMmZuattg%;-}Niu z5jGDUf(&Ys&%_w~0J99r7`^6swz5J?YF=k2($G>qkXSLwotY)>yb?rgv%%gcKuxlV zDKJ(Av2{1r%@!ZKMiDMZl`*Ib3Qg_+7MQi z9wFQMnYc_B_$7+%%Hg7^Vc}XYci%7F_ViM#KylCc_!tZun1q}B4DMU}#`V4dy6*bMK9l(2wPJ zrI^)jLG3Jj#VyuT*aN|NqR+S1SEHOBGeN=X@m0T(-Es75+yx`MmW{-M3_6WHqs-1q zBsV$#FCh4~`ZOtwnHA&x6w({kRt;-0v{Nt9qoZ!wicaL9em;PHn|LMW-SztLL7SHo z-P9Os7%82?is|X&y^U&>pu<-dpB&uBULH*LKj45)Fp9-qFIABXmi=;3{kBOy-X-L) z6`+uO7D88gM*o*A%T`^f6kU){(>E9N9g}@L%1HeON2VR1u+(UHkXND*ot;zP~gqWI3|`gmP{j7!%u_e+p7Uj6d4 zeqF7K3gd(D)24eJoozEM%rv@E|M8fSt`NG(?&Zk@qCDS+;ZV;qZcr1pQ|GYaOgF^# zQjhN;^)|phlz{Eh6mr9yUD_;O;P)Kg0yFq|XFZh-e2Cbq65{0N-a{K+0^0n^G3_*R z4v5m9kzBzIWLb0V&#q7g`NKInN6Af;@r-?r;0-HfvJ8Nwik;Bq0FgUM6=!LrN$^6h zY42{SfhY z*;`nZewMWO#`k=zK+!^{Jt;kW+r$-fJP;_Z9;5zNukDqPy)&9-_T^kc{g{<|mt*AR zKV`E*@xGZ2%11wF7;o%HYHC9DEr9p zCttd@z9Pn-25`XGT^2WXPDoy=AI@`KB`K7J-A^epYce2Yo+S=~18`BD4HgF$;ulVu z0Ux|VhW5p{X|~_L@$`6UnEqzpL(*=MUhAp-Pt4{-8!P8N@ZiBOfv!SoAP}3#Cc*NJ zNY+tUX$1|2{4)EN%yS69hJ|}mHYc(|ga+r39<*U#{F~`whe16VfW5mmC>DFdL>Q$$ z4uNm+XX>LCCK{ehL4meI$}AsNRF`Mc-i?_e7&wHxjsS%^k`lI^j@uibC~+pwx_-UX zixhsFoRmr^lu-5VnSJQ?wi$QKRRt1kWs<%dKhfgfBa}SC6aL#+uD2v}FnlKC2`&CL zYn&>HA`wxVe@^=12{eYz^W8%@WP6$EqC}X$-c{ou8cJPyuFbHb#kJ~8{nPRxo1LMh>R!rAB_Wh~20Uy{!aap2p>O;ugv5F0PMZO)#L2~pz_Sv()AdGHzMFZ&-T+w7_ccZi+#oCiTL zZs1sq>#Jb$S3(O)`K1^#9P1R8V`PY3SBRe`7{r;^D!lf=QC%5kXq2kqYYF?t&3ra3lr8gROSz2)MeazfenOL(+!rGkIhp1ww*eTXKYK7^+Q>9 zm6(F3R`It~j%n%MwbdqFbqS{^E3doFC-reNC||A?W`R(BS2dwlqOwVmiAq9{ zl{n<}+d9F)h%heb*=C!GS3u|C@=Uj-@VK%Jo~kmjaucEOl#eH=_{*3aUuODn@q-o8 zFzBrIz53%UnQ)UEUt$tsvFZm?$AkTTZhVre7AwG%%XDOXFbYp^wdLyr-yQoZZ&fUu z;@g`p>v8il{YbZKURZbAc%`9S*{zd)iGJtY;=Uaf1ZIe@_7a zo^<)KbdA6t;^R8KTkJlBq&le!jsZm(?kiSWF_^=VhpC@6 z#iRnAGw2NsyPfG|gPDWYF0mwg^Cj*>;Ga>ZnmdlunAHU5Q>J%aV64QboikW2z>oqd zmO3HM_^@a%Rrl#%U0bp2x!*Vg?w^=Hp7klHE~RWahJYP`B(WX!nbu%ZC9)lXa*oY| zp=#Y?DEr6NB@yWt*5Ir(#0oaMQsmI2;x!s4u?rVnC-xi{J6mVVuQaB>AdskZc3$pq&(#p%i_Rg=w~1P98&~}e zkF*}~A-3Q(x&t<m8I!`26jo`5vsE)io$SuI;4L`$hp=t zBewMD!Avr1a29a8m-D{sH0In8AHR?q`y8ly)8Ycg+f#QSJz|X@DOq+U0Xm&pB30 z=HKLy7(0yHAw6hrP;vKK0mWn*SNfvwxW|41IbeJ)iwe$(w=QR8C1Ac}+iIUeg|ha%1SVa#Q^-oJ4t zR%xgoZa<)Z;N{*3Vh3ftx0Q#!ih;>Fr?ZBCGKqdeol#f(NN&u^4Gr+F*|b)peHeJ6 zcJckhd#u>Ijn1px>Q`qutUGOTzY01B1OlI>&lWvlSi@gOsF}nQr4Tx9xWjE7vJ9C3 zgFtQnMkAJH_!^&-_yY*XU_KX&e#?Vd#p$tcQhNpO%Mml^JZA~#fnNH&-OrUrgX8q> z-OEpSC!<&|#*MwAndWsi>Yjz&5$p3aZxMWwq&POA8kGzblr5rNxz`HlRDK(TDz9G4 zE_8oz>kVIXswnH~$B$-giSW(G^B}}u2BhHS_olAAiBOTk%+Qag@|K2B{daNWk2n;}z-=+Ke% zkJ2ihyc5v1owRA0-%s=0^9eJ4Rx`ZpDs)ALIf}4T$E)8=Y;`iz^7}f%l&z^-Q>OR& zzMkI5*M;TEmsg#(3(P#C^8rHxZfgiLplf{ge6Qd)az~*B0HM8wgt`QdD$l-q-n}tB z9{<>P_0Qsrxb%xuEG65BB{O|Q)o6s5IebGsgZgCK)KX29h`p#&eictL&XH?6*#JG0 z`Ysi)ZQO*@RO|Ogda(Pa8aT)Hom4GRQvXmSNT#)^Nes%EcpHU$e)&7zS@crZJZK)Z zJvEc|u5i8JIZgydd8sXk`vo;>@iMY%5v*~GpLyW`&4nYY-O}Dfl^5|;5s=902A9ew z8hqT)2Jb0reMM1O*3J*FldfaJjajHz_+sRpux%PFnk~x(`+2dmIP7U)y?Q0G`T^v! zS7uVpKSi-V0Q)YTk8966bAdS!l(6B)wk&0ubS<#(6_3)?N|cms(qw95*U+!ecS)Ht z7xHM=>r*>9a-Y{)5Tnb#T6HJiAP^|ohjy(N^z<@Klevk=(hye%XrXaSt1k8aaFQ;@A+T^|c>I-x{ z=J-Roy^8t-t{=Q3OQT2lod-(E!QtHS(~KZYaE%lRM3Te?b3*{8&Q4!dxkW0yh1`(| zhsy<@O0jUc&DUw(bLDL%!$E`gbo@S++6Qjtd36%o?slk5g-=)Uy75fNEey=tmAfazG%?O8r%8)_2K;45dbh`{CR1xXE6+62c{dVdJ|GXbcPH0&8p48_8AzNZA4im+kE~4U$J`${%5*{8>VwYjG`8X_! z-jrH%tooAot-S1}j3PQ+4>tH*0K5KPgVcq)z;Ke81%!>Jsnr%NsT>h>j$&Tp-Ew3a zR~j%WUkyCx53Yf_O+sfJoOxnD@ZnDC;&vq&8=;Lu#Ot62SaDC+EI81tg3dHaCZv?eak-u9*0iI}EtO_2mDa+Q?@uSg z=~?;m19RzaF5g6pg?Gf+sM9@Hv}9T64Pz)B_4qga!Xv8FQcv@WB0zu8KLzZ30l9M_ zHPr`TU`r>DiL~=-%QrX^->V98c=AJ?iR8AHxVlPeIAAQEAlGep8Gis0N6d4PvY zk;0n2lGN%STKoU;;B-+&$|{}!xYsN0S^QARbR+D16y=X*s|*RY{O@CI`VWBgP6u1Y`A_$+9_ z8mYAo6pRfcXHMC7Jf9NLyYQi)KATgGNML$6N7KN$$+LcA!ex8 zDG!@3q9=Uj2@g{FOC{%l(-qqmY?X}YARxo2h*1RsmmN(Tygsk*!=SyL$%n_%2Y8EVln4xh}l0tX!v(VDWB zS&|BJCsiWxLn4JxJqZ$Gq28wt4+ zH^*gF?quhOSa7Coa{QG`b*QZ2Z8JTWww%~-$GszsHzl9atyWD_3%(`k`Lt+EaPKQF$l2B405%@$|e0e|McK z;VZLq>zD6K1cQEr&Mn@^oBBiZS?VX^=JyZm=E$1vxHEyt4TqE6N8A^(?~MsnkHfm| zr8I^N`ImG|h0peQba&t?jTFVjspL2W_xT+KR=Gp*YnB~iMBHY1 z=$@%#PnIfK^w?61#^iI`bR2*k=n~QDH!|V`1+hyQQ^yj<)Zmm=Lp0bI+`Z-=R6joy z<~~T@)Y7zjm@3w0#LEMc*{rG#$nFG^O3tU&mzwi$`)8_j0`8naq%L%Bbp66jXEHMx zT0;^6&;x}YoNU{GBnw$wV`BoGqyj?XWFf9W4`s7v8Nx`R=o&ISmP9HFqbIPHPWvdNUU6( z7X&6Rjw}8rNf+S3qMTr)JO)I)5~$8Z`YUi+~3wZv2?`ehY##c8HB?q={qZzY~o zBPA2E$}x{tvxByezFIxuPLivNJBq)ixMBjP&Z;naib%~Jq@daa`Kw#0?i89v2(lR* z2_I=*2!qKkfmne~m-Pofva$dmcg``9*8`PbrS{y_`*l}KK%yq!p+VW`#Ikw7Jm~?tObrivxeGXv9|>9iC|Gwg8q-9x+XcKk z0HggtfrYdnq<|t{S!hM|bH6g$qQ-#CPeuo?@%j~IfN_sce(F^-5xr~XjB`!gn+NbQxk!uStQJ`YiMl)Sq+(fAtDoy*WF`4 z0_4xp%IYxwjV@%82err?v~o)#Rn2vG_tQiXSy3O-%O$X0r6rixV38z--h1lz2ePMc zii>o7uT9;65jYX!qr{0S%VygNo`(kgnjQ6HMicNF70{y6; zX~X7pjGv_SAmKQ3s0u`Oz_e2O2kE{@nMelN-8CZLU(V%K+!Bq{EK}u)zcjQe>~xox|bk@1iXdV230RCmeu*Xb%kR*YitXp*VvqXm1AxLRnHIRuJY$n$LAI#5Sv zRS9y@{7*Hyx^wqKMNl~@Ex_Xd4u6C;btfoNZyU!@f;~Y*QososM3js%oTX7$DATCI zgh;**+=ATDS28QnWMktN3Rv!#Nv^bPG$ryic!rSiUCv;vf);m1Hxf;4p1Bd3D)eLh zU4(u_9o4KDx_RaKm!~SgV)XRT3@}BWfP|+*M5FSy^tqx{rW_pYOO5htMCA=gr9Bx1 z=s?Eweqcn0s!0p(Xu}}76}D>8*Mw;v)Ziwu&Z`q%Yg+Gd-h@}KXBmj&vUjzYV8a6k z!gqoen0!m1funloneEmiPj5FuXT(d(;w3i8GL3WDK|BRkAsM$+w|x$|GsIbP+*nR4 zZ+!QlUSiY{TW=3NEi&gc{%H)m*#`y%m5NaZuc1a|y`Xd;GBEp}9YtA^3lbx8q<({z zy%aYTO7q;^2@~>D3qY6q6-D)Ky?$}suCfHg(H3>*IDN|0M0vlE7I#+X?(>4dB9Z)5 z$}gd`mDd;(;wD_A_G!DRrXDgPK=$TZwzWHEon$JnsG{h%96*& z)|f8B>GCC3PsVvVNB<;#A+un(j*4n*6?9g?|Di_YDON&r=}grfo~yJ^|ESEeRdx_S6jh%eTseBuS8jJ;^85%*GfWkXuo8h3qRnK2(k0 z-$Shq1Gp58*4{m*D0LU!{e%wN=5qArOl^jeD`MqvRP{z;rgq?Us9$p)%MqTYL{|9p5yx{ve@E>Si+2XWL9(nuSn;RXOQIo6D40g4tg^U@`J~w54 zFeCRPk-O3B^~>c#6|ge>_=A4~QHRdIc^-YJV)%C)EASua8!~Rj2gwV4-Dt%FyN%l8 z){eynWJTjzFg+=uvywUfZMW6vCaMIiZd;dmbxF+(kJKo1c3MAuEpiZ$9KCioXA_Gv z#(z3i zErF(hS;g!1f%Lok3>WP?Ozq8`VzMzdMB;xSyEiizQoq@E^7aPG2CLj{uL-z#`7_Z+ zoHk6prN837us)FOe|wM>x@-FBwRHL1f@HdUl_cu93Cmp`K9M>0+q`e>L+1l+pZAIo zkC{EGWqTP96$t-<=EgMf6T8MObcCy~{wwK)1Cb@GKW{})|H(aph`fGOse-E4Wn&k7 zH_zj^`gePhBnIT8NX;Tbpt%@ddwj(tVbt3`CC7KbtsPD4p)>LBEBx(;Ui{rc!3EVe z9WdherTp~e%gW^)*4{tgtE#@*dRpO&0(fT6&$D0h=9%-fv~XzhWZNjiudYXT{ zHU3!lH1U3L-etj~tKdJae+42QMe0BN{FvuJx_>v({qDh87PDJdydP{Qu{&%u?b@0nPZM+OO1^+ zu3XtHIxhu5D|cjA{p%(0%ege$*?fgBgb5J4q*7CM(06Q`Bm78_-Om$eSs=Q#wirsr zzT=4V`2$mYrFOG{S%!CdG3uD^WOCzS?mNa=b~+09{eY~qjqLxT0DX`*W?s_rK-JH#`c6Mb1FuLM%6&Su_}fqMqINh z_<9dIeB!~DD!}QEU(SArA{*zKUQa8Mb1DOg8IX+Ipr@X^TK)F3SH!*COyeB`p#LFls1?x{Jc?aBDWzko7K)T%#fh0Xk6D%=B=h2e-;`C{ zn^dNgX66j^lW%iS1chTgDm&K?!f7&SGmyHD>tDLfp@rEC*sdBS)NH-LlP&@oS5S>s z7ax-3d$vzr-R%JHaEaSX{x#lIb7)r`0%$k{2&gSE0zb)3r{|9W~NPIBEjOH{&e zfP>m~8=EWe=NmF+kV1D-O0d)=T`Gus5Gi}Cr!gpjEiR`f4C-l)>hx|DQnS)x=#qiM zhB%eWrh0od-a?FhYc(0FD1_!cTR_L8L3lRzW5SP(DL#CFaJ;O@CD6#;?D<-&3h5hX zmobTX(i40*J6mRBKn*+whzZ@Iu*8rM5qp~R1bx~tSYZ&iR2@?&fxWtvMscmiK6R>t z92WwUxSU}|PHNmFGWz!OmFwE3-hx8`%cdqP$+~S%rjHo3Gm?iXBva9GwDgZr&CdKEn`vRFp!Cp zF_#thd5Rhg9kuPu)2^vWOTLq)Fp|EHO{)BOc}Lw|siF3rQK&boe)O??$W24@fgbNv zVK%LA3=$guYTjP+LPF2J&dihB#_^kgmeiU1Lm%l%qf7mpvze<~B%GektzG7wcKW2* zAIE-u18heEObl)514K%vrA0P#ch4H=Dg^f+d2^z~sKH+4d1$FRC?dWL^;aNSCI<#2 zfgGXRKW*P+Jk0Z{xz9ID(zGKpD-UcD;!6MMHhwl7FtJPKUe$#QHGSo}bhXXOicafb zPMy$(Cq8nuPq^y?xm4FW$#4G(iBDpn=^6qT)*YB4+pdSCz@PY= zPF7LH8d|iIz`{n=AK#ibK-0VpE{ez?&HCssp&xhs2a^+xzh=W@n% zQ-h(UL(~}GvBZK6WKuJoE|rMes+I_cVuo~KGZXh_5Mk(t=ffwPenc+%!TM5{Cp`A_ zR_&Q|1rr#_JvO>xn?Z+U5lZ;4*}_nB#kaE!t#xEt38U3s^ZC8cUQ)^Rj=oQWYG1Te z)*Hbk?gHDLN0xs_@*X9ZU6hsY%JcW!VwH|Pk^K{_UGdFqXAF5(SP+)VAd~;vJdzWw zehcQCFIb;gFh6V2(s}VBLfWD1DeuC!w~3O=bdfj_X>GS#_adBnT4sqTYvjhq;KQqN z3EcWx47Ea`)Yjhzhjc}&k#6RPlnwspdG%eD+Ch~zU6*)EYN2Qen`c?KIQO*MQEbF= z8S7P{@9JE%97K|*ZYeo#hN?e=?jejyz6U6aJsm*j?aJMEbbFiFzAnICBg1AW)L7xT z#*I`*pN!L6^m%Ya#FYAypK;`Feu|jOJp0BMHKu+L~z=*8Fd5q?aD{tL}t-`^@l33@hO2%gzou&oG&zrg&J?S6ac z+QZ~aZ5Bpuw$)$x-sz-a^iPy=P`Cit&rHs|$f_ifMgHes@K#tTJYeR#=#SMt#2^_a z%Cz+6JwMG&ruIXS0Twmipv2(xA7-;y^=+WSs){i%L z)(WA&rgPG<(X7o)$HRY3;fi`xM5q~!W~`NL0C%yoKxWM1Xbf`aT8#qiyT&!*{bzF? zcNGMFajdrH-yT_r@smw;HgH~#9dGlxwQyIHM}Tef=ZtdlhvN`Em9cAN0H&-j z^v!J43u-&Z+~Dl}um+uHa`>0T!g}NP&sJ?^g2?t`V|U{XS*+Z@Oup8CCAB~zoRA#6 zd}l_#$`}`w`{hb1T0v3cIFr1#kZ}?rm0R2Y>e{q{Hk%twq4Oq~x#6HGvrdW%{Pp;K z^7i;e>Ry%XCsB-#$xm7cPvaO^S%8)sN=xeHsL?qnds@ABi&E@j_WBrB%~{go?4%OU z-hUm7+_f!QSuGBI7Z=7-{z}D93@=0q#M!1CLYrh2b>6ST#?<^}rUM}4hqDz-hzHtf zYD%$xz>>uBd2H-yB@5k=xU5;G z;U{daplj2sJP| zwZ*@rPo`gh5Nmm#;us2>#{2|$N&6YN1L*B??IYfk8L6`P5;M2w>+8BJX%)vpB{z67 zCVF5LYwnjZXH{1vSasx~S%M{TWVS*Ik&dxH(DxPOU2q+F=qBWZ7}WQNSNVJj62x(Y z)x0A`gHU=MPoA!0t`jTDZaX!}dBXYdVH|B0hb}N{zN%W3}1$$97zESGJti2rh8XQd)Fn4Pfw^YivIQ9cF~qep&JVat)98-W*{5 z0As8!E2(}BgVje@8-yjN>jYRgzta=%g{5by9El=#nC4yEyQS((Bt6k90mk0LS-_^o zn(2-u(CM6YZ^4o5Sqs=gCzC1=!rier=c*k6Gf?Na)|5L<7WEptm^Rdwl0?eW=jKSD zu|jK@#n()c4E8HDexuK_*nnjluPK86OF=AJ*p}@CT+@kr#it|a` zeIYjpP|+_FWiP=;qBL96Fuqv-@TmO3`H|3I1Yzamdk-}iaxiq)()`xq4HEcfok(cA z0&qbk$T{ECW(|m)#J2erf^hh`QJ9+M_BhjI7qtHDPS!oHwaRg310wD>z}<>jfueQ| z!ot&8&PnqpuSq)gHm}8|D_=Lxy2~89L-I3kt2BA{(&Rp+VJ!L6iaN=8SN}LS-VfPv zh`Ti}lA->QyJ%~F`7NGE8Pi8X@p15Rsb0j3VM?7QH~L!N27)@lgFv@r;g}`1QsM3c- zgZ+DgH&a|Ytz&xsIqA^;1Ulu&@pfuvvVXZ?_~rVaXqkHVmz^zK{g{Gvk3=x!O4sxC zL&_lna;HZf=ag4W`yc53cXG_}4BiRy3w!D|{9%v{Q6*PQDT)r1+$RjX59&K9VZyP3 z+OG6^OH`F#;A=oNTg_Y|OU+n)ie$V^YIQGttd$)>PUEFUi0E&ynSS|Fmrgy39$hCS zcLOvi=w{!yds>-K4aWSQ9`t^xDnU{#hl#rTN>Y&@elqIWs@r*$?@2wm0Xkip=`eN` zwjhedFJpmKi$KPX+*Pcn2RI_desI8%HZ7YejM*y{-<(aQ$pqv^9ZiwjeUzetf^_0l#54piH)KU?Jdl!>u0%`zR-YivP*!rcv+HJ_VZ&FP&baQIv zRh3}DSv4@hkZGo*`P`#?jR&ax_;wLl)~94aTv%|KiRm2kOT^k+tCZ~Q%u!os#1Olx z+UNBc^x1%CgM%$IO*(N_>L0b8d!oM8f-~LfNflY}|W%om-Yt5=>2j+MOT6$hnp!BJ)Ip%G}f zQCbVk-GYnch`N+>77T-3*Y)w3z;}VqxgSoj_ zFaMbPZ8jX_S#WxsLq^eGd}Zp%m{}X!W+RRF^LFw+UrKxrP3P`Aq%=0CwpDO5wEXg= zepISDE7+qhPv>vrTVI7(GtPGKkJG}?Y{EG@a;)Rqg8J-hzTgQLw>U3lI}m}xCd`@s zCqFs1?dsLCl_qVoDooBuZCzX=(b6Ia-~~q=M*|K(Phpw&UYsrG*dXgJk)S0zmMHc= zl|P@*-$^*@OLKM`HY0=FOGm&Jq@(-v0TEZP!pic%b%jAZ`KJQii|94od(NGy6ufLc zrq0nwrN0c{R_%M=c>9UUzha54ctlyG`JfP2)#C*Vd7V9r_J6C}h5x{bIlzZK9fD&+ zBLXz1a~zDV)ueKKs0XADR=G-i0`oAY8v@ox22Wp(lNM!46ey!-Foz79gLIy)uNgn> zZx2K`mkf_@v6><>U*27-BIB60;di~)4Ry+!2t3`!Pn&*j`JrFt`nkz;ZSV)_$hK*F z4HSOa8(<}i>Upd(Wh%g|jq2MMGxSm0bF4DAd1iyXnsPC0T9~g(T1e!E?06#X?#CaL zSi9e)T-!h1f(Z|!GEdK{n$?t8HZyhjC2(%Y>#`k<%y?005-d90SedaWhBEYS?GgOT zuL)nW%06~Vk0l;K!CV$p-#TmUVU{_VXZn)iW@jKWnfV$Qjpj?)W6= zL?ayywm&sLCtz=ZNqtb=5iv^XWBD?HapuKHUIz@C5#H3tY=bRKuOsiVIC`A&%b`Fk z*)T=_l>^+dznO#nn6hh^Ei=r6Vuo$Ehq++pOYcvvK6p2k`_JWxi0ppF8`QUH(msNo55B}3Ij*aVF&VW#% zp%x7bpuPFa$biDljtW@~dJpv%+co2bfG|g4)wP7^M9Ck0J1HWDt}*G}te0}b!_30Ag#B^xFA^2 zH{ge;{!Dc@M{)b z^WHAQuRN(|gZ1m&q^5TJhsUR?E4Vnvo)xRaDXo^0bL769Q|>4Ko0H3pO)4p*&RD1S zR2hWOsT=C5eBhYA!vaBWk<^PzY#Wl`bLHWEC*{>E1$ti!NkWJER*pR8SjJJ>|0u}+ z?-x7F+~#`fyRks&i`3YCqcFjvScY`xAhULD0}P2HXi%j$=Avn zl1|*BN7=E`QE@jlXdXPhd?w(-otO$-_34`Wgxv`T=|dTRA=9K9iR)k zZsD>+?_?0zV1GzO#kDf20gg9r-rV^ou{P=`(k%x{+Sl*tjP&fz7jd1)nK9patXw=q zCqcZMg&S6X+xsA4$Q>Yv^VG+Qd<5P<_ z3?8r$9Sr8T2|LWHo4-C_`Lwtcwfq*1E!TbtG7=pkZfL7IQ=nnyP`8QV!hh-cgg|Ga zRFawkEQpEWl}@q;D0}{u8H{pJ59uKl=*1Gi2uFsS1la>aFt?1}tj(0mxEss^?RHcn zqxc#h2H;2sgRq`7w_m^hM>&rHX(0Zcd#yTP6w`?xjQvy9#wD9H5r*6nxf*^Sn2hWR zF5`-iR|0U>bxPz98#gm?hj}h?oBP>=p*)j<0{dj2v8YRv1hT$LHF1f_UVT$=pcgoh zX~tmr(_|GjS+>cZ;}CETCu*1#KAO~$5Rm#>NzEPuv2d;Wb)(r` z;tz?F)AUY{j4y)CEv6pRz7;V?5Y8F~9@AeWn;eW)_*8F8>-z+GUs71?x$M^h+=t?~ zlz*i?@Ph5ymRCK(jv)C-`y;oI-x}fRkF& z1Xk^xhHGkG6PK;xYHkl>_Zoj>s{KUTx)Wjds;2++C)3h}jNeTAJN3>Up-JD%;B%Fq z+mFKIKDw`Z?3}89TocXpYhFs&?{O-B;oO|v$_x_QbJT6Ef1B!Q;~irftV4bD3r*0Gf5um@Nm7j5730*q5weF5 zd-?Y)yo>Dy^ew7c8~|ENSq;_hAYtqzVX0mIb-DT|q1k@!Tcf>pNwvpSdQSS8rqB+k zO2d?ED#7?>v&xaa4FOb9#dsF0NPmo|)@Q%QgQrvq{~$RJ5-e{BdrMVXy2b4jw~uCg zice}Iq|oiMA_`}q-RqPjQp8Ak>TF16`Dhnb#3tGPTqrsPG@9gIl5D8q%@@TZj5~|r z3r`GScqQa=o+6mVTqD1^8xiQy# z4KKi>tpC_z6EfaZMOg*d>(df_Tzvq1O=8oLbW-dV#>6tF&)F?rbIS;llZd0|SwNczg;BuuR!nHpJGDs1ZA{E*DLyfA#B} zA&q|HYE+EIZA1gw^Z3TlzLf1YF7@om1|27Si30MIiG8s!pAD-OYDo$I)Q)A?t_#0Hd0KeZg^f9`eaUaaNZWH zHj+c4$=Q0NIhja2DR|^;ml!1rAAHPE#g^~yf7#3*pr2G*v`$NIX z$ZMOIdIbu`L(XFhMdC7HlN01u;WX?0;ch-*RwsbHe)+<$-pf`K*D)Qmr*8{&g)ZHL z5G7W)J*hT80CBk>{@EsSJ!d3NMrzyiruvP*FhdkFcvwltwi(tX`{_5kt(pQ)MPRy2 zU%zFOiI!AP;J)zSlDjsByp{k$@yH|ME~gKK6}>He-Q5{QtZOtCc~wPH_$qN|NBF!u z{i5p)dXctwB^z&jiO54asJQOuHnX{)<2gx3*9^h$ZVeOxWJRm|A+G*J)`iUCa*ZG4f*A zB^+GEso_2G(rCGTt4oQZXSy=n^I0ql)EvrBWrWVa~-++yH=~(RZUHZ)z#@ zno?Sp!M7oc&)+UuhAkxJ&#hhOWA8f?$B8zdtV50ZCBR3A)xT$FqU65C$z*)R z`AHbR_3TFS9QmuZ)<7(Oi9sB>_mmBjjjD;ojFf2!G-n1uM|u60=w@o0zWv`^RTuqN zs`dS?L5>&ho&~gTpNn?Ix9+8`E6P+eo_hX~a`|vQ$#guYF>cw_%kfQUezO<}P@Vtm z(ZWttHvLWApo)o;53Pm~(7587K#|U|$9diYEQ6AfE8l*b=C`hh1Q~6_FMOW)!uWJ?Pqjyt zn|h$!$P(XEIBxO0)t97O8#rb0z0{!PdW@2B6eKKZuKCuB?(@`I$#kdq>jPZKauV2I zg!GUr>2bZSH^#f>!;d3XGS2U}Y0xHfLBe?=%u#)4kyJnc3;^brGr#O5kbTsv(X%Cc z)M>njFD;$3ko>0{HhtlvZdCx5zizb&0zbF4KS30$jjapf|oUjHCmR&X`Z%2 zs8Uz<`Cpm)iCTs|G9Ab=tv8^-7dnwYIwfl_aZ##0ObQ>1djcHd^?3wMG=BU9K9L^> zue2DY6AwFevFQqtQe5+Zj2_Bp%7=_?HpGN8@z}#N?svfX z(Xd`~uJ;G)daA*V%$LH}`c=#+{`$(l--(*@gzPcP47(Yuww5hX$1!H9Ld_JssbFLs zg9m^eKSA;vzQ*g5G@fcg8k89{?C6n52=9IiWIY{QaySPd-M(F2luU@S=>Le#lh)~D zCGf{CtDu7LM%~aA#FDg7;lt~*{?+O`-HHmGYN$Y-jCYAJLx0C-RDpnOHJahMYxli6 z0Af}{9}<92UdYF4b=K}t!jAaMtUMQoFoZUyi_-P5fyEP-JA^tOAtAbh77(bsEDRG$ z^@MB1hO+-Q!t@oqHmVsV+ddu8F3z(hu}^ZBwbae%nB znb~}ix#fd%vBHh2AX|rn2bQ{Y-v>6mfraKTJ?VRaBw2CFB+rd%$e~b}_LmNYKra;4 z$JWEtOWSVo8oN!s#6Mlt4Pa~Pf9L^(Z4DwWU}G>QIuZlE+pJtBBD7fuymr4WbSQ3F zlIXVyuZ(s1XV?jbrl2OI%t#N})GPl4eAMVzXYh7UEPr>WkkjRVg-E$1LOUIN9U(>} zWH16R6CJXSDxY|P!#bxGms{79YQKYKJ)|8`xSejt0K%sEl(f;??GUvBA1VuKtqXe{ z19_^j{86ms4?6eQz+|y*6>}m)EpW};&f9}GRlTiKc6?I)>#Zeudn-ZCS?mekM965{ z2R?w$sKbSFz#*5^HjEz4XyfIt?uC!&o?ch$<=@!A$q})o2j&6I5B0v!o$}-)c-gM` zh|KXvuBuTDjBEW^K=S|@9KSrJZpBl^J|K=-Tn7YfdEs0TNyT~a?7_n3&# zN?bq`aCi}~e6&Ndnk4p*>nMf^KR65ZuXflitjzze^@fiO#+ z=&=dC2=Q6c15LK#rUBIR@+BG2)Igo)5cM*Vbo0W~D+#g?D3bc)N8CfJq*cB~_x9|4 zl%}Bo8;ni(v@Xu7P9IL&nY54-(F&QpCV+W>_HpUROLoDNvJ<_;^HRea7}YYeLq$)H&oXxH@=Ftjnr z>v&lyxwy@#-|*aduYB|0)l>IfrC$SY%k})8P_^4`r!id_R`~*s<3}tlxhKrtO7n!n z-K|g8;4-&#+>k$im+qkph>j9eEDdH%87Kc-C?$fUu}Us(#qtf#>rFt9IT>*W;4iI3(Jww2&?PB=IIKn4&})cjCVU(tmc zemgGSY}qAEE?nz^{~BjWa8Y63t;%0uzoLA9`RWAjbvrpx^L%`M;VnKGyafG-^>v=>jO z288nSiMGD_LMi0&_YLwa`_$6JcV&yE6)K~dFhm7*b$i6(A&AA)o>`15l;7-qV7Iqv z$-ug_Wu!r94qK=%Esx3wymN87qRhD%fLUNqruJMXg~+4`UR;)R;6}HHYwB;i*`Ox= zwtf>|x+|LSs>CnshvwJ*W-TgYYFnE(-9nWGF{fNf%542Zc<=cojJPs1@-kghH_oD8 ziP^MSdol|MTH&!5-EhDV?!g%TV}AzE%H@Fcp(3tiNqs1tS3RD zs3P@EqmMpB(k0)%cSULiZb-3_j32kgBL|y`EFlPLKm&K-X=n6n z0i=x@FpaSf+OAW+JFHy!*fjFaZ+D{$(m(r%i;cW7+ZmiytK7{LD|yI{AQOc!`A+TK%NkbxW*qlzL*%(!9|Wn4!q>CUyv#V!a?% zG9c-hPj>~lVW2G34u&RM*GOkwkE=F=cgy@N{d)Dz5l_ym+6Y@HyraQxYK87u-qPo=`=dOg?IsdD({(P=#79h~7Bc`%4h$xFbK_YR9eq13|i zbY1dWc>~iiO28nxX`9|`zgN}Qw52;KVfGv&s=}rw*3QFPV1c?~Y7_u)ZVzI9)`bP_ zSm?9O`Oi~1t+9P8g3PbGxnnSYU2#tC6t5$ z-fYk5pL~n-T1?7<#iatkzzwUdsda)?-lJTq!GHk-ugr`T1SU;o zj^P>VKFBCCq!@zbqQ2sl~S4f zJ>2ZZe&>MPA@|>yl1o`aJ?!^?lE$TaPX5B$@;EHNE`oX%M|n72QsE1RX78bksup%H z&#yd>y6wW?5RXszIW(>r>vfq88Rey{%k7Q(NvJsf)qGwS=o6#zbCl%sU-Ns1Wr_FH24s=AkwU#Y za&f7bs?;D*_3oQEtU!$0OjF}58&If--h{duSbZ*M=1+f>SSuvpn}yS=IqqCKx38bR z-RiBX_)@I;Aa9dpl87a9t!g|lu!~lV(;4y=Ce7jvmDOyCi)}7N>EwJYIQO%!GP_Rk zt@i%-<_k%eA~fEhH(R;~M{*mLb(Od8zjCJ5xe-pUCe(oduOtz^HxrkjY;vOw#^1+q ziS8~f`Cv)8tLB}909zB&P)IyONJ=FvXkW%4l5Q7q)bH_j6<RWqenyy|SF{AMK>q<3umm!SZ zD}q4G1tH!32rg0?AN}`wQMl>)SO?uAR6vP;A_yA~+a5nGqU#jiXXkf+Sma!W!rhZ2 z^%O@kyq+AbM8+>ICS{o4D@Ak+mX;TdB&0Y7IdXL>2RYx!hdq_1+zT@4yYV@J?63kI z=VF2W2QW4o&m<6&^L=7V>2{y56{3eJUT%xUAuH7i>^f>cUga7qwAQZBvD^-srB?Il zL)VKz-8Qc-N)1++w_@_Pk%@K3&p3{DtlvF5G+wV)UJcpcTpV5}Qq((_F51eFF|?%M z12cXVwSY4X^`U)L=HB&0JvuurF-X#MiU(!o?FM=?_Rfi~qI{3d} zzi_X7hTMybe&+gXgHku4EK@l>H>Hy=*eWX-CDf$kt(jZBj(@0s`9|Cus?gka(pmT;wltjWSi}rEEQsy>?q9kI%&vkaw)BBJg?;b>8k_dgVX_N%GK9? zZjw%eJ<1PzYj%qcuD1K<_gPw6q@1N#XSdz6xo~W?U!Hics-hzCOldL%bh^?FrP~uB zPw-Nr{Q^C!Z>{J`1Ag_2HbOVICCkQr8#0*}SR6F;7je>QuQ6^~n1 z9L}@#JI;PT8@%aT%XYN-QM(Pf{HDdw@*(DR(VK@&+v%@~Bn`E_!@+sChK)jigDm<7RfZ%CG>A+0I@O8UfSVL9 zyJ7Ddx zYC!6gONjh6(2bv%ds_T=IU=$tt{3jBqF_3+lxGH`dOdHRl%jxdi@$->*!&xTN1YcE zl>=_&yD~331t*{X`kIdDS3etrw;c6XtF0h#IaeO-F?jj zf0T5@(4cQHT%HwX2wzcYbF-KE>l*1SG?(!dWn}80z_+VXJW)_$iC9h-`j#$uG=E84 zs@+BrWBa<`3O&%GDgGaP|6>CxP$o-Ht%1aK6npa@tuu{cCrv)j#>nFQgL#Yq1sG> z4hbXm*k{t*);XzH`PKO)%ZT-ea*Aa((}l%w|HsPX6yZv07QFyVuVOs-`?2yI>aH~C z1XSJlQOae~L|jmK8$m8-q5a z!yLAGntfWG`A-#6U?kiTz%0t*s&l@M8H|4VzLZY|Bi*?fUhZeE%l#*$NWRQuN_x5Ohxb$ue4fVgDUJu9 zv|mljUyfYtql1*nZx}y?CAW=zTluBEMr!6UGG&oQ!R1ym*8J}QY_f9(x$C=bgaNjE~#MGQxNgCOHp7o z|6g1C%s_^w6F4;1waW;;aN|v3Lm|a!{?!}NCXlhyREF>WvjYGB^*BLk%(u*2{bKc! zsZ|_qB)6Q0i(14VqLt911#L@6{Gc++-?`A#|6c=tp2pm{w@Ryyy?{G>%GBtfz44-F z1v@%WKJ(m5G|4m=y6gX;y!d*kAZa1T@89i#5|?Xv2k2P*cB(*1kCK0-nHATn`;S6H zlcK`evk&z@V5Hl*rulx`N`9s3-T|ww4Y_o*B|C#U?nP=sR4x;W+7>rf@4*ipGy1P;aRX{(Hw%GK~L{199&nlY)w#l)Ji2G6@-6_=Q_KFge zSwQD&sPSMfhv)Jo*RWig=KiDwzoiPbw4oUH^#9>UaRZs@~ z)opdXXIrY3T3yf}=;0@t4MUO2{+Hs#vWu%h#%?JYfFi|}gvqAj;YUi{tJH{qea<{~ zuVl)0A;uL6@Z>f$y0l1)>q$(8TzM`o2-RFbibGccz_7`)5d$XR4FcVD0FgH`oV~FM z7UVh|AROW+Yo>OO+Bz=&AJW~BJtf-B&A?PXy3;WAdR)$yK4KHMW5B81m*<(UuH7d# zk)nwYA6IG5lCe)Mxip(f5KVl1=*YK_(a(rkU0i~LS)-p1Ww`h!lUgN+sXSogOUFMcz^BPlvy*|e*dBM;lVvLUBsWhzZ$_; z>*%XmT!rs4RxeAy);89W8~a5g+si!*d@b5pbIU+h?%Mav36EW$iJx}5FEB0+`>kOc z#*aN;_gxpGcn2{YR|nIiY3seXyKHAlb_RmoNar(phsz5G8rc?fC1~QA$Lt0J>@Lw^iR`Ws!qiwwD5g4>7a$y>D9cjWxE9_W-T^G@0zOH% z?d8LYdI-&i<<+mFAc(0#R`eLpve!HfZ&)Z&&(-4Z@AC-*yLc2kPpuOJ6=RAsh}ByC zCR!-T|B);sFy1DcfSl%$k+ht17xTmg;nx0#AaD*Vc5; zrrW{2T&YiVTnh(Wa!RUw#q&#d!lnr<8FkD-Y(Y*5X8wUau#uu!+<$V6_Zn!En*odb+W7+F zLXC4piQ0WdDc||n1@4UZ+&Q{YKub&8AOke+*0vOJ5X`xRrsw{xgvKRJms-qgMYtTw zuM<_gFa1T7%AEh1G(n&yP(J$Jc?8La<>^#Ff$n7)66R^uPhA?@c;A6JNkXV&lFIn( zRMoBV_K!iPM@#t;Z{9v$cBcDR^U;Y6*&eh(l=8nX?F1ubOw=h&+j9B>t&WddCNuWg zj&A#R5zo!sN>6u$FY)w0cS^}<}vrH?Y66WnuoKN@&S!@9(MM=>fES!qV zx!^t@En5_gO@pZRO+~6$+BN>Sub4%B{MKhEI-vN52kqFAf*>?vzlz{nT`rScp-kTi zSZXvT_>^Z|dG>kdg^tU;pgmI|HN|;}jc(7c_J}H2?#8frh@UvKIadciy_ijGoEn>( zh5?(oLy+}shFHsWWc zUX*&4o2-xaQKd(uV29u$d`q7%oAhHq95X0Zh`x3jCsb4640>JyNdU_}x7- zhYn6EFF~6benD^WZzwj3fN)g#L{79B&`fzkX`@(1ZlY-) zG)kW`vog@Mf8UR8QNRGEQQM&3CZwaj=yr6zTsZc`!6L>-Kt_R5Q9LnzEl zux4}-(VY57YqeHN?sS}8_RQ7y`vE^DbzX4sNqZZ|>$%TVn(i%UuB!w= znO;{*v9pn{6@9pU`E`H5ovu)hHL|IXr66ozOAJ91b6D=qtI-*Z2i)~5Kma19W{d!P}9M}>6S|@u5Jp#y& z7=&QD8KED?{XGK#UoK@yvngT1<$8ii@KXOP-oQ02=5?wiz?i;Ml7x@Q@HVKBA>l>} z1kYyK7x~Nbs$Tij)$G<5(TJq^@xZ>Qg8*!2d0OwA*RqQnE_2;QhlDpIucP!dEP8av zB7A+bG*Gwq@Y@^4iU~rC$_W>xVRzubI+IdI6Z=a`)YM833^SdY5m_)cDfNNl=FO?C zj7Q05X%sm3sASfEfY4Ge_HRg?eK*I=wzr17$63QxepPf|BW4}`Dcm}hd{WYzpSC69 z2-?1qy=FKg%CBFAs1)ltQ$v0{60UyGeiq;ZcNt0kms+Xz&~oYFE+biXOuN1!sB8jYIMOOk89cb3w9<@G&5VS!QhAIgS=DW7% z%XP&qgbCRZ#k{ZsIq52+gD$RFF8&-BtTOOPYe)*Y40UrO3Zb3pw_NGzq#7*fsv3TM zZ1Og~!e*T_(4ZU@_0c3&sa^5SgWZAZ1-=5(G}ucx<>QHw`*wTy&v#O73y-#(Ti+#p zb)pML11AZ~<)*`}nF(tK`K9~%5h~z~Iao_H-$O(JC!GGGh@x_I9u=Eg@3gm>Fmy?gR=+fd%%UF$kp2T0IVCPTUvHjiC!Bpl z83xW-)b04R_DILbXa1ZIiir!6T%Z2YKXYsLU0WB?F-x1tS$IYGWaF{&`cp1ztr6YL zO-iDki@ux|MGUd+e&bgdoF6~Rv*S1w@avaDK;X7)K1CtuVKH`LVFg8sT( z8A@N;+frlxL9^JPL!-<-kSw8$JI!Epphs)d#$iqEVxJ8Bh_moDblAVl7Hkp&0$u)@ z!=o+iB<+A-~5d}~%RfOf5=G~B;CC$zm)+WIC51fp#&vsjIserQyqM|rx8H(Q7e z4O}zo_T)`imZts}t<-TcjC`eLh26_fxHXmh^MPD%!oOpg83iY}y_$LEH$~?{)MaUe zJ>WPukdr10gFPq?a(B4P8)w?5+k9rIMq30k`6|UEh7@mXlm!Hw2%G$lQm5Uq-31h! ztu()ADKg{;_og1O!)U`V5vx>=EX=RnWb9$pDG!yKqAC!Xl$7`lqb3S2vkPOv$K`f1 z9fRs)#;LN;U#XMjQbXYv6Osd#X`9ttpxY{<%RBtC`Nj97T zS=OOezB5OzPm)UfJ1t2s24JpFm|rCOEt6l7^o>my)oT)^eS-Zf;T7z+IrWE{>BMgq2(o7X5gBVB(fk+&rZ&=h%mBQrbSV) zgOL62dXXPj*t8J}!xiqFkv8541)ZKt<;xsjxk8ngN11vLDL0y64~;!BF=P<`+&hh>&8b$4^nUR3G4FSo$2{CU4~-x zrO}?DK*$+~?s}KBwsKw#zu7W~;d_%3GCwgjs{!+=|B!CGiYaNEi*~$FbtfTp1mdmW zTzN;@nlC&S+8%#utSnhuKTrZ=X)Hrzah;S#flBXtJrCs+GADj<_ufq#@W#huo}*wt zqpPFemfULngC(qm&Z4EyHjxK}-94G}0gjT5uZ_hQwBPgoM&H=kda~~OPl_053fef1 z?zoVYL$|r}D{hNC$z1vS<**Yb>mJ-KeU@r^LJJS~$Wnqz67L_ZG#d6hh8Odma%c5r z*gLJ+SO4HbMJAA!1k$oCVvN6ZLUcaM-4V9=?T(^j=>UW{SIoqp@J65v=o-Q1hO^Uedjo(of{GHuc*ZNg0?{tp0{L4eYXb21AC~eE zgG1GMX}(_dVgN4MkYt}FSCBm9pGc<2Pt4HqnZ!-#Q$;wUnjsz~GvF1v&Z{N}gnrSJFmy zKql+LyMMBO2@LqA6lIytB`OVs9H67QhzWhl&Lb6Pjz8(1Zo@95xBUQEV1sY7m{7VH zx33DMHtWD}q{22#N~Ebx*RnUnjQOdw$_!>@`a}CG8->=r{{XQU>I*Gjm2Tb9L$BUc z^dd8c&xt2sAE*V}(QL}AJE)e zgl{DEAvZo2{MKh~sUvWh$mktQf3s#92qIA-(!yLnn%67_K{Rm?+1G6c`XmJrCI|z6 zi&>0eA8My`y;GF3cC|>U(Ansk_tw_qpl39DO9CwaKo@~~1*YzSCGH{M_q*>i9!Bj? z?0rR3gc54a;tV7g6gm>DsjXA&#D|CdPYmAeTJg%8`NZmB9|gC>s;LWcwb5^LaS z!cQIE^+(BEoT4aN=j~BJVj^{-$m%k9g;Vji1{}n5tDefT{&&K&GM@4q`+=z>mZ$xT ztm0VZ)oVqnydO%*&Z@e`--Hi=(CB9J(H(;iV_lX$qnaS&1319UlKx|Fz7?d5X0(_m z-H|>01=HpD=6QCv^^3TmPxxou-@$n|(1?pNb#{JLo#Crz>#B;`y?uZX6xs2cvjSGw zB6U5VJI>{cEVYNrNwY>>!IrGVxsln(wkB8bB3jBs-UhG2kD>Dirl~(NYs#Cln#*e~ zwrn)f`8)=*yv^#1KZc%eQAzdDh{wr`N!FJi@MzBvOFM7WiN;m`8ZtR3Ps~e*xoI2T z<^!*wdW#s3292$0bFrh!e*K0`L=TnZv+*y=eC7?3fwI|Wt6h8Rcq>tI-b{~y)_mEm z1uQK@n)aOcsQt>!R%et<|1DvjY6Ap3NL{*1#TMzQT6D;B&G3Uv?4%X#~ja&*(KHUi|j{^1a_+i8W{yV0(hGdUv zg@kyXY?f&w3>e@udVQk8+nO#^=oXzgu+1WQ#{lIYF-NXFGy#DZFIA*P59K&4*CjP= zm8yxVAvUH9uE+@cU8>KIrCsI+eoYuD;2`b?Y;|kr5#ARWb=|%GI}8cy!iftn7F)F1 zL7c01M-yaq7UL1{96fViLt>bP!RJ}_LRrS90acw5g0lZg10AR4Rc_}}2%+680)SN^ zQ#~*AgT@vH-C=8lr$h84DY*`zLS)`RNQxKeB{g=w>$)8w2vvh%bWEkBo8yOwWFMr4 zhVX-ZJb9uVrG;ew2vZg3B05h^p!G}hv_XT{Kr07jb7nWua@I6a{#%r>jIdlQ+X#y8 zgw&JVVj%=24h-ji?xyS|=1nP9_~1DPSro9Kg3+1xWqP|GW<{ZHLB@tJD5MMf($+`o zisxnzcI6=FY>}qH_Y>7=H_UakGo{-IhMJAVxD8AH)kjw@!66wXLi53F0hN*uRMMF7 z51isLu{C@~=%6?&W8T@&5UXHkg%;jRO+tsc^K?|DI+1x?TP{;W)HV>}z(Tq>EPkoZ zkKhle6po7aVS+t}ImYmRIP5Gpc0pfr(uz<9yntPuN>m3?{vawtb69g&+bs-1)c)^D z;T9xJ9n|v^x`c)JmhHG9dKW%0yu=p4uEmf5s!`Xlzcm#^ng+bt6it|}elq2q36(&j!Nk|cjO%65MOqK#;?ZFz5Y&V@ahc>qb$La6T)R0v${-Q zeNWf9i@KTMu$Oc?#)qJ$l9bi1b^u=ZnwY+X%(FGrJNny+&-S~#LJ0oj{tP=$}_ z3e}uiw6|^Vtwn)MjP}Tk3Rsk3>B8blE0aFRCR@rrVw@rR)E7$UtDII|d+89&wvg*M zPxL)5a&V50QBsFl_;<_@ylE)vC-^#wbi%IV6Q6 z4lD*gH+{qhpIA14b7kM_$4hX@6F{3ff?QxeOs#dqNI;ly!4hBv6khw z-5Dx3`Vl4)+t`k?X2vmnPp@LW^!)8z&XtIVL%2+byk3UOqIAaoFQ)xt7;f{+KtWt?S)trd$V37l z92qoz$2d!Ah0w3W`u$4O857b6X9`NMY~(noHb81f(~{)+l zbH9V-l6V;A3mQp9uJjSOnKrdAfjg^`I_B^D1>rqDD$!+4fGkL+n@apQ271K(LS>i+x#l(-Dd%5AmQH&Rk!R=mFuUpdyl(RILCck z%5Omzja#xmnI+xs!xsmByW#5r@NQA3-0$}M*Oy(d`SL%2vpyl{F5xhCm@T%<%JNab zrvVLC$T=PzZMBkX!lJ(U{GH@U_CIE^KZ-LUBiRh~)vutJnn#a3@xSJhs9ZV6?t*Z` z7sajDmk4*Q4$=}r!PN9xbyKUH#ns_p=jCKrVRhp1*U`ECR~H+g@m(rCcIK&XNu|F} zX>`(P-IL3AJ>O!o4=}1J+Kb{{hetey(fY<@oHhO5I@ESm6xfobLeW&HN|v#AE8{dH z9_M_BhBnG-7}0DPs0=sj3;Ojx5)vj|ET_!`pBz^_n$B<;^rQ0TN#iDyz=Z4_yahcw zU0j-93BM29h}@$FT=M%=XpUdW`@g?PDA=fkU3M>rL3WX=lHiS{aQzj-`oO+h5o?4R zHIM=D8!M_~snmmK)qhma;v1uZpI9!0-6ATPwNumBj17qPfh({O@Z=?~9tMeW54xKz zwSzjZrXqXLYLpwrfR#xky;)K&<;7mvJFcmySFqxCq+0|DzYbeQtjnw!e9MP8989Fg zw#fB%3Jp!5YO&DVVw>+;xv2|4NSxt+fXT`j$YD`JRz88Hm}_~5wdM2iUn!pl6G;h; zqhQv!E;BurXr}yRtgjcVV*qLVP?c9WU&6DDN0=1#He6LX#IN+m3dF(0S2}uPGia9c z=I+fMBr_^4EZTDhJz^6GW9PWcoysG4ciq{e9XBTzxYp3hYv^9~v-lm$>8o#k~+ z-<M$1JMqyp}fsnFoiq^*SPm&Xw5SHr$xmeeX!Q7ZzP9L8SC`&B{Q~OE(iHYi@ou zFm1a+uuikPd;QsQK|Xb6_*~VZtQQxq${U(WjHdIt2M)<=%10PxB>v#{f)(DqHnz5G z{riBV4W})1oG<60@|EM=O|(*llgZcAw4nu|5K8R^s1@ z^UPfd-Y+tJjYNMtCI1N`Y&38R)3zB zxydB#bh}E=z788&GGPD-hnPgBq^}vI%!??zQMK^n5$y_jwC!>~+_a!%$}YlxM~(CU zEnWZrc}@dgqFooaqZ)WJVu(~n@DP2F;aG469~i#2rLD${VuB_oQ<2=-GZizxaMi&CfKSIa>kr_x!6-LV|H$S`|O}hX?#%VyTkW2)3@+z&~NxAU*_%LEARk zX_RaP7?)UOC*@lr`vCD~?h5l5ENzJ?Zx4R)8Bq7}y6nYEtCf2aGB=mfTMwwq1SSx?bd5yap-f$yz8c)fBZy}a z>4a&LbUh24+q9{Oi+`w>1yf8^zk6aGpjZ+RxHI=wLu<4S`jw=qBMpMEJ(trVDu!TM zygNwA*Bp)>m|RY1A+`1EQpwqA#$r{}CZv&GzD#b|ujZ4|yDW>!W89lre4)Vt7E!mD zi5a3LvrlO^^r;k6ee@RS?}X)BCR8Vgf%r{sUZV4?48AUk(Pm(|Fsa+$bUBn6DCZ z&U-4upYQ1Fq-mVu2p63E7;t&PUD4_SGLpMtL&uYsaG2C6O=YYkg~`w#G#lOSk|K5-zhiMjP}f6@M;FAOU&Ed*G~NehY7f}dZ<8^5+Q8!ToEisY zN)mGrMayJKi03uLb=ivxLkMY{YGROc<9lrXG9`}w&$rL6-s=S~l?56B7Y(n9O~36! z_LUpsEV#Zt?;C%o|21BbJTi*04L&J0)05xwca(grmS4&J`_HEaolVPYm0Yrdi{Xls zeqJ)nG(*m3lh+*NU}iq$4IIIA0wv|$^);nWUnKqq2-l$d;dBvIiiI`7XDQ0Q-O>_b z&`j40!6VVuy7v;>5-F3Cd55kC-@B^;vT}cV2 z1R@I=(_w3$p{5HkB(JQYsXKhWfM_ILX`Re5hxYWv3k-Y94OPqxn2Yr!cQ6HHZ?N^jpNx)(P4$4K~OKkU75 zj2Or`5xHlu-pQT!y{==4B%>?$smVEkMvT~4==7HmsC7^!EbI#;yidK_tKu&;cQN7h z?&He_h7*Rd+$B`iR@O%MW&De~9G?>6j-%Z={wQ0=+xo$C<|GzGQn7!huzmjrVQ(4L z^#8bV4~7g7L4@g$(I_w!8HjE)DBYbB(xr4aV&E99#OQ8NQb5TKP&%aqHtFv4dw+ia z`+j~u;P7l4=Wx#6pZ9fLuUCn;J!i4}_1jsG+cVcUyFe@kf*PS>UjhA{YC^0)X5|iH zQJbfm|&%zEHBE>p3}1X$Q*&Y=s7JC?k8p4a!0QQX%34I+;~&! zwb^J#;*egBn-INJWBIoJ`yRZaE;joxpGKMRY)3bjE>%*Jf053AZA1ASgW+RE*uYkr z;JWo;9;kD`+OBf<#SP0hVCEB1F$}PXOk=EcQh6|P_wOn*;4UGsJms*DW(Xwm*1h;( z^dMy`y0L)YIa|55mTIf_Zwly!M(=IPHf46d*s^N}J?E*Y1m@1#{ z1PL+XMMR1rcou*reia=5Sj9hIMjVOGVxh661CybP*9QOsXcElS(u&rMW3fNmH zhz}sB(vN1b;~_Z$2UV#|KR>`SJu%j6$}GtX)`|VgH+7_+?}+s@7}tC? ztE9o!>&fu*r1ywC1I48g`ihCbZtG@1Hi&KA<|vXdY(th^@Q;_o4b(F@`D zP5-XW-{Nn~61 z*fD}uMlJZj{z_63@Bc`QGl2UYAoIu;x~EUu{Z7Dq*1Gq4SE zkJXJnx5rCEr5J-Cqca*`u|LiC9x$*AC|Ae%yT_q~e}L$Vq{jE|5MIxO>W^1c9)fN*MpU}oj5;Fc1tz@2Hu-v8@{YR4r@b5re(+}rI-^PaG~uJSy19a*eZ2Kc zpORP3m;`^j#GuN7E)r=hKB3Dxap0QIx5tZgMr9lY1{#Jg)AcC{PiwfPMHo%)drW~r z)*!p;NFf%(Q}?V2;}|R%ZJ0HAh{!(|s_x>=EC6J-akGNODgk|6_aYB@{3}d9p;^bJm^3A?| zdX0?IyI(nJ-CE+rmhu?B!n6LGbXrGPFg#%JPvNog#EmbJyPwm9Yktb+B=Dk2^tO+9 zTa9nr7xmB_IuW%s1g(_h%M*Bl5#93bssW3J*;9chQ z9I5E4evV>EXH}|T;%OM_%y?RuFTiMt;3r6~kC?iW6}U-)&_&eD=RswWZA`y~uT-t) zW9JCJDoqmh8S*i3u5AKEqJ{*3>7C z5A46DP^&kL-jibTFl_3w0B6rmwhs-*anV0C$>zyXL`0L|#@IX?ZKSHht* zkq+G^f6>AULvBxYMYOMhRuY2O@v|7ltN@Q30>F`^jD8qbx{^7!-uFlYMSyb2Cm=KH z0{HwFZcAPj^9$Mh21AbJ*avtjTqkM^w&O4zyB5l_4OuzMN61WDIq)~a;fcj1@Uo`h9?%; z*6lHt`R+M~X}BzZ{D7j#?}j%$GO#vKVp@MV02015RClD(qu4d}uae47@DUVJ;1=fOOrA z6`9<|=atPWRr0(YSKKdcIj%;rkDU*}zWT2@qr)jOS!eU5Wo zDEQ6BL5qO1ptTM+9n-3@ll@dwKcNXf=jdE)k`RxWeIBAkWIm#p#Qib(a@+PAPt3j$ znu2|=%B4AWb8!WO?D)5*w->$OovtUsl7dr)4$T5++?5KyDO$7Yql)3oWxnp?pbU>II;V^`_|a>hJ-VB{Qd*jZ$^!>wbP{WfaL*~;(=K);GPJKVb)KYCY0!TDx ze_=DGa_&vPq5BJBOa_b&%7{VvnqulIoR!N!b7wLWlt>GV~d;SR# z^|S80Yw^*z1-c}@19dPdjCBl|{$6DH@b6Q`!n37mCU8wJGZ@H&2Mxb`)K3z3edU8$ zNZhh8a{dpr8|OE;F#lm`BbZ$^v6G$7+t@3;)o!eG_UyBb=B;0?emBB`HvkLg&AuPk4$zPH%Z|755#bJ&Be%c=Uz?xSe&?l3OLgsI zyAp97zw}fQWnVCh%A|kBqT?xLCIY?iMrO}v8Ar_m{jmKl?L~)z{h$etKfxAB+`CL0 zejWr&f)Mpl2`|-&NbM_|H%bO`J@}XAjQ@dH$tz9d8%xkU4M$bHw?<3)>ZcBi=X9Ho z1ZNz-n$^0Q;ztJEg%w}l!kQ~^@UiPSzb2UIIDX4r<13*2XnUh&-IPb|&Pi5DmHw$@ z!9zl9%OkgA9gEIf^Cv~#&UbBUecs#t2O3It9%uv+~-(v_7#`w!a z26osVaJW>wXpy?2@~RLq_d~Zg;l@rIo~7xe;`HHhB^6O(JTUNOx!(phx8V~P(@pWo zNc07vM&k*vsAS57FmF3Ijb`)H#N)>XSz9;A@l)H{b2!WR7qvoBQL+8ac8m@S=gUni z0^FqxU-)4^ZyDKS=BG6?f#^~WiyokDVG^0iMQ|RFko(4lx!3ZHxD_i+haf~V1fdI~ zF=d5RyfAtjtXW;u%ff^gKZGVa-c7DWO{g|8{?c=6(tA}d*S)ACq+6U}_t)nDJ(D-r0Qan$1nu{ zm$hiFzQpo+j*{@Jh+Q^%D^I*uZG3ps!1Xv?Nl>J@AA_{o(GjN2JQX$Vv{7BWdU%lV z5cImZY`fJEgH`uXU9zjGi#FFD`Yq!6yV+s6J0?VNZf-zx`^Y-!PCX@Md}v2gk@~NOHN*R{F+G& z6}V{6_i;Rcwr^vrdpgp6@x;EChY0>rT*h^8rh)nO&dZ83H6>mcxK&NtBj1|Cvzu$| z7Qes5u9z2FUBRmYCU9oUs5R!c)Vo3f6Bf4=>ntl-5oP)Y6vf5(xOg?CN1#sUt8H0s z@KMFRaBH&&Rp_dMg9w-WOqPzns2Jc*YE@Uh(X8STvbyeRA_p}CJnd=ffQ3?|<0c4< zVoMe#H2JbA^P*W1?Xf;$=U!{N<$-#>q%jrO8~2B`Es5Ndb*gY_5!w)aFAjplKEJkX z{5xRE?PNB?FFWPMaA+0`bP_dWPDrDi7;6dQ578VZBM1g}lD~}4e#c|Y;k{f-l*Rh9 zPh>~jpH(1}O09Svm3|s(k{JxJqh<6D;hAImZ-|(^K~Y=A!T{^&)R(hlR7z}b(|yNo zHD!^mHY4az%c->i-W&anwa?(Ru@gBCO(geibh)j-S8pcF2+jjl%%gy!X#qgexxz*y zvMp6o7&YNnz=2Vp@6@pDSF&6?P*~t`zO#0Ce1bR;`o#)3*yNmP!WkxNlKOL$z0oBc z+6f?%S9z1`zgM7hLtP#;ByxiL{bjU~ zWt|ozBLLpfWnS0I$!VY!sD)b+Ta}LcWH&XR8MuD-awh0A_nYk#G!&s@9v*pQ71Pow zmj9R@u-OIh6Z*SdA4|_&Iw8phWL8++WhJ7QO-;iZw}E!^ec__;wdwlIN7|?326AY; z%uY1rLTXyuQwmF8lMh4IVL}4RM1uhZmWi_32ifsGY-8KMW=(|C1dEv4))CCsI%rS= zdZ`X$`*r`HsE%JaBtl*E{s;8$?Dmx!VsvD05752APSg$yt*u2kRbwy-a{+OPvmxe( zl;V1>cYXqyzyyDMTl*l#nBAkK2uj3gBmCE{izoIHLx;#y5W{({y@39D#`^VAG?gnR zuaU&_ z49U~2NQT8U`ng@rsAouKf|M zwVmzlF0^~hn;4&^8Ia%O5mMJUjDuq{=$y*qZEnfJkD$4(BK$Hvd~|-7@FS045rY{oG@+L?-v(-n~C;~notxl(Sj&IK6s7OE=eMiM4t zbNUS&t`tQ6h!zfKDxl8CJ@V}_(fOOdY!Ynm$W{lVrDF^o+9Ay626=e3XISKXsjzj! ztK@Q9PjrQ#J=01RlsdMO47Op|XM=IiY%K1JpD6Ar=I&XWI?%o;OQ^E|R~M%W`vLrp z(XU|i&@B+qIV)AkK<>nw>?_DXX2KA-vdKxdZzpXwodxuN`7VDpBd%}OSHo%5ZSJe9iM1P)!$3K;mR zWmA-s!s*FzQ7s&87^Ior+&-B3-jc0k-{@m ziUD(2ow?0f6MK}x#(+!(Rf0?Y&o~gln7@#idkF_djyh8rSbqu-s%6iwvK zL;ij%p?CWLszRnx7yPI7xgL8}g((M?-YKl4w~Z(PfJggOx>!7#h$WX! zW!V(xcZXctW#As3T3>L71I|jVR015=8|K`#lBP*M=x)#bD@bNM!@c-hV16Ng)L>)ymS<`I+2k; z{`x-i{;+&ft6a}Vs;-Lc7I()Sy=H;E<{fd1PX<=F$rq+7qc1enMBEY!&s|rwyg-p> zy0BYe3-<7_A_NmdCJ^h`W3026zCKB=Gz6nzD|IlA$f?63A#T*F=Hqv`n_|c3`rT6l zDyz-lWGxX4NIvwL^sY+Qw8_t|e@uYb*?y2&V|0Dusjm8>ai#r>M8_>E;r0mM+jRFV zDHW*KqW}30zejUJ;1`pXmv>H+%YwL1(-&gjry(EtE=n_)zG!V$(zlDUIH^IhrU`HS z2O_V^?L&HvCeu`ots6_aIzDBv%w+_eh(lhNne#G~}A~pD%d9wBP znd&Elpb~aqrP}^X+V&^=@0q2g?#0I~pWD7a7_t67Jni@4_Q7X&36?NC(;IGHlobw_ zc-Ajr#1r5pdi5$uXQ+k$!uKEka|rkDan?&w0N`Z16T~k%{qD2o*jB;M=R5>vhZoe~ zxB$cb)TJ2mi_UKoWjX6w%CCd-|NMOINgFLoi;-P87!Vn(@L54QCFdNyt$2KfaJ`ft zH&q3!6=M|zvEWY3`RA|y29l~AE>xZ_H|NeJ1Wh04yiQP_E9jZ8DClB1u)!*}B9p8+ zLZEhfG#+AX<2NZt$I$(K1N+U#D_**8boA~cWj#$g!D6u{E$gyyn{a|t@l517KQA=h zuKQNL?K|#?Y}*y!bL!VqED$+-ihs*eJo=?Xth4_{{+*Ic@*onM41eFrvrCE8C2Pne z78h1Kk`apg?~++y{jMDIX>Eu(-Ad$iMbc5%WXu)(By?3x!wq5FnXTQjTo^1099``p zBQ&+~Vk(9#Eo2$iy)}>XB zgJ0q$EM7uaE4 zPmsBCYD)bWlS=m&J!su8pMV-xN!FS~L%_7nzp3Hnh@E;P1^1=-j89zUDV3%qyuZbO0CvwwzL8As}a^&Ga-%vw6$X z(@DwysHY9?z3N=v*uVgyBAZF(j+6IY%&O}&t$%l$`^5Xo8{LbC)w>0TIU|WYHQJf} z6@kfTGm#j54nrP>_@U*dQh9TNKH9;$pKU<{&03n;t0PvuVKPFC2BWsp@({G2Knbn# z{zj$zWqIFYRE0lX6?pbAFKGEoxc4h*8$sdhChxWvmD&d@oqr}cY}{3VAy*Ye6>O#n zQ~#3lJ4w6{a0z9ZnV`(;9h$z}n$fozMK6I7sWI!Kx>a1LiZ>Hz9Id^jQAqoiyM{y) z{QpC>*P%GNi~=djt5yf3IX=szBD8{pqjji&%9095e_gU)b0XV3Z$ozoOcT=x&cx@I zL9hRBNDs<}V^5dTRf-_e83JMStiWsopu+#3SDmwy*097+=VYKh(7@{3qf|1<=IH%z zYZ8({6Q8TUFEQY#A;p8Ddh3CRQj2knJ5y`j9mYunFARNDf)#>UzGbG+h1sw>oy2A! zgu{C-9h1eu5fGDzFi!V~V0sl-6(YA(G8BZwD+ESI7x98ogB8y^8&}m;hi9F2n%z9; zJOXtcw}m{XP6`d4e5Kq5Cd^ik-cO6)u!_@kL)@*=7CT`*AA$9SVc>d)cr(li6B+dX zUMv_YY)IY;uzP@Uz5tp7_A?Pj+%1l!h*hak@{Gi`mI#ntW7_(`FNvH^S8}ZF0RG;| zB{_yT#lvDm_y6Ymk7=Z~-*~-Q0NQ^ha%2k=o-b5kZES{sUr|w~h66YnT3cRrq6X3V zbunSCqHh&{IC(?G3a@zxDIMk(+76C+6+3Ij0OM(Bx`8DWAk!mg0_stc{C6OB0z_;` zL|%z8Kw#I_;-4qIYO(R&QJ3)q_=KWQ9ni!I)#z7C8owMkHK+#{NAin(E~i$2o=GJ5 zdZuC>UwsT$HwW7d`PvfLdJ_Sd3Y4y5H?)U1fZ77+e?J8lEmE~f zq>Ik!u!qlu7Xl5Eo)uk;XHoqO7EpP#cNQNy6aSgl%?NGbh%$rGTt3$ z_{=WLR~joX&o{3CW&PNYuvs79i|25$2{^2zd+8gFTWV&t>X;FZ{3%4iZe?c8ra93n zTC?&R`R;O0foz@Zl8=BM+)xO8Wb!hx`F3W?pk$yfA4O!my11|^H}gtSfXWB6O62tC z2b~x?cid>A8iciL!3`2X9;Up)JCQ^S;%{Rd(hElf4{Ik-^A>L+ba8O+EiFU27qrVA znUQghjl5ZZb&6q24Jtjq%z@-D&4jNIXu0UkHDq5?DT&0b>=ptDAn(JHCt;q6+X6=2 z(1rOKd8<)T=N=C)SD!Dcs4CH1y!x^J3;^gQ%6=CVd% z1PO;*>iqtABy&=w&VHICkuKQbhAw&cXSwpN)9pGXHvXL$6^* z+$Bbp2{)Smp7fhx@l<_c_~_yAx@6D9bx*m1!=|2S&O}UaBjBD;z0FB`(6jx9g!hS% zq$<@bJingC=a)cnO)cf&Nm4x-q+S}q{mmE*Wxy8kuR>PW6TEj*0fN(FxJ_}z7 z+zNPRHdR2WC8sjvhnZJj-;+~Qy>3tTVn3usuW)#_B=7^J*c%W)zYHVU!wM>(d|YOv zp4%f+Up_^#hT%_0nYwf_){v(D z7y4Rc%+fKAuzKq!mCr%4Jp&L?=4&a58-q|JoXstms&MPy1J8|Tf)DvHdc+4}heg+V z33Fx#1d}*V%mFqce+I|LVPC}bFj`~Pj#}%<6`j-bsFVo5BUn-QZI#4`kh<5FcyZwQ zYL0v=mJ>t;^fM68Vasw>o!?cDn2eY1s~R_v;j!DdY66ij&><7 z3^*O!FXEw92(yOc4lDiZof*1oI%2D!T$qJWgagQP_NXYkchJ&VxK}QG^r#>=i8fAJ z%r!T+s`#&#o915~VCl!C8x`kiCRv%cotN|&Fa`BVRN9NKUk+n zN~_h6qEZ%b@aX#3#EB;Ku4(%#PT~%`9rDTYVtsd`{TSpkJ?0L2D~ja%%m(F)NVa$T z5I+s zAOVhcXpU>(nKXtwXw<@qs7UY#goAEeqe7v=T_Fb5oRoF?d}P~L~xOd?u3*ZQ*4lao!ZeQrBjET2*&bYz@hrS@cg4JFe_SA5w8My<9!4t zD^SfU6bONJB-Z+z#n_)l_tvpO48O|h%}yyr%d8ae|4AAVm~*Mfi}30fvi^J51-Pc0 zL7KjQ5{8T}vvzSUuXs2`YInXH`Ue3yENk4FOmluNM^`6YDb?Elcr(U>@DK|s^%Z+_ zyVTs#K$O6|^URUk$<3u@ySPa*xZm1*z1k$=#g|i5jHYOx#VOYLt-}`RIWL=|xzaIH znn6_Qh9N&|E#u$=4CNx*=5iD7i=Rpvht<;;oq|V!>3F`>XkmYOY2>AI>26k7-5{e` z=e2@-ENe~U-ShN?mCc+b>Dw<%l|8N8|29#?@>y4e^j+S%!X9egFqxindJ?x21xCk1 z^<}pGD#ec0v<%ZHYu1K38|vR*E}gPmFgtylt^X_*lb-aT953fXT#LT)I`zMhJY`%A zdH-$l!@W_jG67hFnT3-Yg8~3>3;22Z?n3eM!70-Ie&V8E`K9{Obq6K8dbj$x>IF#1 zQxCC61tA8PR#(L>rh9kSw_OciB-q3Z{w~isdzb^?nvRSN3&?cN(7_W=PB>4~1{t2w zme<*@KEhkf=yuN2N9@R*>V1lTfKrywb*H-5DBkkEM{@6gz3&I2C3KJ2#rE*Wx7IOH zfmVh6)tXP|>kG|KG-X%OtEeiPanXw_9pAL|CFQ4#`!yRz{@|Gg z*)Dm*Of!*HB-07jCO_+*AZAzb9DLbDtkN~=ck&sDcLaf@zK`1~fXGX8&@+?3SWHO> z^2%SdAH^?;T^)1C2qur$frFqjC*6uCioAP|-^5Q+tc0gED)bEGcT>=k`=9IfE!ga8 zx@Z-bgP$_5^>dOkJ$DS;l;`3%DZ*y}0ngo_?D&}iF&JyglX;N?8Ph$|bfiC$C6<)WOW zLy<C<_DIJao6Kh({7yC+kEJQpD8K*kXUClYb>$*vA1}Qpq9E7|#HRaa(&71-ih-!Ml8Y88 zD39&5mIHv$sNXCHG^%*I?3*Q0Sd!U!#6i@fr-i%#qt8he+95 zWsbXYQ6c|C_?2wsUd^!0d;v;LI`4Z(3nV%xo*BWr7GqVlNVxrmfdQAu#)gg}75b$Z z7HRUyAjVU;1NZf&G~-A?AJWiPJbud9>_uNZfknKDOs-0FTslIuw&4!>$m<9Eyhvh} z$~kyZvt!i#vEj+?7FH(zGpxgYa_qpuW!;>5*XH{z?ASAk&+gqEv_=r0X(6gr|FXqP z$q=qzb7DSv#OA;s@{_vznhY&!d2M^K4gNn6`WouA8Y?JKPa7@G&ud?L8>_gh(uPvsT@j;A7LPBQh@owZSmGY*Y_J33E@UXAg1lz+q{lL#QBlqS+yKX#e+OiH1&zYvy?t zdZ4EEAfH<-OD%mGfUiiw{52WCyw-mK4K>Y;=jSQY{zb zb+Is3zm@xq1p>2k0$i#_-@OKT8h2j6d2ZfxlPOQ(`X{JZ;5=Ods#!NI@X!lL?>4^c zkZmRJZZ6U~_pm;l@v<0fRydDAn`{k_aE$b32{JvnP|$!B`12fyUiJ6S^o;N%yu9@@ z;-*)XDp!Zt>40yo_t_yzMCISl8+{F*|Bm%ETE+{(9)xHQRt0SQV^(#AV7y8MBVbnX zxboKPh=lBa9EL0&swy?aD=l8;b#2zhu@J)+qj>Ghu;or|HSACyqQ6|iFwX^XI>R&B2XpE1B zw#$BpIGlS&twei?Q)T|U@!}DUu^eMhU8B8liFVkX0Id5cbT?}=kNCcYn)rw|NOM@Q z&fi-SRr+TwFi)wn;vOM`!Lo@ZIaI>%&yU)ME$YG`tKC_jH2Qbmk~D_Hm6qf8$7p(N z>GX1A^5UY*NPv2)An1mZ=%|yb>H0s5UBb5ls->~T-7KuI>DzghoB>(rvRLi$r<11! zaoiGSH+wpr8tQ!69!yR?3+$ItRgL<_Y{c_Bj4*k>CHS5_sPSim@RNYVv57{tYsycS z%VTxEKS@)Tol68D7%N2{;`$fzEZ;0+W{Gur@+gWUf@l#PSJYqEJO|VRH>u>c;`~i4 z<@WY3=X%vv+33au)VSP&?~b7XmrpL)TW;>@vftgf9+?~bJ%m=T+fObE1QnhO^b(+l zZn0nU6IT6&gN|54%e_{&PZSZ5aK;?eS+l`FYR-(JJIug6lE5@~Z6Sd_7sj|vbpQLA z)!nq(4z0JhAD?zc8^X&7H%p$Dyc9)fV7!{Pf6(i8cFOPhgfG!=71fR%QqzUAr3U=G zIl=HQo@aQ4_DL$G5)WPS!Ey7;mrWU%2sswP88_dPDwEy84|cordG_TdwF2%|h(W4M z2CiGJzWf_w6OX!E$wO)U8Z3q?D<23J)@QI5j;ttKa_JdUPAb?+pjI`+e9GPSJ&Lmo z^o{rm{hIjP=s-K+BguLUi5qxC6GuJOPWOus9-za@OI2l~mA8LC`uU1+P#p9JD_@8^ zhZ8cmIjWpr+Oi!Lv0Fc2bpo*z*;|TZ5{q386JzH)lMx z-TS(<9b4LIU$Q#Q+VFPkfeo{Wd`Zs_$Dn{WN^M)c71i3xW`FU$rRu&zXPx+f#l1)G zgXEV)s2TvEO{7_K%T2NlYFqp3r{vc>K2_XOhwpzWGqg!cEe#(NuE!v+5W0%4gY!44f-uF5w+>oH4T4nr96%RIbI>7m^128Zbvd=KJgnx%x+`)Z)fsFhfYFmK^|-#x_0@R9Z^ z7@gwVsF%LA--a-A#HtX6mtOpGA6O7MHPq;%YZHlQ*FCB<`Ev5gTRyPsW@joee+)<) zR#N6}EHo{-F}hcwL~P^Tg|74&h;!~Hcge)>6Pb4InHvqKS}VpsQ<`JCMaH{kC}$NA z+7|UdC^&Zs#uR272L@>QNd>{GHPg>rs%K1R@au`i5lzaM)qkp93>XgN>0J*xg!Lu?iN z1e#X;_r#}nse$x9%yVem9Rxp=JS|-|Xqy}odh}UFKcCPh5 zlu9kOc~8U&^WPt3RL&cekbiv+9kmgzbrpT{|B<6->VEy#NjZ9mA-8QMQYz_EzYcccK!ne zll_BECh;6E(4=0fC2dAO>PW;R^(=VB1Jg!QDe>nAdd3>ctO!+2xn{vTaCqjCdU!iA z%iVDH7V3y;@DVo-SjgTVeduC$)4c~J%Aoe${dD4^V$AO~;HowlXD<$WLcNXwQ^HJiBSw!-Q}+&X=M>*l zQ-WxI`m$nI-=zK-wD3tX%)Cyr`|FHSd{4dzhszT&&(+{kOCZBfoJe|JAUjRggXb@K zI_ASnYb-Fy+*{F;J1~Jl58R9%tbhLrf}Tck7<_yCFpSP~ze37O)lhzw$s4tj-E9WQ z;}jqzP^Oy=1}t)!vK0>2vpy@=cb*qmGaDnV8tl%rJwWSqHot2_kpq-MR26|rL_8DO z+I&&7K7$)X+qiy)qH@6YeW}9BUD=hzQx&JlX_L7p@+`V|}H3Q)P zd}A#-LyMuEu&9~3d0PbYoQNvEEUy7{O7u=YxV9HO#Fvs^!rGV#0e!lgde%O6wcKt? zZ{HWMof^FRB+?74$asS2=uMWBD!uVfgeCHD?3yYr@i&oMYbNH#1N3ZlOeImhyuh7|@?7%zz{ zvEe-R^o4$N7X|PSZ0d8{yj~+9K^jUfYyuA5IA3;^_E8O3$pBSPW$JZdHz9~_+9B6= ztd!43ft0sGq1xJK5{JYM!?&9Ymz_1;EDFS}lW^$s!ZZ;!2^(wfk!<7vf#ayc5D3Fr zOF3`OW&NBFPt`HS`d|a2u}alKvK#;j`IpmNwP%kgV|P2qp4DBZGEIoqkF-LWlR)mz zdgW|xq;Y!Mv%)b-8x$7keVpBk|J9y0L{vEed9K4mt; z%znI6qMS0j`BHr<84ToIO!G)is%0WRxn4NQ#a5M?(~k#p1bse3x=AU$AUf20N6m*d zM$h3&234l}oix47n;hQ2Eg1&A-X(h}DPX%aO9fN`4Bkf_&P*Lu5XHH9DKVoA;L4EP zlwHz|&IqxI#4_4fE374N$UzdczGb>N` zfhoA-$(n};8fs-?Zc~?KFNhpM(x04~I|`KBmn((q-|Bv}Qu(LGEyQg!u>Gu23{C(+q}!*BVO!?-K26|B-ZJ-IG`#J&Mic5 zOymL3Wc_YXNF7V1a?ipj=caBPc?;%TpE@am-r6ML(e{PgH9d!)g6=@Mod1T6N)jxizv$uqan^NmKeJwU6yud= zi`$M+FmQoPFAjIK=`2&pP)+AEMw>@*vRwHfA>ItfkeC*g$b86F=T^+amLge_@F?_^ zU&ZtBK3k6_yuT?MWt+=Gk7z1Z9}j!LoW=yNq}kCMPGp_8_{5 zVa_w(g)fCB{xM$4?Y$1Kn^FDf90RdvZm9Psa;U#b_|lEQc`%dLD^~c;)-1$ha0eCw zvF#J>FYl>1jl>?ENKVFIm@DEe56bH^1?UAv)6}KS|2f-z6R(VtY~H8F2e|9s$B@fv zxsR7FEW`z{)&7hE(?aG3{HvarX30XlY{jBDz(6I+4H+k-XpWe<^~`g!0vSgE+BL1J zQ}~z&%||k@jSPELWH*j>rtl?V;Ih!O*pqKvj{DO7vyPKtNchG4}GmlK=GnL#cI8k!qgC?aariPWxV z6<}W>8$o5}p;_U`%-r|XR)HCU$sDKNM)b00xkpz9WL4<+a+V46Q!1`Hul!*&`49B+ zbZ|hv`+Et{V+6Z%OBY``uRhUHWRDdEoj5xLt4ViX9UoVueG@hjt{BvUfb5EIp>HI}E{t!4sa*J!!rIT?EgIF1KG9fp2`dY~ zL_yViDPo^(7~1gUpcmMVa3sJoRQ$c*rs}fr;XPU6bV)A&w%Red{%*Eh=9-37T?Qwi znrU>bnU^8HH!^;vrc|t4&L}mEmxDAmwnm@I$a^7)d~q{DJ7ztGK@ZI!(;~*ea}i`{w9$A2T~VYOw#@uum)rCa7q=2IP=V zwXcE-5p-|w9otiTrSOE~qO*UFa<8S$HHRfAtvWx5KykC190=%E&+9RKwg0 zV#L0!QlfXUVJj?UE5dl1yDI(8jCmSN`x|61ONpGBDC-})NzbZok&0R)ph|xxXl&;d z_>Eio$|kF3WZT2Gl)7q7HLdXoV?9A+oAvpqTo`&Q?Qg}N^+{2xPXZEi zH=^lS(&4);0Kn#h;cVcZ8>;e5ARRWv(2h^hAP38k?@8-IlK*d;fc*b!75v}tB!H`p zsMM@e0kQIt0rtr>Z>Im_2au`p(QEJ@YEf8Gao{ZVl7Q*8=v&!yca7XIfw6bXW+!Y( zSfCIvQY6PmF2=9a&OM*-e-;7ygMRhvNp1m9bg`psd;k5)u;W&?gRQin2kzfE7^;II z>;;ZU4)+c7&_#izuaaM-dl8q}Csre^6qpD}=c)EFZwG6X&Q1yhyi zM}}%^2sf46x{qng8{3$AsGP8_^-v82JJ_|eS4b7y^Q0CK0^aEj(CYws0*D7kfSoW! z^FI#+;C%4@A95ZP%%rViSRbj;rTOz^LGgL74mdMeL_LQ#ByvMrFLsOuTjCG~*c2@P z_YzEp>mf;f@nVcJcP!zICfZn;Lsq9Z;P7o{72w1{D&Fq9xQ|m&mZ6`FUVAVKbdrp2 z=RF#|=Keyoai2IF5Z_k8C-C)CZGwW}7a0b=9$t6SR(}oa%Lk5J4P1k5s+4peZ!6bu0ZN#6;%iTHJp)mAUdhK+S_l1UlH5Dg`te9 zYV%^p+u|dX_gcRvtN*^G4D=5Eq&fig1N{OU^0xvwxP67@NS^$5C{04fRC&r~RncOa z0|kzTU$z|rx-f_@^k^K@3K%TD?>xdq9uA?;QDs^`R6aD5{rSO zYP(e|Z3GLas$+W(6Ggif@nEM86MT!U5?gK~Hhn5~%a}9a>B}Y}T<3FB90rsJP$j-z zyq6LD;kUpZ+L5tb%|QXjF*X!XG;rOkP1F7K5aUJ$Zhi5#n^LXqRzMBPpccI8B5*;KV{JFZ3?fp*agX}+25eGhD9nC%BBZy3tJDW=J>ptdq` znuTio0u@{Gx5)4L=rRW$S0B}P*QvSxa%^)I7-4G4J)3(J=RJTNTjiZ(7(u1F5nSm{ znuyPT@97ex{hX3qP|xyK6ROOy9bAthFDvvs)mxWAiw-5H-m8T(?&yR!gTupni>+6K z#r3|YtRp`BeV*;Qmbjrr{SfQErKaN>91yP)9v%%=`Y10*pL1C%uN|heG9O+aS4ssI zMFCEm)7H;W&Z) zJ;}_&@3>oV!4Cpi)3la@KXFr(UDL@A#QM)<-gw~D9|=e`Z2^9v7ZkIuf*fk>Mq zW#iZIj_cu+l8OPLnkTJDO?}|z)Pt%VomvprmY*3ah65qv3EBBDR!{*ul5~_~(#fNx z$=kSuqZHY80IjTx)5IG#t+2{Ml~Om?jHT6EVil@QLMeAkxgFFCX_((&|1oVj;H>oZw0ohX|p9Q)kcjriD+nlfiYzj z`j;WYM~B?gWSZK*u>~r3UQ6Y^DSt9@PjJR(1q@qC%j2DCB~zA~uM>g+w&wfkt{?a+ z-W;=vj}D9bu&#x>1td6iL{OqOy01~q;;xYbbvurkvTt*cB{-?KxWa3)sai|cQ&)WCVKHHtD+Kj+PPoPkGFIbQ@)~bV#;GYh7v?IL)RkvrABAFd z9^8Ii_5ZMS-a$=m@wX2K2_S+ZXwXm;6bb01n*gDR^bVm)5ke6VB=o8jDMAPx1w*d^ zLQ$%KfRq5zq<4W(q)G4KedqpW-kUdX-XCWslbJAQlHu&VziX|}@-U{eeNnf-;(Xag zx4|-d`YXbCc;q*}KxYRY{c}PvS$z*^?AkB69QiLu|0DIDGl%@ZCE0WShHb6yW!H|Y z*PxqDJVKq#{FF;gI&Y}nw0Wh57|%L*DsUt2FH;|FnvU?CynbTsoA}ar@*yC_XKefn zTCjkcP8Ch4>l(mLsN?w$D08Wt0h;v3j=VPA9{ z(Tb67r7-W3SQneC1bu8>vwpLMtdM>ew9yz>tZG4jM&5vgNz%gH5;PTXLiBZ90n1|7 z)aNh)vlnpx*jhI74yQ>T&o>s=KH(2-j``BZYxm5#+^rhRYXrQ^HC<_ZRd1w^< zqs=iMJDGWns?X&Xk(*>>>IAPPVEY!KOU66h6@gJ3g{7~Llk$x$?2rETJ z>EOK1VxA|L-;>|^)_e){>Bl}3vSSyTUMS5~;)x6Ge=0GoYExLPPYXdH$!x_s`Ad%j ze3}acF8?A}Vw>3=e##2rEd9W`i+{vu&~ynkICPL9n493yYoyvZHJ<*D+r+B<@F|u(9TqoCos{eU^OE5}Dby2&yZ>oBcZzb|`lMsk8E!s%oMG-BP>ceiJ zie7o(J>+aWtJL6S?&8PqRC<9bOk+^WDsV`1l`ar_(e?&|0}0l$it5(fH(t=J>?KfS@Jjd!-Us&-ogwBFR!~|69`zNJ8{bI;6)+zxP^~*U*Se_b&Uwt zam}ojIn{T9MM z!`u_NCW7{V^J}SnG?{4^d1$S4IplUI_w>`yu75$ErKE2^@xn~`{I}n$irc74{j4|@ z+rF^?c{inGCVfh~*dX}%PIOiTZzjD@KX+U|>(Fsoo98J{L8oGhdgwfJ52Rbs*KkEE zOiGUE?YevC6LJwR`YKV6e6ntyQsgh6q4WM-jeuD;KbzP6FbOZf19Ypk$6V=yo=fS8 ziQ`IB)_L*5+c22Urxg`TCjpdN-IHRIwW_Uw^#&~Ex#ZY4=8kC5a5u$|U<8@uPxfDz z+EtEJT;`=7B^?0mHpp2Ep7^$1N5StZom_6yXzUoePyb4#^>nslY_j*; zq%-)fhAOW=M{mUq(ECzBdbGnJ(42nI?I7G6Q9u@^uJ@&HxO-h+5QracMSfwqKW@$8HkhmYg**eEtjv4|Utx2`wp;SmJ2+&DLyv@m zzT@*N2Zb|V+}KiMxC@l9Iak@=;Q5CufIn6LhV6Ti8y1a@k3_noBhohyJA~3&YtM`O zCf@1n-_h}=-br1Rl6M`GhznKo@x<60z#xB=<{VQSoT*5sI#l9Dr)ZE2%KZX!NBfpkEsqt0V%xfri^&hvb)t=x2vGBpt`QOpu z-`$+MrrrlSxjZJzdEOFm_YpF&N$HOJyHv*cw?lM4bcHG6qHQl$m*{?+^? z*W4~t?02ctGUOBILX+wD60dneZR6*k04&1%oQJS8q%G zSEO+IXGXI6Jg&0r{NjgV@yDGdzv{I^=_t}w6Twl%5oU3DKCXk-2HV?=g4exSM^>vt zvt>n6Law0!s_ebV8&%qjW`WB!lZG`-L!<=^R3v{PyikCOlUU|ijLqF_jFC>&bXcOb zbCO=Y-y#o;sHyrS$DHdtluTpSXk|>6{N){94ClIh44XR-w=K?hZ=P;Ki=O4|r~Lo)iCajk#awN?sl+ zv8y^0%l}c3uJ!GBBZnJ2hC4OnbmOc2nrb7Ut8&M7xeLsDDX6v#bfH9oz7nw1cv9!V z->ql=N{~;0sY#3oc;Okv-Skt$m1%@_FL`f84un9P*U)x*rLW{Q4d7Sg-zo>Cw8jN} z{g$+Yp5%z$=A5q%dGY#{zy5-TpWcYSokNfQ<5#U4Lo;Kw<2kP>>*n|8ofx-Fn+7Y{ zrFakVjYU&Ns|$*g-vZjAU_Pj@71-F*mo{^m2Bk&gBo2D4Z#KSBDFFW*gu459UTwxhiv-x@m_p_T*as+4`;V{JpYH zhsu5#V?*61JCX)ZytsSk6Xo4&v{2ovzFuLKhscVc5DebUXGlI2r#Y?1FS(nLnz zI+{5s`|Heag>%6t&bhYD@S*ni%B3xp^;5n8AvN%JS!k}7P)`UF+3S1M)_;4(`&YX; z&yK?Q73rqpUg@@1q4M8x8HciEkAelwghKXq;a}j9*R;6>a)?%2$`D)jf26blp$&sToOb*6q zzr>_F1p8SFS*UPtlFUc%JzWXHeaPQ<8SrMO-oM-uI z3OMv}`i=KMlfmN3yCa|Qewfn{r`%xL3w}3+n#)TGIS=8rGV1?=B)Ur)hl~{X*jZjG z%M>)KT5$!X#*0m&F$Gmdp!5(#3HRgBUgJ2$6fy*##IGo^jn#wC1q9eDA8eX1_nh2{ zrNPd|-JNM(VfQKSg4)_PBUTLzxTv^1fbg+!ydqER{Fj(kR-F$Oto5S2g(wX>6{ub} z(8!}Nh7r5-G2sCnnb@uqM+1R`^g4Il^Z~}XMd_+t!raQSIn}ofAy?EUZxC#)JXW$w#jO=R$Gg|Ux60&= zzZWIFWaOpFG-2QU!$R}N!jgXIU?;Yof;Y3pt2(UQ(itY-(Axn@Cb<`d;+l&L zbsdsuX7W3b>lKEPhBqu-+U5MexJ4QIuU{RPu5z<)=5!|<7L-_1zDW#z!7Q03WT@&o zbo^Ag$NXdt9PdYOulecQHthCig7|{4(wme#Zj=J%ffIGaO(~p=}qP}V#ceQrHC9%Y}rTJ^+fX^iclc&AXWu2yE|UX@_- zQWEaPO(-=Z+=B=6nmv@O3q)|AqcX@0tfSt(%pdpT##1tP!s^)YaLqqWa0zR)->BGl zErpodOwayp^(@mv?6JXZc>^v*qfpb`l~cW7SW>i9)!!W$P;yf{!^I6=(nhc%*sNHw zWRq^aKaNl@J^^knp)TiOlHxm68o_#B^!uC2D6LX>w<0*ut$V-SdU?X0$23eRuKi^R zfD8@No>JPwAlZQ9ECq)R8VWMu#l|aUT_WhWT_wcfIFeZG>mk&B2Jc?_aCz;QV189;(z9OP_*|?w^pA0+$Aj% zI!TzxiXZu4BG*K@$NfY}9jN|VjKSftCWCiDRQ+N)2?GHNmSXO&A-0x1garIZd@Doi zMb7g*@)*gg?~^IP#S!j$w1GZ+WGp_ReMV&#$xw9aW@=W(1DNTiC)0L<89`uf*3}^| zrBBA9VJ!R2oF-ZV@xJcsCQk(eG(@u?0767Bw(I35@KnBi4mT%Xzwfa1dff34wc5cc zJ1rGs!z)nnXa0y$!{F? zv$9kPo0_Y~2uLyj(1t2%h%*VDDz%7#z-+7aX=pe6C}ObKMLS3V8w;cG%caE_FySnc z$io%(SHE5gr3_t(Lr*gMUgLj2F#9=jGrtNsVF|{cEE9W~x|&x3x}KsAM-2u)N7~kgb`bvu2sOmC*j&~iN>2% zE-_>*X!AK6%Xvk5!K!knpx@c;a=kJcRUE$U4NX;u0a#j@u(;U;?H6P0pk{ExhgC87 zMQmtQ879$AY9Syss$bX?@^viN7@4Ys5>OE2s(~^DT#ye(y}OQc^<5|x=IKuruudp|u&nMh&}!QuTzFr33~Cf^MOQ!5Bv zKUPMIKF4~qfg9~v772e@jX%UIX~{irNvW^Yb-!aofr4RvBTCxH0~EiithnG|(W_cY zA&OuArjB>#;x6>CsFXl~6tEI`ih7?lcYXj+g-uBp_Vv>z>$My_ z2)9VY60Y&fiy()u53NU*YQH#GHF4tb;o8#{KCtLcbz;+8mghx@kWJAHAekP6PfGm- z)$rmhNWtp!KXO`NXmh)P$NJh;QyXx_5w&|E6n45?8EjomBI-c~hUm`=a2~~eDxG9! z1wmp54eb$$!T4DaKJXL_{CNm&U(7x+jEmk96j}Skj|$De$G+{RGnXEi`lS-Dm6abf z?p6Bn3-VqLQ(~}C(7;GQlenW?qLVYtnh!jed&Z_^y+a|Z`ir`ZLSrPezezXn za)-s5J@FyDx4Q+?NWBG~3$vli;J$k4=K zEvuJ84n4VgC`7*S90MiKgl~@lu+iYk^Z`7Ct7@n$60|ExU)n{88-vk*?MBZHGty}7 z7Tr_MZ{>TeR&9V6?NCe>KJujwCI?-xUt zTcCyz>|cG+NV-lkHy&P@^Mf1E?k?c-Pw4V->vHJ)AvUk;N-mJj74FC%9dfY!O*59( zzThWdESTpFY?Kcc@0;FWQ1#)F+P>Xm4rlj+nV0&Oach2PF>Ab*v%^i!C*Z3473I@& zH>TZLK1yY1RxarVgqQLOy5UcXqrN;){q64*f};$6JG}^&v7gGA7+d(Z-g)!rd%6^U zOk;;yx4-iFvd4r=9ikhOAe|YvWkKbRkcl=A)+j-e^ZeM%#a>jXy2gll+8<~se*4q- z|2pukTwC zx7$oUM4kV0_jBavNmOq@t_tX+OU9c|_-8oFrMAbZaXsd)ajNO!+w5oooMe=+26w6cBe?rBG1K z3q+7C7k(x3cvUIuEDt$j`okIeXJt%EPu#z@A6krmSFtgTi7Y|~)p~5Dc93=ShxcJ2 zLsbSY2_VMn6udYjR;b^%`gqZ9r`-@!BefXtaH#P5H+GQ$ua!Lg#cQ-@jh9VN#ABI= zU{?KL-02~Ucyrl9gg_3Mqe>@e(d(#q_nLN4u%HmyXxG(mB%g%go1|zWq?4jb#~uPMe*ctiJ6Ar5!kc+I$`t;m zVg2q6*8yh1Hw^~sY29vtN~%=ZUy?REl=2KPLFs=FM8qZS?qxZLugYF)Ah^ZpgtPDR zmHLqLWP9YA$*kC<_leH$r^CoYseQ3?E2Wp8Pm3q~IvqhO>7`e>j$FS&G;(y!_1!Na zex_ru7!u1YpvIg{+Y&Xy6E-{V)y)j!BTSYAmAOKO@|3V_O7K?B$G(RJ!@7T7{+LUG znzh*-Sf2Vr`Vy`XXo1etuTwJhb8ZFf2h)$U_YtwcDrAgIeE_Gn{w?+Z6Ujh?y z4AUJ~cPh(FfCnp1aH|Y}4S8Z*TILSLl7x5CB_j81eD!qAm=qAhAzH-oS;ewDtXxUI zv+IuZ>6`f|!-jHp+*&U`+@;|VY6y@RwP~|7o{;5bGaVzE@#Q^UNmJAm#@a~&yD+>Y zecpwmq6>6eB&fN_`IDx^pWhnZBMEiugD;Cb<(vw1(u;&%Iw4R)1k|{^qqmh5{4l;tTtS8A?Boc#}-svP)z^z?vRWB zw9?WNc1_jkGP{$U5W&2D6&3gMltCHIjA(@^NSy9B7nF1rt1lS`$k$Uuvqq!=gM@BR~E1Y3djDmKTFNye6OTa%gGg<{Maw{E!`=1K*i z_}X4j4i56;;v!QX(L8Hq19lGfl?PnROl${PfHI!8c7L1(h~s~}{&gjea7v{!5|bw@ z2T8A-2~>TU9hV;;p$EmwtzDK*{ZT))o%yjcJy&!vt0H}bn*pZhU|kwr!(|z)0J4*= zUC8~fm0+*1Q1dr9kEI|J)g?8ER+TWllx;%>es6`RFATHOyU~m82l@KD`IY}E>EcxA zOIU(0R{j!`x?N^3Xf9+`HyHX>NKWNJgo8M^Y)wP;zbb{Hq0z8X%{Bxt?+^(dtO?AN ziHLr46jpCkCwc-K{XI4oDQZXH{Qi*)c6Rkk{aMegxDi%O*NN<}QqBwtnaY#b&Ej#J zYJA2%5#ojUogFy_ejgr z;Spv=l|!Nz>(!dlN$549F46y-7l5(ge=gOKrPI{Nk`9qdT|m*<_!-1VBgt4*!0d`> zZdbCuX8kJoX;7b|95?&hu2P5?U#0se#|!a?LV0%{rA3jB6<}y#%?i)1o4&pfz+VZ+BTCh!$Qbjxr5T26iMI)zq#PC>r=voJ*(STCQ%mX3{DF)d6>&f&A5`8GZT45{2?Hp z$38SS+Fy-~kTJ_VOHecpH(e=kfDH^<*S}v41rzfg=QQaJyXnx52i5bYFQ;=KaF^ww zF}hk-rr9AqJoD(eBbj+w=JL6ifb5J=PT?T@I5Mvb#7b4;dq@gMt(xNf)EVAw*+qTo zEanQOB1_rj!^MC^fd3^V$cso(M=aH$==wxV<|$gAo~fVIY4)oqGvDHJewc&UeEvXD z5Z!0s>je{)T%m%=9a;45akUUh^WFXwm`} z#)Tq24-vokEp26R(C?RQHd2eH=<9q|vWLrMp&(2a3PG~^Gjx9~Ec1J4AKiLnikeLg zb2CPa7F^88B(E0^1=qas9qo4$E1zp-xV%!=Z<2^(mPvZnP2;_O6)7xtrDs!!@sC66_6y*_!oHr% zd0>>phyqPEZ3}`D)Zy^G@GO&oVMIeMd;N@+@ zJCzvdIkr~~#$)+)Pg~2w(T**3*Lz+826<(DYDiV$y=;tSjQ@sy+b$Kd3_QyvVhif3 z;KJ$Tq8aZ`nm=DH`lTfOx#j%BgAIui*)+eu1L?S(T$j0)yjJYw@RMltQ?U89>E7xP zNiN^sH@_a6xcA4-3nW70>iGMD=@7H?`n~^M(yV;GmS{FVmc<@yoty&MeNbmwWZ-G? zz!x5j{^VQFDf63yakN2BN_)aYi_hOC*iI%o;A~s+P-09<3+ssWvE;**^K@~r7_Po; z&KM6@AJpw$N0&#ZSv~z30Tlfa%ZTTeJG}Eu8yA*`Y&WO$cc@)5xRnMcq@T*H6yx5V zayUjBMG$v&O$Sl5KY`4=)W4v)qm7LD*VIM|#I+3Jd74G;HuZX9EQ*pqgxN%H*I z_hPXTP5!+tO4;Un%0Cgdj$rpx&!`)S>Ywfkc>sL+up%9Sg>&afITyujA zAzn44r{M<2t?IH)f?$7H%y9XbNnig4YhN{%RfT}A2m4;{wSSaa(98W|FE&3tSd7=feXgR|ecEfFe=`4K#I^F840TLm z`W37RKMR&ne|07}GHQhvi9~_eiQTy{!b**3U1qgH*~=r(eSwe`w00l|qJYmi_W+!y z7@iP!ASS_Qi=3BT9DOug*o*0+otB;QDZVmLzD7%CHKv)PHLX^Dv~CEPn||fpzx=`t zGXSTdK5zH6BJoX^el*Knym9PR6zCT3>fnj3l7&hw2AL?$DC#~y>ouB0DNEM%`4=Rknk;%hF_q48wq1HaYcp$VWM1i)hM_e^i>6b zU(6p@Ig#x^LW$xyR<=%FZkEAUo@%buJ+ZYLLaF5unQm$0D-UwVr{p>WTd7a=l2i%j z6PsT@MJH!RFYZGlMfNT!Nr80*%z@``B{*yj3?No#yr9Bf3QM!YK9vqOUXa;DPB4Eo=MlRhx z&6cU(^mh>;>J#ii)*5c*+MDKR(xrSlK!dt=pt3 zY;sV#TsH{|>7=B7y$epJhMf+ta)4lg5LSc+K%^sJO?L}Z1~MbFg>aN$-vph{Fc@dP zqa1~6zmwW3dsmau&$IoppHOXn4%;mG%&x(9!ha;TojUX1Uud1c|kv1A9 zEm`AflvHD%y2zMR$IYz}Fu36bf!$lkRXx6XMj=Cfc}7WC>PRqSxHFUQy$EaY^xkvr zXO}{7&M<5*cDQghY$sU#{oO?ng5%%~)J1afZ1|_F+~SRv^P&wb4cFWYPSs^Mzn6G;UM49M4d|$9o};Zs&PSq-rzhlycB`4cA=awp+Z}}lMrL6QR`k+o$A$jT~P%t zV$ubmwPrwDrr}Y4{6A)XmcOU+q?a4qA~*b|%1D*z#`|cok-zLu8Rw**iOidQYh6BfiXwwo3zX9dUnWmC06?S>{>Eh-J{{1QESTv@=&*S`&| zkFlOyEWCbyOWyEIs;&k<>!Ea?(FC^pRv^wai5#(*pk4P#wf<{l*RLZ`cenRk`nS5W zf2fj2JJXlg96LM>6m>(bwYprC`^lR>V zcU%!{;RJ|>(Gu4kB)@cKY3g{{zNe4vy4zi(=+Zh?WjWEYp{A@>FKztk{DNQef?(1D zb1t%xY1A8Y(!jiq2g0i6$!4kpuGR~AUYO^qpN(x#TKHIe9&PCtybL`TsE<&qynbVx zrnFe4(&pVF;t3;-_%1|!>}u)qkipArq?&yVLx7vvXCj?SYq8?>cUktRQm^#;<$gcp zdF1?mcnU!A*k7zG;x%3dgG)vaQ-w>KU1d%W)~k=`0w9D!9UD$;44@mHuJ?*XT`q5^ z3-a3Rd}%yZ>I-OzC+F3l)Vc>EqCnahkz^X%EsA%jM6lfoT9V zK~rC|mqiPEj4V0!YW*uU=) zs(>!Pf$;$%4B zlCcbf!CKu82dUx{!ZH!I4X;e|BuHOITH6&<#HZ7zaaL9PXHJ71wZGj`^KQG5-TE90 z21%`|J#L9$kguWWwC2az<-z9c`zOd*#*KZ}d(EY-1Prv@c-*ikmKKsRm_hwK7bOe5 zSIgfuy`EiS7L3Y#fmuctXN!^5eHW6nLvT#5=u-@leU8Dpk_#n~uQHxU&AIaD{j^l5 z*#n)VD^3T5zEI8O5PoB!y0LlBh?Gq)2xWF#H$wZ~ZZVs#j%4DkAjUETO_jHc{a4WZ z*TYtn4VaUE&{N^h1pkI3nVhdaAJ@8r-DlNRXjv)qE%jl0SS`WR3?3ed6%-mxxjhF2 z^%74eu0AmI*lcaY;Nzfc%_Z^@+!y)AG440|i8Zh$&F+ zeB>M@N@iuqp7Rc$KVb^ns*Kjsug0&POuXrmAY5w>lrcH^WmJtHLXSMMY>vG8)H;fD z26CxHLEvzs%!xOVay(QjLI@CsETRA0R(qeDZfpM_*}#608#s;w9aW5JKB}i8zDbFz z52w&>M_pfe^)6-=d>$8YOsuGTifz>B#Ft8c6kAGdd+xkU3H~);m?KAUo1U+1cB(o< zq8aKu?&4`^xlhN6RIY<+S1%lYEIs|2l(X0VeJ}EmZfp^RN$zJIob3>a?(@j9?FZpb zN0fAPLk6qUWU2Sb4h3+#bW|8zuP!d`-U}PQ9kdzbwS4MW97G*6ORV%i)dQ^7^0tb= z5|#MshaC}BwDqB7@eHdSv6o)JTg96#{ydh2ZKewf)D~-c|Al|!p2IO_qzNjD^kMOG ztWjQ16tq)xMJmH`r|LeHLsGx~rfTVDY1ilGTHyvp$7XkOvlT+8V%+c!Mi`zzAD$gm zZO+%TO}w2cC}n>(}_oGIIN1QcE5hh1u6 zbm?)qMKWV74rK!|SnIn^ypVVZZP_RO6biw`%XnrC4?h@SG%NCI>LLHh`dcONtRb*k z1?8O=dGl>rpNbT0apd^L)asUUZTs{y90&O#wY-9oo?()9?UFa;WxHlYFamVpJIXvg zTvTz;+{eUnfU7KglLmYZvrO$jdGsO++W43R7z zN?>TR$l5+FD`L36+@EocQ*2Im_)uhr(AaB=G#ZY=;7-m^w0CpY!IthT8x^LZNn(S) zD`eQ9^&f43N`AyHos^h^G3s7HVL7|ss&>>xTtk2%jqV8#+>Ae5zXTar5o_iBA?o3b ztG(s;Hq_I^q9@etXKm?!`UrqHA6$)j&+6-3dMb8H46izTF_fi02STFkRac5uI7Xyi zUJ+yuEatFaXM&_|NY&(sm`H2Ac8CE~0BeI3oLfxiu(^c}9xL})g>CBAU|d2e4J|u- zCpuKz46OtWS=`%tdf?F8G7K4IpNFzJrGRc?EVtppix+_ zyfP{H98&6lyxWgrav1d9zJM?R_keZ!TPo*_c6=epmi7wZ?yM z?U!pcr1u-yzW5Y}Yl-L~90oCcRp%R3M`%3PPEoO2V+rVV22 z)sOQ#>c^P4k(1drwV}I0UEHMCbnZwFT+cXiA=`Qbm?D%xzn-2hXFr@xA>G8$<8pTh zn8J30I6*2YPSwGeXL0R@af(>nDZ^;R7m+kvhb}f|G@HAE)hRA9T~HFOP6q=>(l$!e zd{-uk>EWv;Oli3sBN~Z|1ndDRXFEm9HQ^yrYPtAxFx?*r$=rq-hh5he-d2VI40^l! z?skcbd-`UY3qoU6_QG~3w=Hc6S4Zz3MS>z}ibL&saS-TVkD{Gh^jJM#TwkrA2w>Tg)pXSPzo-VS5mNyihkf6lj4-Aa4128 z{WIB4$WBqz7a@lZr4L=5fceFtUa?M!k>5ChjclP>^m8c&OjDzgC7P02BV{!!nH8!U-cGQkih85x0;Sp28QDDtb}%ScL~uYPMkXNK5idl3Bd;)RSv zqyicI&AT{H?sWJDBTLZqa^-ZNqwXiZE^cW1GeaJZxjloT2Otbb<&a8Bk~P3l?w8X~ zQ&cXF_bfDa>&HHiRQ*jXKaG4xWYAQiAq_Kg^||^SWwyV+H7m94-61i8^mY{jJTTUm zaP%vnVEZw?xMh!S+zZ2J>~n5So;Ac|N)zc>H!T8oSp3X4rVq%tss9xPPe(j#?zbEh zV<@Df#t_?$rGiloPjrEh3n7yFepYMcv#_nfFLqJZp#lAJhBY52*p;{r4vDX|rLd|w z+|IP@>p91XQa@WBo(UKxHi=x)Z#fTeU=}gVeTwFIqwD48tM3x>Yo{cK|4x-v9Rzlx zh&6<{+5ggQ`Ls;~<)H6o#Sk)b?MA?Q9ku}3Ia5>w?!XVg)0Lut_B^S_{u zndfs_9sgWDUg%i!uB>2>}$!B$vN}?FgV;Ud-wW9jlkb)P)F1XGWJTq=9MATRfl~ibaM`0x3!4NAT)%a~Q9@PY!G4l> zMrCM*`}Bv`K7W258%CtC2zrm;!s~AjzOx?m3Vm6%!7rZrQw8q!EA=tmS%ux4L6*be zklK~fvG?7zN9>M_3ax%)YvJcC^@lbxw9j1tb-b?i)P% zo~zd+K-p0|HS>u2++dQF)Xh*_xw7?EqTgK-&!mot9PSV5c<#Xnw427OLPk36Ba zK3!q8-$@`N*7a?uU###pEomN!q(u(D+Y6Zu0r0_vW}omW?0e|JS3SE;k+;3lEMFHY zro?x=o_%r#`4?2`nEEBnnRK@nuHDQ0x$w7i)%t0Bwu6MzW^OZU>P%37nZvdglk(3i zz0w-99+l*~UoWo)9fz2-^Aq+43y8m7-U9kgBI23RbADs4ri=LL$lbQYRw>uA%h22V zwRy%?DlCh_A#uIDIdC<#=vdeag7wA|%U>k*MFja?Q99k}sB3~Bv;18)-U0n5JPaXy zz@g9ibi}Yjy76HYzyS67F}%26IQ?fj0K$T{zNXSa!=$VqchYUxHJ{!@BQKCn%8*)l9F3rz4}?evZulq`G@hMs{{#LgYD{#4DuB_Jx?lT0+c zq;GwVD0#Oc86%bkpPSniS~c^mu|D+rAU=3kG0gTdQ^D)Ox|AXAvU`ojJPZB-lS%2z zkGd&w=?$Z>Zbmr>$=#4%HC=)+DOC8pcWd3;_JKrt7>CJi6IdO7d5Ajp2_J7De55<+ zZp=W?m{_RglZmY?tM==C6Mn%=WfU{9MQ?$3ocibdIag)u!)h=B&jQl#UG!Y}-T5W(&wsajdXLxqLDX&mx5aC@J=nb@bM?MIm-y!WhS0Gm9cgmxQIX zuOjeBCV-I6AG`nk|4wTEKd&JW?%6yBCg0#h7;J!gUl$yAzWVZiFyQ|wjQ{VIUjgO% z%d!?tR$pPuqy;od5P#A3Xr|L^ZtC;+c^->Hm489EmmK_j7+m=;DKhcsI*|;1rzna< zY1#VK(=v#h8x<1C za7{!n-<@wSq$_W4bfDFXAZ)#AK;`__WLB{x?x_oMh3BY{*me!s=QjB1neKm$-8v5| zsdR)fJns9%m${$R>G#pNxqlNI1IwTexx$DwCW?Zj zJ=gWVmwYw%>KQnM_VWzLkUS*WH}dm`ulvkS!k91^N8_4Q^4zYUE*LM@!h-Eu{Vg#t z)3M|N+rewe>rn;uiEO~{Ly#+=T?ZW;gFBhk#f140iw^ofx59rKw6@=WuP)(%27=*- z!mX3}xif)$_%{~Bf+5b#(;=!GjiT{X7H>3w!m*VxoF4Q=oGcyif&XK6FtjwSWv6k- zW+*=_K5Sivtn27ZK1du{bT=rj29sx{^p69%*ZY&jjz-0%*qC#d!hULb~{XX9VmiaF{PkQnD+j7*|?`&lL@%^TF zWYu6O_sM9VGwpF}9D!lKAaJ6K2iutkVoH(ig7Oxy=DuvtHdFC{7KKYHE=bJjCUafQ zyrk&jht4P)jSa-6%cA&|ZvC*f=_ytE;ep@?smr_4r|AO=(R9jmhq<#v^-CShd6sP2 zJLq3bWwnL`f){C zE&1wd?dqSW$w=ZZ7Pq6*jN^XB#uE?VL1a|ls;;!vFYugCkob!n2TapL8r5!d{;g8X z$)VW zEz@n6uosDAtgquOB+9PDd}M4Gd&azUPh(nJxPkDGGoPmmwhILpRzZsH^H_s}mD`_d zTuZfy7y~=H`r**IFRb^)u5p2yU1q*$>cy+JVA@ z*K6d2ID=c&%0~~XqiRzm{$bmkUMRWR^j#2snFmA*C998wS_LxZpw{;9^A0E8lq2(G zsHO-v-_#i@g|k|zQ1Ax`7eOJzn+YPR);YhuJc(!!1h()o!*6>bAI!sKG7#OR=Q$Ka zz-L5jL0<(-bE*c(vVnFbQqydpGi21|`^2vX3VFY!6W_6S`!OsR2jnh3{<+nw(XdN9 zlkq(&Usyfe`_xUfND%@&m#A<%WM>B(YU~(l&j{^WSHcW~tZcybHCJ}?^r6%TM4^rV zN6}Bd$x4BYT5m#FIp|Qd=kQkmo#rtz_e9Ar0z?7-H^>-c%R)2O_LF34MpIRf#Olkb zGF||8bSsndeLj2}q60|%`kogWnt9Ckc7+D?Lrp2%pdpMp45lAx4_Y*($mFThC?9QU z2P=XOHfMpow#l3_t0pZHOj2b@*j(4?$h~0FgpX4x3ibPs6u3Xwcn1dTEGB&O!oq%d zzQ`UM)U}G=SS_RXQo9gUKH;R;+{}ePen~eC8)_bHRXCNr^I1x0H zNyupS)!m@}7ZfTvwi@sFapc@MfAvMKZa~orK?yp?>88imLBIKdR@J$4m5?ix!ebMC zt5~R+_4_)9SWPv}BY+6$;**s4ilZso#U#1D$T5LaEV84O(=O0bYxL{CAX+yj#KcqdMoZOt1rq9O=<0(uF8g`4{tBAq*@+&e zV^93Sf)`ZMv+y~mzKEzYb5>L9efnMhsdmbP8cK$oMTG@JlWqTHf6>7k=uw5x=*C>h zf~F?=z{_k1-f{O*-=vq8e|dW!lXEoR<(7Sxkpb$k-i}-1ZW^YxzsHH@mRlxsgD0C5 zrHr%uA6}$?<7hm3pHr=ne%LX4Rrpf=nxRh|I#g=T4tvP?A=zK!qDqQy&XKP&CN$aL z=!l97|3l$GpM*mW*^Rb2C5031rqjA z{CoejOQS|SqEh~BZ9Ts4Gm|oDJTcR?W*ozF;rMvXn1@+~|HJ)nb{JEIPT{_`c(l-8 zk5#<_)pt&pF?~ecJbUw}YHnf_5s_=}@R9%rYzW30lw?XVDV#43Y$usO-#HVc^ z8NGza>_v^A_Iao7FB>R-%t*W`{k?=#eDkDJma0#|h`Ac1emX<+wtwy-IxGQEsZbZ&S*xM4Ao8A9$3T#(T{TkP=6dSK%ZAh()BAU-O<6 z`8&ev_#2HT5$Urtsr>M7D&JMSIquIhiI=(tkp(1BtLBxa#=E5J+L%VoLTCa3*x%c0 zv|%{rMv}BM>1hzy0d$00tP*s;@JrWW@%6$rw|Gfi=guoSnF?K?Cx|?4qhOz|EX6;_ z=Hj@vI*`}VLY{r`)y@&ufKby=*JE%9fnkVEP;+{p)pJmoaT&_>M$}ExLDwXqolyk8 zf`_l&)rl1z-_PV~Ce!50LiZBL&_a{Wk>8cqxdoz?Zh@WS>ovJMuJ-i`Nzgh5E>LS=53>27pitMiT-aziN89KKxo!}D)AGpW+kRrk z)02B?6Ld1-m(m(AyE*n&boU!8Oz2(}$=O0V3RvE3Jk-DZetlf;OLd9qkB#D_g}^T~ z9aQYf#geQljI!L-YwY8fuk1eL4Ha$ESm9apT28!U0!7XGqUu_0<+v9(+^`1dZy?q#FLVy6$iv|qMpfpjchF+B3LocHA zj`S)>5fKb6h*Co6olul2T_B-@R0UKd^xhFf{(Iit*_qv){g4ck$&fP1lXIT?cVE~2 zsZ>$+-~r^$TrNBuU?V-&19kELEV7@U#Hc>b3gbY%D)`ZDEP7UH*c;3ld|b9<5at)v zjWuy4>4?{&tDg0W|uN2UmCd-;Ptr}=A7FsiV-$bP7_RQXE8 zK_tKzUdl@Z@qk{+Og;mjIT~G<`lc_r6uL5zL(u!G=F!|sUf)^!p8U!Mc6!t#< zhTZd)!F{hB;_@MAY>oJ7nL54FdKD=Po~h^2t&sQYDj{+M<}dwXm&duiVg78{o*!Ts zLbjS!?MR4PlYao6-jb@bOQCOF<$oZ0ndB1H&<8Ccr*!w0?3*Ru*3kseF3=qIPZfNy zJp!m~En833dosM-FjW^Woksnmm2+YxpErj$zEy&#c%|jSjI*SYPpdzbe!p?po8}dG zt(B%z40c8k!uMc$1EDcV)9b*4NM8QyG;)ISJ>uA2w#WGWA??>ZdeA2$=`dS>JY0~e zw78!w*}6>3;o7Th{yxN3MYqUF}8ve)K#rQS*1>~v}+b>*nf?Xptx z)*{NvVcx#&_LafQILJ{o^K`G{JB)W5%Y|9ZrQsB>%M1BJt6J@BE7NdOfe-QG#6jP1 zZ!z>*!olyG!ekOL-50Z!@3e|OF0ZUT*WcPlN(gk@KfOOTH6EV@SX5HnO}Vo)A~)I~ z7O8Xz3`S`Lchrq!SYE&fu2W zB6cSw`Q1O^A9w?z*P$r+07h)XhG9t^YSG3gDj~~8YMN<7-{GHg!W`HAu>5e3?p`Y{ zzkuRuG(NkahVMVnXW^*nT}0dTy^`3sz2;P7llZ^~L5AIV>?n;MnC4rb#YQOGm-+ml zd*7s2>NeezDK!7t`8xfHpUaB(6);hPvG3}NFYov;H$Jw>5qEo0A$YforGG82>Ppi| zdI-KeTm0m3QaZ1FX;=h9O{5nt{K(65uO~8&pR+QjNvHo!=zF$}j%)kdSNsR}<1S_0 z4*DKcmQ~rVa083gFU5UUx21f|!E(G?1?%3onFDLni@==UoXkH~liZ(Bdr1um-u(CE zvgkcupsdEF@Y{v!8gb2IQwL8TCQ4;W0jc%YT-8_g=eK7H2JPPNM!~2Qk7;Kp3M3D` z66FR&jwGP#ruQ?JLw%A7IQhR?3zeSeeb+DPKmWeBr+Je3$P)x^Tf@Hi_2<+*eC?@RfA;vh^}&Eh7tsipo9O>Q;LAA3`==k%iPYk3#7t$AX=r_Te<)&HQdsc0 z{&!EiqkDTL=AL$jLmF#tQ73$6Q@IUPfm|htry{0$0XG#AvJ4aWqeac(v>e;V**?n{(187vnV3grzZ@DQ-170QTZN+% z6UH|*Me(1>yT*r!Z+!(LG}uXb$L09{3AaO7SJV!>hp&H<@^t!zJaHW*r z*FvUs*|QNH)@8^~93z=5I@IXk7k8phj;eQo_M2(rjwfngPpZ$;uhk+_Cz}6pNZiS1 z@#G&unI{2B@jUB7?*UP3rTX6%=R3CYM%8+Ui0Beko;7UhM63$EzRj1aND%whmKI%| zE}vLNEc;drPAKzy8G?~WGrRq_Gis?)E#r3Q-RVrt?<^IkFm6j!5&T(J=zWU1KU0w` zliZ1ve7C9C&yFiFHRPm&OLr~3>|hYnfRyg6Z>kczN0IaRg^4Lp^W7BIQiViyFLSwN zqtOoDZUx(2MQXD4`xVs^oeQ`Vv^F`IOgF>5kkT@OnCK`L87nH$pd=2q`{*Ui_O zK(E~(pr0UM~hxhCcSJlUa7T36Pgr`Ufp6#EGj;sjY;Zqz( zZ~ycVd5NVXxO8AM2p5s#Ldto+UV25AXZHA1!d)59#UN)A^j1C+z;1lk(_LkFy-CBVne4V{qv*2y;Mo@TJFtw~@>LJM4(`0@>~ zucqN*sSq&OBJj`2Y5gI2<>V1vG#NZ=QAE6xJSu7j4Uv zZD~>I>&Uax!7rzFu)}YBaDU2btG;dhb`z>Ftm!)%Syit{ zSbMlhy*$0xP6GK8bOb1?m4lWmm{Wce>kLsyG)?^{LrQTiblR#JR zmVHu>m+f$uslYM2^aBAxpKl!dV(MJE+>6|omgAm1H7YVm)6}59i!&@i&N^(VI)y%y z{1bTrqxvdP>B{X6EbUQN4G%o%q+G_nE^A{Jf;K_P#8`U8(IutpevtPgYGry@2Z{0P zZ~GjqNCqFzO9&2mb;-=bEg0NVrKE zl6pQNIK)z=q6YG*@pg95*@uKlV+u|A5eIY5!JI2=(@P)bI8OPknIz&{#rSuY3Rk7o zf;jV&3)3j{-+*1g4xp%EB)!t|Xo$aB%&^|Rnb?fKFN|EIi7%9vhc-}Kr%4x*GvOK_ z-LnbP0wL#qTYaK=@<_=A4^*1k%|~NMSb3oy@>Y$U(xJLS17or)!TV-0!gx|O@GTw{mQyuX7TMymK?KBD$XsNXA7NN{9N-o^~EW&;-eDPHhr*Ey%TOt zazpk9tSTlgmM8F5zCS@@V^>(I1`0_t`5qe6e`Qkd=sD^qb<|A+?YluyA(yc(axn7! z@K|FZWg>Tu=M5)&;Um^%J@6Rb=vlCQU7y=v^wY=@fGxy^`SoLphZi8Oqq#JThaq$kY;HKfGg4n#?M^>)5ic?qNDfcu`|nb`aCg)@G%KGE>t; zM~wqGg!m5#a{GP!BQACOF|DYSz@2YdUnK!&y~KCfqlC-ZC z&MWkKrTiVuajM9;uYgIx?c8p@^?hTztXrgv}iN$>hx7W{P|Tr8S&X z6FM!+!Z#<8DK?wpCf-a3Y$>IO05xTUckokElDqM+h3W@6ctU&{q}p%C>6pCmm3@@-loCJl=Lg_ES##p12xG&5L@ZnQ1L zD6T;WmKQd;{mk(Wnu!SQlw!F(g~7nYHoPQ7b<#wW+kCR(U;Av0*oY-*EKZ4Bzk%GaWFNfEOsOtS)XGvl^8Cg&U`Ukw z@rr4TO-`UwhN~(^SnIh}kaXpJ-+$;hYcIazt-i-ju+s`fgQ2F~%9i)kQby z^{tJlHtEdEV9#>#hPT68`sMw`6B_8h+q%HWOnk;l-D*;sX%DGJWcbAf8TN6otQqu_ z^YW<2lZU9JY`o5QMl41e`tGcvs#p_RhJV}q%I`X?w*yy*ch;i>6Qx%Re7w^hy-K&# z(kKCxvpnLfrHENi)cKjHGxZ*E3&YuC?XaRIJ7Qa5Fb zdx}!)thuYfm+mKiSJY4cYUwW~$D>~G9@uo!B}dDd6h9d%eChh+)$3nID%BO4NJ=F~ zcmS_^J%Dz?Uk&ZATju>MXp?lv`qlS&;d;m2+JyO)*H1sCe7G5Bu*yRphC3&1`VtM*VFihtv^n_3Ul2lqnwBG`JqzvqATCw;tkVD6yM@QLJnQ*Ezo{cQcyR@~Kv zwh84}4=R(E9!b(+_ zb#`7c8q9SJh5#Q$uLdS*VuRLyApfg$I*`h?dD_WR<)`bgbp}fS=~S``eA$hAEQufIafA&$m=FO26r;{s{9~*SseoGfoI>ug~mZHO-ulpC?@xh~17n zY7)6@`ZLCF#pYb~jRk@1bi`e4lzFB>jj}Xb9+X;Qu`1b7A)Z_1ozcA3f=)H7%Lwpm5wk)c8Qc>A@{aFbY3HV%li_h4w0=|Gb zw?*W#k(b(^KkSRkrUT!xlVTVM!-b{r_a7@HuaLR zzO%stHRRo1jf2)0F{~Ie+|;?S|HV=1#ScVKQDrt-{g)<43)0Sd^I2g>Iq1BZ{$fH^eaFR z2k!p=x}X3zH(rDl%pw2^UCUy15ZyyDGFaNtYmPmN79f*>K3&uIiOIF}lE`%f9qU7X zE)Tfu^;FwGJtbQ1S_IJ?gx}iP-u;~kt3<>!M|2j4sc3vshnEiy!EFD`=0}$AHV0lN zBgBe_40Qu8`!zO&T1NFkr<*wcx!pj8aK>pJGawW4fC8|7v<%va-EXoikE)!C9EDT7 zm@*&-4#Mc=H-wxZ`k!_4$qb;6mnA$7gJI*<=FzlRX0`wXwezk#Cl;5;Ea{3@IySe3f`f8 zHfAvOZtFnasWAPzi|uBz?XHg&F*0oR18*#9(tsDZO9Y^{mU>3x?Lo_ws!>EnU9J`F ztG=aCR&t8)z&b1kBFor)`Zr8Zlf(!z9Iw#cu98{QcM?tncnJ)a_V*OxN+rj={VYnB zwg3A?JY4jI6`X`51ml@&uc@*c{qMO$J^)w$XMGvGF3mqzT*5K;>% z*q2E^OUWq#dTn0941?MoH5f+TJEU9*$x1{Dg^t4-l@?7m#eFnJt?`&=egmXAOoGr< z7-n0tjGtd#u**J5l`Zyhp7C>@Qh|XnIj%1N4B96-^n;L^GIFO+$F`Vw=Ttc@no?Aa zfwR&`UuU?@pR3`6l_hG`I>j3_&=+d1Em(=2{txtebWrLGzkRJy7X-PrFb)ENyFg%V z8{=P+^RmnzIc_}HTfd2cXbstu{(T3my?t5NRYR-Gba@rbDk1? zhlMJm`=QZlij>Y zn2aL1z;UILa~P?+J=m}%{I<$M+G-cw6nzFzDnq9L0 zyjcihn#Jo5HpHtjDG@@?$^qU~=(2+myDz0|V7a|Yp+Yl$eE=cN?`ul{?M##yd?)zI zYnBK(lasj81$n?Y`K`R@XY>oZ?5C?m#&XW`GFjDytJYYu(CMk0;=%l!M6LSCJE6C{ z812*2Q2CttT8tJA3$akz9xd7>gQEhf!;*YRsJk7 z5%NircQx_WAcWHkuF**MuYSb6nY+ajm8OjeT{|cd}}|Pa5)u@ z);_rQ1Fd^wsKuPV^1wB(P|va{=9f7Z-LguY<-b(srkRBzC8knmib3}Lxr744sDxnh zeW3tHmYR{|(08@gv}=&8x4Y<F%vQRSq;dsol@anBr!# zWiiu3+d)zJzmw%rP0mf@Q-hhy$2+D~bizrhyJ5$bO!<7=D>^0vr@L-T0AXbs^hm_OCFo!5j^y#j&od&3`>z~dJs{;q2xnEG&7{l@_S~sH5xy4aGAw7%r|hpSir(EBjd|HkS2g#C5XBz(w2B-m(E_8309c7yn9zS8DEs{Low@5baF zE>7|4d65S?);c#*I}HCf|7yr?=aVosdsjAF%lyT?-R-}mB`9$(<`-H0+2rx-`h$n@ zBDJM@6KjU#=2w?_Oaz&rnt_@`#fId>jypext{39dA0*)eJ{EI=4Fy8S8Ho!;9Jp?hCRO(Z< z>K^}s;Pp2#B)PYR)#8r_OAP;8k16}9v?p{qq~2CAkYLr0Y5nfF_2XviQ+o1I`vV+K zW6Yz`-9q*gPVPZ#ITXuZ(Z`@av zMm%L9pTf!10*Dzr%8(9S{sAlQEY)WLh}Vr9&*@z>5ARzF%bP_@(6;dt;S{ z8bkgTR96<+FB_&!O?&5C#Q97VnSZ~ak>1(Qmw7o%kgHx!(zlT?>SOPhOG}M(J+NBt zP@_qr7upt!?RkJiG(1{;Zsh-)E$^;l2x8TCM5Fvyo!nI6TBc23VO1>5;jlfXQ%_|wU7!A%*N_Uqi@(4nB zE4GBny8DqkCRz@uQ87Dx!tXz{DV5{&wZgovxCuq`7o5ZDsMOWKGefFN16OtuCj&Zk9CV{g8e-7rIJK~H&n{@H7kS7as2gP z8Am5<55aF4hFV*866_<34rZnNyr4St(^f<0@!beH2h+MBC2`3Gzi(U(9s=ag{N}Q2 z?IDn##c02+D5E=bWZN_F#YJO_=IXp-a|T!)k`@Ch zzw&_fCsQ9^AMR5F(T4E(S7sy7QQbVg>QcFUn3gJMwvufZ+BFcDL{!34fkts}dFno$ zqH`}}f&OJuR-*D^TH1V;&8CzgV7gW{*25l5mX(IQq6M_%N%aXWV@IX2MC$-jf}T!; zn&4I$UGK_AzfCvuDKej2TU`i0SA!#~(Au92Z!wF3Z_}!`^QqPN4n{*w>fhvz0+oXD zgAzTh=&*x6b3+VrZ^UWdJ~Z()OFWXtV`FQ?I@Y^Tc>DnOuC_!wiR_h>lfW4kOnm89 zG^a&C8K$`@#jtzr{KS^0MVX@LMsEbc#q?dS3usAxWaw!a#=$(I`)W1w(9(wm)wy2q@^nXE4>2P`?dBbw(qM z#5z@_3vyJ3i(Z&rUN?$yE!8X!V5YJVE!|YRMycdg-K`1=AW2$>Vit|K*kaGeCN!do zGViKMOl!9(U56{1YhGl1Om(#5PoW;@v-^=IrL}X7H5k;S`tTrn8n?l}nQT&HGF04t0=UYx9Y$w(1JtNTz$~ict z73h>2JLuiJT#mp^SH(9NYFxo_f?s>te2uU3uK=bo_VS88XJE4*FgAAS976!mS z-7zxDxt7raL!!&i5~h+w3;8kbIjx^GgzE-i*P2>3-6m>y=s_Er9KZ&x3Hpsa26c^U zJT2)2-aj*gRAd{z)7tcjV%l@kAG`m3_YFmcjToJY zDvvRsp!fM4qn7>Xp|5IiXLSA7^5E!ivOHEw&Z+D{UJ!JILxjn(g8`0G_9*Hp0=M*B ziel1e{Vn+uSki zaWcr>27Vc5K9xPFta$oVOwGbc_}Z;S*i=rZjj$vwuo}v)qCIJve=9B0qSB(e{}S3m zR3rrj)E)CzwY-Y3xhMyU`RCO>R!n){-aMoXyeWz#;T`77oeZFIi?iBfWJ9lS8-pli zxQ=b@M@-mE4kxAjw>*bC;-{8ccOK9+Zm(##n#X=QRQ3BD(AnSSSj0n1dm^=-Vs$sd zIE2PF8Cj#wZeiHd5~wah^Wp?0;3l3O7CeLal!B~bJ8( z&)Zih-_)3T$oOi_<04AA7j3hh96}=Jjo#9Jd`6ic`&qdtrR6ImaB&TmS(-f!Dl;PNXF>YH5@d(IeA_`-;w;=wQ&E(F;A?n zsRtE7{lLSFG@f=pDM_Jx>zkZsAr+@&4B=bpwD=N@OfZ2OS) zp~Gh&K{l(E3@-PaTgDQC&cbp<(AJzL_Sg^9Aw$UD_8D%C($OXG+lwf z=U=_8y#$J*UcQT(tJ2u?tm1tTp+oZD^EO}ASiV+kKTc82J|cs8UU1b}gQyl>&~A}b z!h-bq7-m{4Zw8+gf28bx$82uK=6X|Ovx1sH-L;c-!g=SHM14{CGaq~Hm5-5{H8^K>jaM)n?8F@ ze%wIO%<+%+gVsxSZP#M#7Pl3U)3ZR+5t%Y|Z3wF?iFI<+_~> z3y!Rx-0BPgcj#$^D#O<9eD?oaP*M%U*gzuqd5 zmJb&^9)&FjaE=JW?^g$})@${zVt0>uGm`?BUcn%pzm;@*xiZS%-&ssOUdG>JF^G`}zPxt_(>&DhVbySD{@&Rwzo^+x3EyVsmA#;LLNpulk-ryd$NkjDR9_y`PW zs%NW-J)p*PjuT-7nS3q7N!7J}3EBi@Yg#n@Qqv2#w$+n63g*jCS`_ktZFPTR*IUe9 z{EzK{smepPn?&NHfpvF{vhFVSG8?&SKo;?)D$PSr1U5m$#%xMg;ID%b{9He_EN07* z-P>+$skYR>`A$X!-+%-8!F(wj!v{?0-jz?JFcG6xzUx7K6b3x~lGZH8=&mhn7L}j>`reYsFQq9J=HJE-vU=tl<&pCpER@I){N>l4XlP(3{)Yy z=5YoDM%rW0f;uVM!&wM!207D8+?JswYNW}gb}RF4QZul%L&Mv}+3tgS^9gV^RvPw2 z{qfPKj#gM7*ihZQ#TPdZG`_!)01H|)whY?5(`S{WAd!m)i6aN(&|ouD#b?rc`tjnS z&uAK19YB;W;M5+R*g{aHu;<3*<}j15j6Yr@5Bev}M+x@xk>I*!Ig|C;I9Gb>DbVje zTdHjDw017mNfQ!zYV<&Z8x#vgtdbQTYU;c~04%Zb;7n2KF*vP>nJJvEm#b}t-`Xx! z`L|DMQ}S6wOFYo530SS_PsAuGkfc;oV!NOTaguKl4$_MNH>+ZTV4_Lz8)rdvta|u@ zcM{wQJ}FfEKi`#P35j_OrP6C6;3sVHGCP5b=(L&7P?X=mSc&`f-&TIkd=5)>IO5}n z68a)12&`QdPxsSOHm+Y8Oy|ZGr3h(I=&YpW>FbZ*HtIj6MMrFmNqy_eBeXfBT64b2 zHs_*7khQ*5yGNjU?iS(Yo3#xTazJrNa@tPcOTJ>%b@S$p ziF`+PIg^hM$$F|oDz-suI3QA2ws_6Zb>~W{zqo zrL$$@yG4RXzND_1w8uZ_rCF)eWav9B<7E+{-gR1XY|vt1g2u8*x9510B2C(WEHDLB z=lLm&&+mpoAaOFG=RJ=^uI2VF{!k`h!<^+&PS*+NX8I8N{qUND3e}!L>J&Gj-XS^H z*7&=x#ue`jzMCw&qxep;4-$&Ef1>=FkCt*Xe5;T~gMV_S!dkhV0lw5y@;QeoXnf_z zgJmXc=(Hbye=p#LHT%jC6uc{OO-s=;}rkh4#pq# z9|&Bv!7G>I6CIkciLOrKNClE;BOZVap0rx_nK9HkYnn=F(Pzgb#}A8B*Gc#(-ZCa| zBC)Jw93iDTMVo9nlL~@xhE(V^uc28c+1hT84;Ovir{Zh|s?WYaRat8Sf#4LXkB^$< zyD%;Po+@eo?!5J%fEfBHb;rz7ju~h{aG=Xbh*GUzez(%@Nf3wj9uZR%E$hF=VC~mk zSD@^iu^=ZvI=al2ZbE`_>f_x9VBZ!Fl9npkto`AkG|htqBTWfT(i-+?eA-M!dT(9W zf^^LNS_m88%^AGZs5d-PStvFutHDWK1|eT4g0I%|0ghP{3l@yV<(6YzlxkPELOSJe zEsyh*Nx2&Mxa8KMEK}CZbE#0}HOtIF8P*IsVieH$hJgW>2iX2wAZR*JVTE-)Sto$@prnh>qZ zJBC8`*$CIKp-F7lY7RoPiYD2rjEH=IzLc&9B28IE4#JTdT_Zuu7fuJWiF@0sDw*pHHeX_IJs_uDL!0DoZJ1;@!_V+W+m6Ue#vf{} z9ecFW(p$!47%Uol#)kK1PvL6S9Jp0xGPcy|hx^du)yG`wL%KFE#Zrz)DlE3_hPpr@ zd&YW{8k`piWm*^H1;)Ie#%cY>EMueO9)>m!YWw_ZSK69dD{ZhK{{->zsFn_3=e9Y? zwlpoJc!zzx%usBYF4w{?f3eh5x!0|+)blgl!sxspt!z}LB1vtoWblU%5h+!mv`&mM zl%~<=yfB3tB}im2Be$|%W=4|5FP~0_H(|yGppi=FieFe(uKRDUsQxu8Iw3DPmkh?Y zn1X$W^bfGSg<0l%Y|x9xO3{>-;>NZ8JQrq%u{TqE&dq-Qs(rX~SJ655ZKun$(T)P^ z0luEYs~Kax&B!7X5(H^9kj&)lmKWao6UL4*GQPDVTW%E8B%<1Q5%%j^$A?d0+=;=( zJ;#-cT(TdsMBgU5%30uF$Ai zoKPX0K6Au!BX_1kdp0RkN@HZxG$L>2PJ=U7e(96B<{zsn-RghMNhf$B&|NlFPE8)3 z)I~sBj($cVh@3OIw|;LsKQsTEM%=I(;C=1?4$cb1JeJE4)dT-md)H@4LT34aCzrY| z&E%=)GaqO8z6^XnmX~1%-u7FE>|pjXP5$x?3mo_7Bm85J2$yC;m}ymvJJUbmYppkZ zzkd|%TJh_lpH?%B25JynMq*w4n=;0Jmp$IQP`5`~gfLM#@Uhf2fWJ|HiX0&RHel8q z!1D^PGn=Wr7dfuj_EneeT7Ad;4t<1^FqC)}hD-O=%~zEaW*)qO<{cpWWPOlSbTR$s z8(9!2h}O^8!J7V@tDrze_WJZ!4(=RvMKJ<2v{(kV?0}%q6LRi<7IIp)3l{nq-HY4| zcME9Ap5by}UE%HFC4i1AVn@C8IX8{dpnzT>VcNsNc5|lkE{f2_bc>N1?)UVz`{$7Q zmgY$Kn_?k7v5AhcwnPhRBmue95%?#rxawh|Z$-ifvHcq@ocdcf@5zcwou~Hw5)@lk zpD=)1-eCW733CKTK@sGE)dfppX`9qbANk)n=XdYw2AGxv`K_7>^o$7e?Ne`_~_4%*+v!$ z%IBa%^IYkZmOtpU2ou&<7Xpo%ot3~D(XHBhQ!D$;LYe?7ABNNXvgH&K>?|;pzFRNK zXP4i&M%N9i8er{b_;DACMB*(TJ$RxfarR+$zXEBT(nv4XFGVsjlTYv>_@LV0e~mp_ z<4W`KcZ^eN$!$CKnr6z70xJxB9HdIB;%cJt?LbtILo{?~ww*?aU7FxFDIiBmo6IV^ zk;1@}mTD8A+CQ4nVV5|!mLd}tSoKj<;~4+lSki$J%o$u|aRZu5XdC>xlDTEeKcd$Q zFn}HWwKebjFOrT0BI)!-pxPVxb{q(#DS_KS|KE#%oorSH96iM*6vPU;`2V4D)2Ps^ zmxfw0JW6UW15@=Q4$G8@|AA~u;K^%-ia#YmWqsyMQ}Be78rq-#s(V$2Ev+Ib_4iL} zZf!hy8T-6rEFA9e`_31pT%$f$wbGAO#fQ!cLYPYB>ehe6vfQyPlg&w5Zm(&`$))f@ zD7ReB;I`ih>tI-tUy|TE+Sm`=TF+0)zj?tn449JWK+x#0N%OE1KsX-23ceQD-@T_R zS@i5bkfjzV@tl2ING23RPqi5WD`86BFn<{2)9|ba!INMqEN`79{5Xvl$__;ml{JQT zx=%`*IbX^=lTX&WG}X0w^msR+7m(K|F9Qwg&(4QSc^le3DYf0FB~#ntim7E9Jd5KQ z*QUTgn)tutxCE5nJnj#hxRN?{Mc)z~Ei;uQuJCU9yaPoEBC+8X#S>6eNXd7vbE1Jz zmg5s5U{{eNse~^Y1ZNrOQ|oD7caci!ajk0MI^GnN@=Z849?tvAZ5da5qZybXuxPH1 zGnqvxiW2Vg`OWc+W80p1S4sJH+37YqLn)=lLu$T(`sWfg3gWc}ATo#Svg z%tT&_tC6p4c?V^zf7TIQa+AuD)zI|+?l6=0tFbc66WlFFf7^O?Tg>OCt2z)e0j0{& zV}s0o_Kl>5kvA(VQ(Iq-1fSfn!!73i=&ef%NtxM6Z2vJXe0yOqsSCDv?iSnT7K#uf z_Z$PwIu~0G4pHk1g84q~EG(c3Z+=Q0CFkZZl#uAo7XK@9qNrXx2-eKxdlRF};~1u4=G z?u9E32#X5FVo@Ewm?0=>{=WfaAx(#1F!B?e;SPQS&3kq z?;s9@?gY1CCGSvYXt55x(irPBXaDlCr;qq?>Q>kZ9V zF_iOsD*EteLDgy6sn+N&3{PYUuDUZEY6`8S>%BZjE8KYFZI%wud^HUxWC zmQ!(E#=DQ7*}q5ZnRqPcTF<_o6_y8U@-3f~v(X%CY)ne_f&SupFI%UjtW8f zF4xBF!@c&k+0SFF#APfcpn6I2t>dh@ z)}{%kya@^Bk%}D6WfiQ^bSeq`!HN&m*<>69$?drV(-vG^MD!}h1Ip61;WQt`U~qWq zmXW-_5X~G090@JH2VOiYdYVB?-(C@59lFMLJxKBgeWISyD6l1+BCbA@1%jZjBu|i% zky$0Giw=t{I;sO}C3N~J((Gd%GwJX~>7u9`$B)>pY=RqJ-ykN4X4`H+Py-MoEGh2AM1t~I`_ zk9jy*QkuDRiCapfKV-j@f-g0dXqh6wDYI5yYW#CGfwNm(`FKRTDRn$jc7lh<7cQh% zpZ)1xZ?uBUmC3CS@ddd z4v%K%lic>c>3;JzP{iv*Vp+!hS*NW}HjFCP)@6=#%y!LHN2wO+PY{}LAP<{CjyvXz zg4hN(uS6y#Q&$io)BVUpUZ0fkf5L+e`b=(FLqMfAi8rNIsh5nQ25)P`L?tU?@m25D zTFs*gsT!_%^GuQBKC8?rS{*e5uEmSlY-^iM%i+J(T<))(hJB(AtHUWggYCOM&&GS% zy&j#gkRyO>>4lGj79VcwWy8*!Szj z-D_>q@bJ>{ZNB+RK4jx*WiCk3Hmo(xIjHhmy-T;I_!seznu*+)&W}%H<*t?VmAeIg z4rmKBh`+bD*GOd5D!iSU9SOyU{386CpZ)!`dwuUxct1|R{@^U}?!Aby2NhL?nvFs& z_db^0LfyO;yN3&|GI9DPLiKBS%ez$7=J|41W9t6aZ|?2$-?mH7Io(1Z#!9}u-yT+; zestR-{M$I$aCl@*oWjGG-5Oqd{JGsPKaJ*k7Oa2#K1Nwe+VLHJTdjB9>T&<~*Z~TH zuR7dpwWd!Ioy&A3x`o=ZQ9LlMtkFCjD5PU0kE4{MY6UW{I14y{q5FSs@HR1MR{v zdQgbG@PN~2+{T>datM2prdv@6e+|j(RxH*T%2nDPD6JKWNY<-*j0!o~Ftc|`d`+-L zU*@+?6luaJGSU`kQq3WUcgDjey?2LDfnRT4|DtJd;|uoCvhGs5q%VZ)P033UyqvnL zw{Oc&MvU2ipc>2fe_uVr_=eN@aq+i5IO||;m~VFD;xXPrvsuSnO!=wZe{7;LGI@rJ zE6=5M`}j>Y-04=<4c$K8XXX*?)gkE^$$K>7yR=k$HaYuS2EpbVO-i~R%Nc}V2z=sC z&zDbD3*nz&}f2YW*4|cV-HrMbFiF z($n(yEZ^9pg#-pb&MuXf>S63nBO=wkkrb{F+D2+=DJIVmj5mPMGG`wWj|L;9&kB(A z+t}Z_j)G6j=a{t2ZgQj^aYfRa#G-sd(HBz%^50G%Sd+YL*>E8`=H^s_x(NCJ%~9)H zIoU*(4Z9sBciD5NO4dFKo2p!qm_UCGo@_LeVFpng#KQ*oV+Q_y!QGMvh*5nMtfaV* z0k}ZrL0?kvwK1caQjKg7Mc1yI!7B&0MXu)Zv}@*Z@Wso*Us%+~GWm=Pz=`?v!|GN{ zq!8#@YEkKsq3HZvP2Kz>wkv=l~^E|W6U7+iVNMJ$$6 z3uQ_J?25ObwBNy@2m0nFz9hMSbFCd#O#QpgtWR|!B>&hCF@_!E!Pmt21Z5Ahn%MHY zes>+W zCbnB9h*vhR$*TEjD+_;f0ec&Bl@{w%rbSKVT88$D>&yK$d56Al(^4dfVTXaD=2Oq7 zzpiZn(22tc`+9+|DXUe1+4OPotYUkP53M3S%%9Y}0}rL8WXl|7y;T^&`4+|% zB^pW!cCwR^B8fl1qoA&GEa~VD>x%yceLUG7y?5I)j*Q4Y6ENJKGFB&GF_6vGo**hM zuO`g@fMoBeD`*{SKUN3mLfv7Uwek`uKIdv_;v-X(7;Y`eon4Fg*pAN-`x+4$%oadx zBE1HU(ShV`nBFp6fbBU^Pb`CGZxRISs*=R(1K%qe43N%T-*vTtBP5=(xLeD&W zvx~ki?3Eq!L$krc3P!xs#H&N%SvI4nT4?xLe%Dr|xRyHQJNe1jmd$JZSo2|m;C92y z`+puFKXLpAGW+tNDKvalR(!sjFO$Y3!WuoxiI9->;==$8-svKmw~U}lF5fVk)odDZ zD;};QQr2Ir1CBU2uAQ}g+WC6UEWKE_Oao#6Qu;8{Y}NSbyXc6#tP_`luElWS-YeJ^ z<+XuhVa%Hk&La&N&E$(Ng{2xjHGQs=J;M^v9xna7(Nhn9*Z=%qg!;ltgWyzmUvJle z(Dwe%dY{}sA2d_{n8jGeG7_=ON?lUNW^X)~bCsqS<7qM4_O}Rmq`T^DN3pG*Hp_yF zQ8o5)e8p-7N9x%*cPf=Xa7KPPJ}#jTV;Yg_2pr&vnq(6KS#ka~%YV}04rkOY)FUWn zBOo!+?xl6HIq#59dN2Fb*^(VusHV1Q+3RK-0xhjUA0JSHx1Ka>`1XC0nFyyJQvPU$ zlR~^n3zXNa3~Ku2)DaS~?V^I5$EZnHex_BhzAyc|$U{&eJ!&cay>O)AOFwTg`|gi} zC(QmrosJ^QnTZvX)?0<_-n40LdG^6D_S0&HzW4oH6C~*HUE`xJ2xY4L`5J&E=~edI7!7@9$knka;KNUCGBt4jD+k-C1nw$rqE+ElXu;5fsCDhjznbU#F>13`=R>9Atsz_~ugN_pB@EfZ?FIJ&pz+++L@O?o&I}9oIV-duJ>?q+6SCAn!mG> zy+a%5eexf`B%6F`U>vfDmfs6iObhb#OFe(HDu;9{()(i{82jUy*%v>{Z#D+Mc(Xse zENbA4q;J3Ycz#uh`819FLED<&3#y5yX2){YR7iy{&_QPMdxwHlJ9k=}7a1$pZ||?^1?hPC z#FM4z%~=>AYx*UO43P^R5g3mVsI_G^$r0p$DMr5^RA?NFCZS$N3AhOTuKH%W7(TUD z?X6oFV9--Resc2iXRO0sD)0!JnZEt%a)-ovuDk1ZWH+YRlR z4M5M^PW{K><0zFI4Sa{QI_$TMy@3wA5G`gm;5izaUWwla&JZBNAqniq&Xozvh@4%Z)#N@odn`vKKg8|mc@+Jn;FnSpS0;`pX#iRsM zjpCBzH*W;p6l*IFFIm-$+u7+;2w}!#Y(H0LXxiaWyu@Vm6B*q4DR&)fCP5i~-}z5< z%6#x^dcyS%+p5SqefVDFSB_?72A0^7pW7U#QyS8izX#WdqW>WCa#q|~U~0t|>3L84 zt-UJaZRqg4xd>GcSySEcX+Gu%9b2BFhJ%@yHdgp`FCvsHWm7~ ze4w-(>X_M#X7sV3rVl6EHjJckqo7V#Fdh4Qzkb9{&rbWQl<_Mo!DeBkN{Et1OH@Xb7hkeFhFCJ9+@3oWTHS?9-K)iKenY2Q zDMH}{nym-fnetMXA$7`Rm8rOoP&EROKn&bc!}_5^Jak05X0fBq*2$PwtDs9lj{riL zoTBN@{CX004p9nuE#8;1V|McmiR`xg$E?_26JFfLnnygXwsgEe z!vPNW@%y?`zWOrO@mE2iSxYHHw#$cG$~+tZPpLJYE?6MW7t3mB*p) zcssr*n7erLE;;CN4Cu2N1xj8QvyowyvULQTec=T`s+=dC-aO%?qlE9m?}!l`ICHJ1 zsFB*p%s8!XAg@X}{PuizI@BeB3{Jsa#o3A1FZ0hdti~fD8<`_szS*4ktc_R>Wlc{L zMgicK^<_00bmwNht&awF%l2*KICI#-m)eQSULGgjwq^5Fqa70V%+NLM zO$55j2lUfe9Kt_Q#drpxRziJ6o=>>Rs0&|C?n48*i=|w~{<@Fp^5KCSdQLFKoaW2X zncO^E<+-RE(UMB+X~W-@O_M>e+4gr&-q1>2R7&sicJ0qann8+mi@x=bUJ{~YJzZ+N zhT_BRV(sY0cW{YPRtNo~?swcQXB98Joz7fxLL&u_CC>=5=^$e&M9ayVmcYPx+sa3JtD*TSuLnS4h8ZV>b!;WFiPt`ZE zIwvwKwvMQ$6*b}E=n!=e$Ul>$k)Vod{x9J+rC+;uow$&cV%dDMfZ2Zl;T#%S$jhCC z{7MrC3<`-frnd1d$hKz#tRGu!my^BUMA<0j2UcaakX_RjkK{#uE_ zVc7yBFbr93I-a3Mh=AWph)mGOS?C@&PloK=CQj|bP@a4c4i({6bHkHQGoW+XoMxQ-+~8YoN9-1@C~SXuL;9x))t0s#6*YU)hOezwII z6pEfj`}(Jt@;nzeFXKVW93+|DR4)3bW+LPMdDw{=B*&zzjhb$|gfaDH$DJkFsL!k4 zvQ5ePo_0ku>o-}aVR3i;Io!!eTK%rF%oo`AHw;?TaaH>o`h$sCMD`Hc0Bn>-$XS*^ z@*UF{-s8XxTR%OEMvyy|dllM!4Zu~`>oVK)zO*hU=)52h_>P}=;7yHhKu|{;Hl-!e z5*w)*8q}-DNGB2kqsrRyY=LWr~>xxK99Vh-y>lB<=sa)OD?1sV*D!eMM? ziM~qE>3MZQX(>a=W#SL(_D{)D9K_Zqw;cE_XfYWR;?U?-~|kBW^syg#F3c0tXh>-@}#|fkjr+n?ZvknvT{yiAfRa=6*=%er78*_AJM_ zh&eZ}P#Y4>kJHvGO?+UEyEOyL1A#Kf4#5B*(lHEP-8K6a7#V53paZ(y20u@=<7vOk zJqZTZj1T_#FzvYv&-oKHZ06)lcXd-CPWxNxI9OeT-oTa;t}hPc%&LVTiWH)kLc}nW z(!ptFLw~>LtrovG94EsMqz?H#O?4#=?DZ_eUK%Yr_7D}*#x*AN(IeuUO8fs(9LDx%*!^V}1Ok@%_u_ARD8Th&^OJ6tJF!$F zq_^yFfc3AE_~5cY7bBv%S$nh8ZeZeKvi3_YI`%+{+&SAl1Nq*2w^FKRS|;SEo`2WQ zijN*ce+8b)F5R^0{ctNTBD5#YdvIZt?cSd2@e>vW^!4Uj$uq?XE7m_H4UVb~BtxUy zZ)?mv2r7y*#RPtR$=M#)o#bP%^^nFoEB{u;U*YKLYe~lX>-mz*nynR=9Wailed`nn z4Qv*3z5vhGl&=_r|E^J@BSaW%*2Qn^zV+%%@z|&vMhq(UqqB2Qjb!irUc(o|i|w~~ zgUdDPE}_2)O?+-$(@Pg_Zn^wd-c;H@FGH-LkoWr!RWn@AE_Li;owl@JiEHFXLm`0@agNv!!91>{lB+M$iVvl0LRQdE!o~) zd9CJbt0WfAt?!)%b`95vZ-ZS;rP8l5<&b$jdXJbeM3zb>AHES^shkjwEsNj+|Fn@c zmDYnN*97&yX^1txrE`HPSs#jEq#i)S77HUYk8Wl-MezpBw+%c0mN)O^X(yP-gXNj2OWt`Q-FYMPVX1sP`ac?Lv#nc&JypL#sUUv+- z-sVcGp(KX5RcOILpet&d;Ur}pYQyQfz^WvxdH4f3!heTkb*U66nI z=CMPik?U#C`FPs00QF%5wq@DaqlV!m2Fh*7uV&s}X^o2J`>p}toI_b~V?l2^un}#G z7GpYapLRvww)=(g>sS-@G*~UN>ZKzIAAznc&7^%1y^YN>=Uv8NI9BY{@&0HXISHFz ze@WkNg|$b%+85p1vazU04*eybw%z}pexTM3QNF==LruJq~P1S8p zltTQvV_d{5Cy6yNRH%)NkLhnJy@MyiEo5sbMci^$udcM z=Jg}NO9jj!3^H5xA+8;pPw)4kK&-*X;1vIGtJw7*a18}wFMm|=x{O)OF(-g|v5&Oh zw(uhNw(8Y5W2#bKR0Eq%Zl`MC;pz+jQ#8ENc84g#_W*B(kdN5kvTpgRd*Ce7%<_(F{pqv&i$&oJrBHWcd7>S9)v5 z-|#`AWga`tE}TM?S@($l^=d!x`*c(2bF(j=o6mA8tm$t4UD4o*LyEQaMh;F)oayLdg$Kx`8 zT3YQbbx}J06>a1HiM^6#?=Xi%5DHf+rnW)` z0eKKRZjn5hm53d)NYi>CC0vb{!SlJd4vA1Hw_WF%1NowZuX>fDWLXo@dIrs!%JhIc z(ETG>R+p3nypI5@BXVmP&A<@ip6cCY&n=W&UPcvxC+C+b6B#x_PWSx5x5o0Wd{Ip8 zCLBnMTD?5?2m8B*uTbd^Unk0iFjKO>$UcKzM?0nkbWOA>1I30+yxpY;AQilFR&DIx z%32N{-S;~#qe5^$L?nBm>2@A_CRmFAXvnmwl#O9Ov3KS1*0wrSh1*a&mU@D8ggbMN z*4L1=Ra<1fnXa}mf?zq*^KK9Rw7|mZ6V~iTe2JHz4E+d=k+Gd#dOvLb=_wjNuJ1t8 zb>Y_hQ_dNG!<-33#N+z!zgXkzbbks+*6P}o(G$z#Q(@LDRn>}NZ5`zeDmg|`Gqolf zMSM~nC=2TZanHvKUyqH+z}iP9^s|g8&N;3z=Ik;RXK2<$c*2%xcHi{NG)VR^iQ40= zK_TYj0is3z0PNIiR*_Q}1x>H{;P_5;`{gk16~L8DBR(zRdK9Nlc~b6B2_TEotp3ek zEfz7jH&JQ^0}(ilM7n@-7$3kN`-(0uNCW;de9=*Hp~JZ zT>(8_1ZE}ZG_p%|FS7?$EQTo>4FWS8F9G=Xo`ZGg)O&p=000Qe1^ftPa&BfOEZ{I^iN)E3UcXQtNpNGl(e(tPFUDN*a@E2(^4$EnGO zL?z%`!hH1=oApl)CiP=SgXDXe|E>z>4!!?jvA>-BXOlC(j-k=01GyqNJ% z9Wy`8{7vhb>>~22-RHdRA+^%gN9#&Dhio3Cfb;az+bmY!S|kgvk5XpaTpc>6O0JuG zyex7&$>D3*k-5mHO6(FI2#yf^20pPKP7(?Kit#r z#hRimbAV6w*!~##yy5jrlv9u=rY<8{!){2^aqnUA^TxGpf%^CdJ5LNSfSUE38`SCU z#HUv>i?6rGe+KIXEb%oK22u+UEUS~Bq5CFw#KDe+mdRWvRmH}yK$x~7e^PqZgumv{ zN2-LqBc>WLv1U@IT?2r`MkphX`$IVP11Pm!x!)dW{<&H#du{Jku7R}cceb&fPp}q7 z!so>$S)r$j(I1f?T{%ugm0mq_c&!LIz3pdJerB`Wp<>71JSESBcz(XU zU9XaU)*BYlUMK6(&p)2^buR0P^QsT>Fz2K!q8eq(FbSpED;>o zwsF5-;f+eK`813}B^tc{G|)caqag*B4e?d=meRP9G#n4YD$t^DrST~_@5hDpf)Z0# z;V}a)wKVy`(^!_(IGz!k}z(;1bjpM5FXw@mxc~$5wg~r{24h5}2b@aE^weM}CT<@he z|FORMchSBD(U6wrRvHDSQy+V2z-Q$|2}$ht@(as-jDdUl-2|=@HWdhb$bcSCISW3nm9YPA zIVYFDS8iAuuE&q88dA|fI{D8X39X$Q$G8Z=v)GH`qV6X$(&gZCE@obMG`=$o9DPSM zV+PYWv2)fVM4C`^dm<)0tZ=+EzM>AnL~%8 zD@zV#qT1`3T59y=@i^_edNk3%y-CB} zRQaP+my)%^5>w(wu?l1P74c$g`u0MCQ(A}9pvw_o@7YUyT2*sBTK#& z0I@^Np*~a88V{^mUGwUZECDl9@~BJ55ecKMQRUzfi_ir4mtW<4f0se?+4TBP0dG&o ztvLPLsnxtg{r-eS%t^n%tBi26{d=<(bx=wN-7itIAr}XhoEPk_D{%`kvN;0@)Bsx@ zvqFxNPWBC;H#ge1&!iwKE23*>RP}q48jO%IlPhl{IL&?go6%!{%M;`#1Kc#;Uz_Iw zzB~LCa&AIxnwAC`WyT2K9xi0m#8_|=PfuuzI9I}#i5I3Htb_r?_q6V_y-{cF6YY1> z5=nR0T-V`G>BEEpDF$k5Pykx*LEV)m|2^#v@bw!Jinu{wE!Q?K*Yj~PjHStMV3aGI z>4+~aSLXIDS(-%U!Cfh1!E^^@k^vAbs7W#?w$dWEq8u|{-|88pw!2FrM@`~#5dPsg zrKqK1;*IR@3o_=*atO zY9JtxwwUwkQ(%~ZPTdt(8ef1gYPxI*#=3wfy4xi4P7h2-zP(R@)$+Rk3cK-)kf_ft zdLtJ2S|EJOylCVw^R-xCZi~5PmtrK?U3Nr){VNyzw^EdCv~;NBk7CRMRgwS;t_Q+4 zOz75bguVegId*p~64vAp5Xi-rM9{UeBIUTig)IN2QUH6G$rR?V{8%sDTASQWR@-D5 z!WHv;5CR4WpDoln9VAl`88aJA>E3VF*(5yB3^SR;wD6u<8ttX3`yIx{*z7z)oJYlp zq2d#r`fJG$42}o-SM2VNkzjgVyI6$`Ura5^yoxi*5KGi&fL{*`Cuu&KX^IemP(#hd z*p{UCj-OQtDTxY>acMBRza!a*Qql_w(Lm=Tmy*Oow7heJujjNFXa6N(2pw(;Ywm+< zETcfUjtmQd2 z!P}Jh14=one96wpdr0k1(*SHtey>u$(gD}JJ_yTAx`atFUaoR2NV)m$9o=$d)KU2| zLCUBLu)Nwi`(=?-U*%@)H1g67T{!H`Z{%gI!o&VNLec>C{ry|}@gJ&FYm-79(I*c= zh0>vEpjOZaF=EcGG&ec!@<|g8Yw`Z0(6jV_hqD%jc1m|~ z_a=BGm=+H5NK)V1_W-UhE8M?a9!dP<;o;e1S|XV zFl9eFtQ|}%PAJ#-lb8F*_8ZkWmN)U;`ug&zlX3eKr}CvQC-*);zWfKk!Ng~{ZMN53 zv1za}P}y?aIZpFKyy@ijF(V6PH_u#|!ZuoVT#GtQ$d7+3G?TCF+|V;GC437uMstj; z`VnoUp;B&n zQ!d564i3+N|mRkR;PukKU1<*LjpDdQc}cKY*k!-8c`-mbp-n5}vR)sb`m zL87!xIbzFxTF0=@^p(>zRCc~xy{n#1`9pV;;|DRHlqV&&ceiD~GHTZ9e14Yz4C1Y0 zdpNz!!uFnpbV-Ke@O0)-himS}J<+c#&d=^Dq>Ret7xljlTPq11c9NVYC;QAlZfBFC z6~T=`GxuM2n(m}0DSkM$3i`><`V4$Jx)i_jz-auR;HE~e&goNhImYUz^YA&wN5$Om zhswj--v{mvX(-Q8TV`v0Ps2mXPlJEf_TGuXM-?{&`FvUuxl`a^HSr(drlU8$47|wg zJyaj%t@`f}Hf8?i-S$uKwY!|S!B3ylM-Dwi%gT%n|;1Hl5XO3E;vxW1CLhOauN-f9il3TIx4Vs<) zkf8l&&YusX_(7=a-UX1o3;Czj-VA^K13Y;xkbZ9}8=v?&{te2pS1io7H(APd zw)-u{Zy3iev!T}zIVv__$aR_ecvJRW=E1WOb`2jkPdB)#6cRWDJsA#ci_&-BUEuHl z@mXtqzptAuzm`|+;d((<+vp4%5tFAMu{}>@^RxD(DUBC+RRLjM%pMuIt^dj39XroG zuRcc}UcIOOC*MsKtrXrQ<1Ecxm6hdR^8mN(UcI;XjIB^*U6Wg0Dw&DNS9jG-N)UI1 zbd4g)FuK5b|LPP55c6HCZzP)n9Q?g# z8o-&A2F*Nr7;O)&-=`lbbqc8DmET-fXRCVX%r2zx?&;1@LN)x*bmQ*uIyt96Lt9w` z1X4_cs|9227(V{o`pC9v?Dj?7mhh(JzT~GY=SG+z<6cf%4c?g0tdTaA{=$PFz}Fl) zOCO{RF^pVQaF^*49>ZGCitC9F^G#-d6a^v`)9OFliO|xKurrOjnu^n72A*Tt)vvm6 zE%g$wc^1E@PrM{HBt_l;CdHL}%F0?^lfygx>GNAVoAxU7HVBPqTRwl=@9{YPj?nQ5 zZG{(Vf3n(*GmA5F5;cjceKOOq=A!n9i*Ug?_4w^dT_0s^kV{iy9UPe)Noq4nj3Yy0_cu+zigpWd7~~ zHD?uFcArT>^pFEM`A)q(@(u2LIpf_39B*8NYbPom@Ea?fR2$|e^6#;;nI}sTUpSUk zwJj;gT+=!|TFbAHX5A-#%=R4S0V-0fUz>uEyYN1j27(Wg7g-|hfKMp4^*iY<3336= zhG~_U0>}I)agHQ||RDP7u=F#ctUt zC~GZZ_*JyQ3A#1fTagpF&8h0!l-r-Rye#jir2npz`dYsnnPPX%nkt&hpPLoE8(d`d zKl8ZOP%GT|O0cZo)1kU{h=Cp6V@lMEUeWcV%UR=! zQh@?EyhV`>rksgmF2X#(8hBNl#?N;k=(RaGa|?s^R@`tB(lKj)6iir1nxxMk9f?E{ zT%eCDo1TfFp#I=WS%-RvxRjBsZ8(Cne3V-69;||Cm3k zB$RDxs@fcuNK2iYoAqHXEre~wt+nfu)!(YSzNY)**Z*`@CiL)$Z$`}{ZpaC0X7$Yy zl5*A(CaDCqD2&dDVTW_STqqNw*)dyw5o5j#>}F{8qRhX44!;=#|2lwJ4FBE zAKc?Z9#=HX0q2gYwX~mIncUEq+5=7D$8J@PJbHYPRU>&1gR^jiQd{feY=fyFb2pB4psJ9S!O-Nl(mm?l;VS zuolKHmB!`zAjL&A$dka6j}-x=8~qOZ4UV7)tTWJKy0C<$ts4$_E_6E97SA&}z+&?I$Pp_fL5%`7CFV zTELai$cc7PY&i%TSBhg_K|ueQo564;uO1XKy9Qv0*P(x=RXP8>rv=@YpXm@4aG|Ng zjdofj3y_7WZn)2H;MZxa1&yxLGu{B1+m*fkzNT(-vMrHDL>GMu}iTPTm_?Z0q)CDm1Am^r?u!UoK7|&K?K+JzqK~2Y756tYa8wy zsf|b@REWM}bZtfkLWn>j@O@QWs?g&eo_uF(UMYg&YqlG6`N7v*<%E;DNudyev&;e# z!c)FC3nftftt0TarQmTMTawP_{KoVXzHD~}eM`PKas^jXa`j1tQobSG)rCSyw~Tbz zk#amLlMp4HaVZDPVFJGDQx1pGL{q!7u^x|aiHZR3s6w}m8lfrM^u>{IrdilsG0VH- zY<7@Cb&`e>yuvUcRLorM%pkua3WUNESKA-&65YfK(XSC*tCgvvc_;n$7`Ooy)=d^{ zif@#3El~z6`+=>EQgURr%$FexyKL%kBFCg$gJg1}j8FoKz-lTX6}dMx6EDzs(5rn#lBGKJd6K!%m#$39mJtf7f;(JM;#fGH&Ralmoeg2rUR%)1vg zFWpJO#J|XXNeR_Ojt+CiQEOL-V}gn`V~yb@WgudyqWxK6cv4)8<_LLyl1Bw`ymjCI zN}RKa#IHbLTm_}JOzv3?SFX2vWvzYIQFV&m8!*SCug7KI0*%qga#xrXrM;|z_C=Y& zXXHuA082rg{r!uJqot}TN4obIDmc|^)(W^7a z+Dyd&gXwQB(yp=VQ2M`(fAB}oPAIMACG%XY%U4Fhwipa?VWVW6@D7#=(2$2Ny}c5< z%#L?t$mxX%{{tHGZ`t(dS!Ikc7hMa{_NFlY?EZNU5JR^dZdoTYWPan=ulQHcaD9)z z+QaImSartei?@Df_FMPwLa~#RU>4rIT%AU%UyyzHGfV!_Ym{9(7^TH1Aym&S+WMpI+<7O?59Ztqf#24 z-2L%oW^K>&m-fu3;e}dP4|~1~Hg&kZ$!<_f=8$7&{F#FPCwF8eA(8nu-)uJffpB%z zzt10czSI`ayjGH@%tzNay{^*W6I~LqH=MW_nn_dANtzt8ZD?GnKmK^ZWpLu~Jd6qi z|54EEL1hU(B;FSKFdp@){N?SHL(2y=JTUk*#o%Zt^Tk{a3d2{}`P_z|=l%7?gt^HR zW5wIQhDhh1b9g^?{K9PJFTb0j(Cf1Lg6>?MJmsAIm|A<;HQ=0~`gxJN|HQW+8^77W z3r(Mz$72jNtMRt#QPpODo!zG;C}wU>Mh{!kGi^V0thU6SY7`pW&zLytT|P3fz4D%Ck-O=NILyi%4?k#l?H>b*@`h2p&?`9 z!ib9CdysX@zkz++4&jV@z65RXaOT5{41BKp!JU`*V7cm=Qa-ix zyX*x0X3I35HMz(4duv0Rjm8}Isk)%Jh0plK`$RE$$L@jU)vC%zv2#gXsOcaL+`!(j zF>NGgWX@6ntENsGiH0E`&E|LIHh5rpE9Btbtj$6fo9vd*#Ab|v?1$6e13NtrH5 zWwMLUUMD4!`APEBg5-maO57DM#9oBh#vRCp#_C|P?Ot=@T@ZHOB6@SC!?HytYYo;J ze0r_M@AlWz?JpdIFIRVi7Utb2Bn5y(kGJn5uZN@Jwj~Onbu6uX1$Ex!H=QH~DWlj3 zsrz2zBg8q-TJ@& zE+3>0bGeMPU`3h#)8C6~{-?mdZtkJJ*Zu#9?SMidYG@s;Erk1#nO->&p?Tkx7B5OO zFns&}NwW8x3OVMNPU$1h;{Ytn)V8M7&q-rT|3|zBx~RRs`#MQ`+Ki-u|I$!dDcFiM zH&i*1TTH6gGzUTCw)yxvxUKQ!q;>Wm6(==SV11nt^nU=^ptC3mcb$+|G0?SoUU%ud z>JBj~erRR`+~9%yFC;#7M6x~F6rqJ1Y)z!c0ivct z87dnV|AfFds00@VTOW5}`a4D?%Is7kofxabeuSu%^0HqE=6uFRLF$P5jii6J$*0D` z|GrWVypW%o`{kn<+LSB;Q=Cri+%c+n?}uRlMt0Q9seU& zpO|PdsG#;uE-`kOX~Bc?W+m2jM1+Ow&W%iN`Wa3}cJEE(mk&@^e)%vESAJm~6;o}h zZ51{%BCQb6uNJh7B{-L$4<~u0^qdwszaDYj3X4mr@h@ih?`L7)U(43iudnkO$ z9q!diqtA{mw^4?vq-0_4Sho}cOVOLGG-EU-(V(yAX7|4U;7o)3)uJQp@+U=)H2|A< z!@e-BYH*Ek{dF(O6N+9by)a))r&yR&xM|UNie4pw+Hy~AgjI_Yp*!N*iTRR%PXx%D z0Yth0lVQBt7xXtppoF7aFIau^Q91Nlk_fI+bo-z2(NF90akEk_Bn#vDhweHe)l;4i z0;uO|03kr%6U`2G(qLGxzoRd5Ti;dCmOt>_LJZ@yc|t zh003Kz(uD`g6Ov;;{&G(A7c&8MbUw%1l^x6S)w zhF}fA;J*`hHN6E*`!rqy2)zcLy(G0Jk>X6otgSAyXB}5EzCT|6-7h7HXAh36D+F#? zb+T9|C`&US%A=*oDrROni7Ly%relOWl6HQP>^Dgs*w(O(Iso$vq+%o(wfy^}bPWMn z^yiA!%lgCWM`bw-_5;v>Me5|n4JH(!0&Ko3#6wpG%!+HwN_hvNe7scyUzAPeC=pe5 z7ARZ$BD>GG`&BrvTm#U<7a}#&?&xhIrjR7k1wxLxCutbK!e>%b24E>OMP}=OvyRLO zq52gqzTQp|b2e&MTYf$e$&v;90hZ>?a)Et++hC#GjEO6ffUNy0F91-T7mocJg+a`v z`h`Nk?^Oj<05WRI zwQXncq#9Ak*bzIpExH@;9~%!bqxq@26R=_hua8us~>r#t4Va%O%&XP-<LNGnV`DKPQzGKm#r^%U=4>xN zCHD5t=BoYd3W?$Y#(Ti7!7h}=(r?n}>~ji;T=^}R%y%pKhic#i;u$PI(yLV5buJa< z`1jGmhz+_EPB8$-+4!pNgLU=!*$RFapYj)f*w{Q^w}%y->qKKGhcGSDi-@$3C?SaafNVd z_yBmNY=bWqt{4qc;$ojTj_RJ&44H~ND%{Nii&<3P))W*_k`gyC%FL32%;q^Z3+AI4 zXpX@0Bp`h8N>Yjq&MhDMa<80bUgfa?#i&j=ixStgNJ9jGa^$B#DdhWs8 zoovu^`&4mEN@5^t-yAIOaLg-ZJX6*xQP@R+3E)k~Z*8YcLXCxj6c~JvBk->Tk?!r@ zBpqw%$<}_`ux}e

mI4--j4N9}O1Ibzto&;c*RIB2YhGtm>F`!%jO~6GB!n&Heo4 z_L80KkIjEvISOwriznrnZrz<2qD#(KX7Bz18LTPcbL0*DXV!ljH*c`C$0$F_{GMUw z-^g}A!?A@eG>dqv6_M;#rCemTjk#q8U8m8FlZtR)%I$OuJmmjCX9d zHe}=%8J$QZSEQrNzEWmE&?GgfDBH9_M5PgFIQ)F+l_nUwPqGZZ5b$+hhdQuFWH5t6 z-cYmE?&tXf-5|&SFtYz9{cSdAl?oRK(AGr;7!Ll9=3-yYai1dQ`I>1%>7mmhUCDQs zp^J4Dwuu2Z4?iiJK5A+>LIEd=nEP6^y3yr?sy0S+QBfy?&dGtnvP&sErJ0D;#$#~%v zjPBbtjX-9mV?-r2MC)+n)U>XjNS!iEncoFyXlT}z;Tq*uw8|b2!a%=@za@pSb70MN z7OxOPu=i^M*F}TxAKxGfvirCXSx(2bdQL20%=IiF~ z)xDN$Vj}KxJwat6r?PeNa1?8Nwf@q54P1gw8fJ$~Y_COHH1ZFh<9hqRH{ff8nE&H|XZe~Ml{5el^XFjdJh{(uIqPs(OK@Hp_^S*z zR;j%^utca|3ET~yt`$i}DCsmMMgiCh$IMb=pgBQDRrA}%F&L8r0|4tqXN4o@^+3c| zq>@gn;0Y)}CxbGe%dWHd=g?#{-s`@CLwAW z{+D$fVD!7%9q~3HG&4$RyHjBpLHlh zkL5;1aa@;f!HRSGp!mYp3^8*5yW$S;qHQLw;LND|Bu!j&ie%gMG9q|(?Y#WbGePl14KJe#` z_36i-Y^VjdeLL~SzTzu`!mhB=>-bQ)U#rE~la4=IrJgT^14ew1!kIObdNNnjwS~kD zB)-YLT4JefaBI8-&Uv0F>K*DWtt4;dZ+Gpf>5ZzQ2{{sF#W)vrV&TGq^ZoMrcK|zp z&&|}(R}wKwiR+sdW^0kCLL#bTVSgtgIvF0gl-J--?#Z+{XxOX_Qo!d&Wq&Og^T7q? zO83mt1v^skovPaPArDL%8eSRY1V;@G1j5-Z`Ma*2H_SkGenvA=EGaK(t6U`kN95u_ zrQYd=c$4;y53Y%`HaFe{JZaZP!VbV&(x;8uVHXLv2SL{#5e|1{9_Frb?Z0{dP)J~* zLwM>Bv?1`BB6p546(*NIsptJ#au5i(YATTBH;O;22==ID-|BbdzSNQ%c)02sr4D%r*1%F4Rb zKe=UjKXKnpXJ1FGd@%P!Z7V* zI=wjAl9BVHBhOn}D%aMzgM;{xF;7hV>OFt?_nx_Kzy5tJ(fhzM=bNos)+>Eb3dLBg z7?VD6#5aD+b-D(x=9f7TGY{8ccod^aWAo&RPOp|0u8SrCU{Mt~*E{)DcmGY!Eo~{; zJA_IO>%*@CpL``XZNL0jG0V19Jm*Ko#|VA%@0ky(hp`RCw>5}yo4pyvim`{+^N(5R zSKZ#OaR#zdm@+BFgF-n$HQTmRZk9bSp4=VsxN3=ciq`Yzai-rI5^Pe@oRn;+i-uPbM+8Md_U-eM5yI==q$ zLTt3RSU&a(r!ccONvUr^gXIU$=9uNf5BUY_2l2NVEJC_HDG+tg7|z~z!+j zE-kqggnJdH9n_H!h5-J_5M z+x}?93ONeC9_kp%Z9!0X;^nwQ9 z=&0&?-(u2KWWO)3<_&phW56i*V*|_a!S^d*& zfX_O|z*p?rL6fJSp>>PZEp2wYg@I9t<>^%0yyJhSb?R+PCS3u5e{!8Q8+C58Su^Pq zXX7PO0({e1n5?Q-MJdD*ln-|o05_j`l)yf#>f(Kehb5?vKx|d`cs@;@v*{4&`WbBD z=(=br*WqI>d1Wgrcg;>BtAzN_4T5IQEP4rR6ij3#1o1TwEwGmJO^@4gyxvEdgM0pj zrd3r#zV;{{)!-V+2YH8$6a7X%c-Wh*C6V!?$yo3=Ix+{vSK$y#)mHI$9W`z{+ZW%o>|q7IW#WI}%b zt+(pyW`X39R+xt*e|QD4ZQuxHo@A`xg5EcCvb_|78#7PzK9MS`an>ygL$euSme-Ic z)fV=p^%dUPA;D1dl1uoUC)Isde`R_7Uu?Z+IGg_;{vD%Ml~UT!7&U6OYF3QcD{2L? zs;H<<#U7S3$Pcu*t&R)W72^F=sQa`7q+&FD3&5+*337b0D8ftGG3R>H8?CUoPU5mPlciC5#@N7i;3?y^WH9(>17p2NhPH2EnsL|2 zG~;FFn;P6bAyn`_iU3K)F}3hCUOeFCV`dYL5sqrbwHQ5t7HOn~yK+7>L_sFEkms{F zmm&=)Z0zl)*xClcia{XOfI00)WEq?Z{=yd9&&mDrpCqV(UjT(-p-8;jK2;6|92c;GaPxXK zz6|723p`shN-fp4zpQ5`SXWk*cDKL~gVKd=>NPxMV!WAvFrK>UOZ)el-F}cljP65S znPAqP%3#4e5h0{MA~opYU7KU6Kz_ENQfD_usU8^79L^?KKM3N7CUbrkSSrvO9uKJ7Pa%x;i75!aQF@j)RIRG8|x0)x#BUa z&Z5D!DyZ5}5Yum+Tz-z^W$9=4DA${CoGL9u8ZEXeQm^{=@-s2`lUQgL!jmbjpFL$ zs8@b?^oA7|4Euz$MJJ6ncBhhEj9v(Z`6&)l9)GqLySBH-Kq{6{#*ZXpj~;c-AYD>< zWDB&ca-;z(JtB=2ab~%8B%SGly?EWnUqI3Z-9i6?R&C=^wLEC#i|6-4g+9ia@8x_-3oUz%K zeUDCA*KFc6I*RUHX>>6ZM4v3aThbb$Ck1Hgnp=(zM*5R|vaAo*3OriZ`c(6yDzVKf zENv2HcW?U-EsBq_6yt_C-G^gm(j&wHspH&Jzww(0Zz&Dp!!cj-!;qcXkcx*u98vd) zu3hY5<=(Acvf4fQLJr5+4djx&$=f1jDcPS&NAfgb@q#mD&#gvlC=-Cr4u>#^W1zopx##ZWnu@pL30Br%KdGa-rDIdGMbBG(OWpLbhio?VkvL9MU#TRs$_2bo4GGD z#_Dv;5_F9)_bIDbDfTu!jW;4I?C)`<@wfg5W|@`1tG|24+H|Q+mJCeEA31u}SPy-^ z;k=zFp}^cZGiG@zZ2mtGjDZVq-PN}Ce~Qo7((;B9YerSz;z#T1TaOSP0k-QYVv(yH1@K50pLquQmWmnwL(W?~ zyS-}&DP^so%hvfXOt5^m(G$MVjj7VeU*-t>5%jo_x1!{06J%AMIVy&#>cv$wA>X6E zGSWQ8dR1=3J;&hwtr>s($PZmhdYA{lzdi{cqe%ThY@j%|p5smA?DGyWz(@!`SF63Z zdAb;(EAssrk~m`*f{?s*N@v{>hVj zUABQAPGXE?eqBF0OAE4e1wA&*D^?WA$UU#xQFwV+Kz;JQ^ON+>1!dCHo_pN;Pr!6V zd%w}Oc%BHLqvLbsQ`(K(;4SiBHizk7pYj!wRNNOXf4uS-1+|v13RZUD`=Zjjh~E72 zS)+aNw+ona%e&HI_0!9LkH#YS1dpFAKRK#-u7FmxN?zap`OPto@^pTXD6i1ow&fnM^ls65!ICG!gG@VyN989)l}JT6oI2&dB> zr^01@kjWC)fN<}@f&IOjdr}>Ay@r)d+}|?up~+I@MTX03!|8kFNkx(#fdVB30b}1^ z2^{jCI}!C6hekMy`Tv}b0jYy9tIMwMOa&{}kh5X2C*1X&w159v0L2C4FQavKs3;`$ zF2~j3G2)F1nl&Wf_CL988ezNn{fXR4-FdwxPVY!Zz2$BTmB7Q&0K-PH-e!@{@}|H9 zjBr>a$`j#lVFw_%zuCvR_6b2K4gR?o*szqGV2@5!XKgWwbcxbPGN-05qqttbonsf4r#yTbJ(N!Uz3ke2(U=T-QIMZGdkz7X5ElCaEwW|_y#S047os-;R)s^e(O zVpgM%3ru39qx$XZk+iCK9zqg)%duoA4Z+O--k?<#N1#^j^`>ICnSb;MDL|+3#gRmL^jYR#D)s1e~ z8!lqyE!6j3cfcant71xDebVpo7=~7en7#J8dJ~tcdR+_N6?Nsa^m*ly+4U)UO75G` zKE?;6eX_cJa3RihHGk4fdX)RXRoIN_0kMx^&!~L3{MMuP7|o1*OYcFu>IyUPrWZWDdOjE}!X> z`j-Nq!%nk2+x)k=UM0F9aNnP9-L=216k6_Fr1F^~s-hw#=q(RS$8QvqY5cm)Ft22m zcejt8Ai){Wcp*`Y3l;+p(PPJHzczg4-l=0-&e@zV51GVZM5|edZrcFqo-X$D6a+Hj z-ifOZyq!jfIx4iZe4B~*XT-sz48Q<1H2y!$`9IzH|2Sx}>JEsNZ5&iinB<3rFqnq{ zQ0o4A6~wlo&RQ`OpqlSKzvL3nMQcj?|0DG|f1Vf|_kXmuK7ao#IRl6bQN+ja1GK;A z)3m4=Hp3C$=0~$P5C-CCbhzwn;yNn@0R`>$cqmK6eXMV4HqYOU`h^%TL-#}|TYcsd z>z047>ViOLm;~q`nrXoEqvc$bQt54zESAgcHW3*F(rK@f$$5yU0#5^BauBbp}t#g+Eg{_$MaiV4}x$uxd;7O1ev$ zcM-M)ynRJeCejXn&3x3Wc`0b~k_L*Q1J<%;=taR=yojL-OZ7;O*PtO8X1D9=s~m3w zL~?WaQCijFjI~@(NQ&~v`-zFUViiBdK9~n`82C1E_5TV7Ml_5Z8UE?!>+5Rp)>F?! z?dTu8!Jo{y*US6+mc8wzRFw0coQqa3hez9E!lM)+=3<}2_yIFTnOH~1P@{(dDjKM; z=(3P(mwrVrSnlDiU(HIN@yAdQ+%c%55bi;Z(BTAu()`4qIQ|;7kgM!A+2`QovPqZ8 z7-{lANdotz(by}osilHP&|aRf#gqCQ2ROtIC=l2Qb~_wQj* z#8=yxL_hUIdFVXV8bYBESRtl)%hdP(ekrN=A85H0ZX9Mqb?uP1xy-zv*3c=>_WvaU zn9_xCSX4;K&J5FJI#NB*;gktVyx}dIab~&I+;{HD{oR=3j35yL9{7nG&F#nUh@W{AYrwzNrYxWaU@XV?-K5j7- znp8i;H!8F*oYgneJ()UKSGhc=;L2liCAS>;wZGSIML%0M#$<$K)G9g%JsWdeVrKbN zUl@Cw`lG=NZmr!_cT7Vp&+3EVa6LM5AcMn>L!vrOwDE-FU7)(05OGNf^S0qj5h`OM zWmGnP!B{U+tO1>TEQ7BtG^^+%ufk2t{L|=^cWvEpv{6gAl>9aK7NL1`?4XX)>q{!4 z&{d!Cvk~<^;9V26l#vq7^n=aB&v{DA8bSr=z))ZMjR!hUx!sj>6dwU+)izO-_Z+bG zK%IAS(c`7yt<*1tMAVXF;DGxLqU?#0r|`GBD8<=<;PE=Tc-ck)ew;rcPl=G4!|p!ycuikGIs9yysKuhgtq) z{)3js4fV6adXz@-R8jtGWP|bBejG%aGCCy}d?iEQs@!mk3NOrK$*oddQ3L`RYs87& zmvtE%^ws!VFYb&#+A;@`?@*wB8=Q6dDqtZUB}+ACiP{{=?T%xakkz+kWUh5MdAz*B z&rdhWhTir)$D<*kqX8kru{@eiQ7z7>+MQ!cB#AnToG~cn@nkThVmO<^XNa6E9x~4F zD_(r!KKe|0HDfqf-jk+qKDY>tWr@kI8Be-w$th6oKLXIcir=21^7f7#k-@6Xxk zJ!O)F2a}iKHlD*{L3b|Q@s*H8PSg)vm+{%=eA4lgdheT_kHo;?uSCCQ&j>D+Qr6Ul zmcivC&Q;H&S!2qxRQawSd~Jf)g(mJA+8Pg!f2Q@TZ!##OYkhIb43f-o{|DBlq$9bZ#^LhSxTum!{F)#UMXRc(1%l@x*rA#7UZ}Ev=q?0j- z1|R^$eReZB5fg5;{6LQW?CLGQ>6kA6;@q%}N_a|d(3@W(Eey07&Q7(6_vrV`#=P4e z*fLIeH+H1Dzbo0cbMwf4XSt?ym&R@+Ku0FA`oL54{O`}@%gvQvqTaS|>!BTR&+G4< zbKF>b-i-=f7^3D8MGJ1Ox51L_?Gh0pH7ixf|CS+DVgMj2D@;2j8<3AzH*jj znJ)2rpv=BGkPcsmZ%3?A`}C1FtYRct?V^qMj<0g?hI0NmC2aOfV{8RpCj`@`5ZczC zB+$@0H)-Y8*YW1xRAB}PrDd$jDNr@rYmaCtPG#+}Pj{WS71xaPlfJSpz>MtoUViQ` z$rj2$^yVS6l&Hn_FB3I2@KGS3MEZ^*7dbMfI@FORKP)!~B^(r`YnvdHV>zP9soudb zzYW?S5XEMLP_>+a#qCV0d$?S7nU9I|Lg?Hac#1eBs4_;+epT)yGb5#h)d6K&aw@is`CP0|$2FO4*VKuRnnBS&?DFxC1GDcvsv%`heC z=GEMVz)X7b92<14bRB%=&H4NO?h}qZI#2BQ z`|=T{;`+)9&-3+hAZY-!ATQ;RHM*2`$u5|K)NiRR>C3=dGXCN!)qW6YG-&tn>%Y%$ z82rG>q*uzOhmvjCF;-4K-ao0YbgNTfmA?_(7ng+4FL+B1iPY~QAQPUCmfn-=m}N~d zM~(B-D(M`d$Fc3}^c*LoK1CFEtSer~A(QJe@937Y$EI-q9A;VL8hqVHAQn?y- zR8GFG-v{L(D)!~-W|}jkHOdav%KI{dV3#C8zopG8nGc6lEO^+G<8)vqy8*|;E*5LK= zg;QeW%dU7|7CMg^_YoLyS>!|q?yb(1 zfd3HiPDSDn65)!H)m4$!(%s_k21EO4PG_vovhmR+(ilw25HrOl0OC(oSaz7=$Z zB1H(&me+s&UC?V&zOR*Ox%pA`aMDE~aBOOfE_Q6x`IHw00=lqy(-O3ZwU-wXR z@zrtM7~?)p!I$XE(di*8)w)-=uk_IS9PFpVVI~bqKt4Z#hA7w|Q9^c?uMn8O_Lk@8 z&7>uZT_Ut^o>#_0J*Rym##j7Hxgd`RWNPMF-E}$oGXEYwkG(?Rj+4jjNF5$fCEn^f zYUyM3gc(FANVbhveaM?{B(8@WjwVQ3IlVlw2w3-2G?=~tZe$~8{MpED6zn+S*Z~d7 zo?viR!LaC+cXIGs(Z&pRk@D`3FQ)E?UP`b{f&QFi=9R`rc$&MhMU%hY+S)*f+5RqR zl(^#zlA#(h92u9l*K@GVG8a+HSGgUwI?dTPxb5xqX)shJh{yyAik^uKgc`N(_x>@v z^SsNd+*s^Gx)+C+v;gqCaNPzRM3GpaQpB02?~Dnp0cgTn!6<{?Br72ZYuGEtb$XMq z9DyF`jIBTBv`>xy)#vHE-|uC+%(iB=h)r%&8pSjnjQVWm;UMb0Muv%cbM|92d^O1C6Pz6(ge^&VhPzU6cg=;f-hX|5 zRyFK`W|rv>O1;bYl^`S0T^uU8?a~%7bu4>byiKvTa^Vi&xb$|htw!X%GuI*X?R)Dd z@|Rja3ttEpM`?3N;IYi+-`MKD%&$(p?_|UBW_fcZ4TjYI;JI16!09P7+?0$?OyF&< zx|KyQ=)S2Zk9a5oxp4R2N5P19libOTOTQL6^{1`uW zKud$)|B?FmWtQF-xNMh4>nM&ZzI^f?qx5~Y8Sji7B)nqOWp~akd z{SU+`s@r>pZ^X3h>m~!8x`JknAER?cnqI9;b*>t(hx|r66ss|)1TXyIbH6;#dUZYf zgR|1hKD8Hz(<{i7F9*8t7%P`Wk&Z7F^r0KQCQ{% z`?IHai4U(eSPSwI5c{%hgVO5~OE*_unzzloW)zCcq#AY=Of8f6zR>@=LV=Nl^@o1X zSV=XH8JC8zsehjR>Np|P?uwnaidejP@Ai@uNKfEuF>k(SlH+AwRvuXgon&I@Pr?&$ zjb=DQup$&DF|)a~Dfje6aPEs66P5&lUA3ghul|m7V+HUODUD+XIR(WHd%d!U?`auI z`UEc}M_A*jPIHf+W3!K^t_;O};#;@OK05wKwe#(MlmGNvf#6ri(fbEahvYeK2;cv< zp&C|~dRR2y1Y($dtGgc^J=7rN+jEz5PE32m_)8+h@Om}K%sz6-7ym-70(vNCce! z^TnHwzkIZ7PQR^Me#L>&0lw<+4c!jqT&IZpJ$5KKS2(}+wcY$Et0gWvs5NA8#h(z` zLnl$#p!q|TChD_P^-CSIPS7&K4?Va|IpF;MQ{>LC1o?T5+u`Xm6OHQMs@4r+B8Ei`EI;jbcw$@UaR5 z0H8jRqUM6A2M9(fhg!z(t-ZZb(=in-G(SGL#2H{+X#;EV_}be;_L+f-5}AVwDWK}h zCB1?*j5R&$w^~v)QpN`6v~LLFA%_)4vO&47w}d2wzoG93y?WFMuqds^^pOz$FRR0r zI@KO$m>3z_Cs87wpfxXabwdWKBgJiy`BSz#)g<`5aa;m(qUe4&0f`UoXT*$#N7WeEO{rgXO!W=Q*1E%m{ zR$fTl%D|j1XL~Q8eRMxImOVMFNnZ}>_=C6P=mU}lPZWqG`2Z*VgcTK}LTW+hj|Nve zh<{82e0cY8!)}@oV`(Xh^3j#v_-~vsgB-^Dg;va|{+r>ZbIrVz*kh!({I?sC|1q5)A=_G^V*0elFB7+c%x(f2=4N!Cj4mrg0sh<6Z%Y)mXA;qK0Jw2cd))E_g zhYY62R3j5S#@TeozpLATk4n->;Zy3gu z?+|@93z|^u+RRcRuw<97Y|I*5pfk%Q9Ip4mqt0|T6#PfSRqi``C?DyT9Wz^$p2tM` zj2M&#d{8-b6~gSwLG}9k67Rn;-hme}{<$N2K}5B=E#yyD=xVIZ;3Jk?iyv09k2L(; zcgui!BTUdtoyZPAYr3uuLhS(dE9(;|g>dLB0{sm-C@(>Uz>F2E1SIj-2>5ZIvN8-e zo51xaG368wa$k*6};b`N5QVxB1LGxB87698IIo}zLI{O zsu2cMU&NwI#%%BN+M-W4hoM0A&v#b<*Qyw@fz4PR!1uoFfq zT9D`{Mb21(EkET6$o%3!kswdUJ~*t2Q$?I6)xkD5;AS)p9>4p5o0mm8rJ+>B_tlrc zPkd=5*qnJRW$xqe_DSfvVy*lL-`CvP&r=<>4;mCrP3){160+6^l9G%DOXgpAtOJH< zkq<(?WLugs0E?_owLj>3o z&GX}F4T*W~Z0d#4!2ZqQi*xPBQr5ddm+HpjT}S3Bn|l@x zNe~ES7-?x2BRz@)13>}t`o4^UqtF3+=~OhCt0JGys^T=1l$oJeegh?+wt#Nm%1z3k z!E+A>Y=#Aug`((d$4Sayxe-|#nUMAhE*4{@Rh+FiUxi9Ar_D(1nr)9=SA99$_SQufsu*$;8gK}(!^7Rn7|U=5PRD~<`239G-knlHm~=c zm%@W9*=wlOUX$Rj=v8ONu0!4)%8y*O?(yn<-s;Z9DEwcpNvA7G3D2N zHkm_e7alKC+eRsE51o};Ai*m|fZ?!B#=wt-p0}mK&evEfeC$U#7nw``vSBir(Lcw?dm^U2o?HLU+=vfz4YUZJQwXNHrJ4gFMlA1xh_L4N$Vh!q&o*^NmOuyy4 zZUB*wJy!R7E}a{l$u0x^W0iv*1^@=5F>p?V7ZY~|d%YVz@60iyT)j_UYFi&l^s4&pnr!gZ6g;|C*3_j!i zh|_{qtYv8*Q!0Ae!)l+vSz2eh>fi9xVsZ88e)t+scl3gsH!N0oC&b5hzs~0u@134?3#LwLudpvq>Kw!_%-5t;U;j*ReWhN2B zdYpyw{WW*I_!-YP6+FNE3rJXEKPjCAD@+N${~j2m1UuNJRk-dKweyLdM!Y?e^UrEC z+_FDQ5 zsX1ugoFytjLOv997U0A9;fCtZy}7*^P<<=+T5NZ z}st&d_~wsZ~}mE7~I0 z8E&`xD@p0U;C(6h=9Vy3iZb87PZE-IXn%Lwbw8mbIOUUKt8m%t#l@^n zG=z{7o;Mz;cP#$KA%)@PJCKIEJnIQF;qN(bM@XmsfH zommwnWxJVaU}aJ+q2!70CZmVIht)?S{*d%HB;ayvH|9zY{xgS{6Jvg|u2*V?o=^lU zF4kl;qo->;>SN2BN%M!orG6RAv1bMegTPqM4SG^10t0w~^$;i=X`v%GSI94fJ-3gLdJ>f736DIo>g=DRPE@wtf!zBvQ2 zDw=!v`gZP2yuh4Uj$m_*t7*fpn>(z}DB#+PAiQL1bb6LJNp?^Y)ibZlaBxuLv&OhJ z@wp-B*q5y}O&06Y^73`NkUwrBWo&6t>;QI0<-0>??*Q80xp~$v?~Pe_tzG!y2fBJH zo5oDVj*fNDSB*K6Q>Kh(rg%s$qef~;_+(*-f`i9vp$b6Mr4!IB)Hy{3y zuY2Gkw3|&2+6lvy-g%5frVdPV*7PId7qTMXefvp}W?Aj$icWsSWy9uv`OC zQ9#^focix$QvixyTLEZ#hSWfc|Nna6NvY+f_c5S4!^i@F&0TR$95^-tNwcuPJZx*t ziM>Os>TFUSss~e9WYC#~1?WG_x+x2b9^wZ^lxCz=I8r$Nfv$*OG9dbq!#1nPa+gQk z!>^@l(%;;j@CmJTqLrzlN$Y1&vpsmrdC#ch;5=t>MXp7H_&<=sie@Km%IL_5YL={| zZa60K09Mt!AOVoXHI`5?X`@2wwErw2cR8r2z|gWKi_`+Ipvq0)l}(){igkjdzFN3t z?$gU3WkOP?qERKuK%w@s{zM_^fCJKbH73N|Ql&jpC+YU{$<@1z{d=7TlWq4V-LR?TuJMPmVb*O^>9hL4RuM&GqQNIKI(u z(;qafotXGi{uGMu*n6bkdWy?LMho|>JBAbJ-Un@ZAehp^yxO~;dnAcQfE zIC_*fb84Uv$a<%Nfa3T}DV0_5x-Co+(Cv|h^74Wg>K@AggGNIwSbwA=2KO5nNCC5_cp)L}2yGgZU$941P zu0;2TN8>H?-V7)EMk4{Wjq6(raGh_BmJ5mBc6YuWvw~}H?bhV07L?Ca_wIaMVVL8} ztg3p>{#GSMdU*VQpf9N}KPjFV)%-juUN}xVbYS)v_t#xM_}i$zViAajR6+yp#ESo< zZ?4t-@Al{J$vUOirOLZo{OtblC%%xDR%Kc>wtLT1{yI(s1|U{FcuhgN&fa9VL6}iE zhcHXTjgP0#I|+eLZWtUNe+)kOA1LM-%+Gl1^X%;^i(}dwD*ow48a59WZ+R)|(~Ins zi>C)u#qk~_=C#1Yo%ES+)I{iU{1|p_zjRbAIEuV|#~8eX@%C_8aTui2CpUV~vEL7d zuxywSSqckQwdPTRYRPh&77g0gyuhM$E6YhlOi*KT)Iq$mDMkts5>bVYPT6Uz*s%Qv z+K<}B`hC&ZrIdpyD*h#8xvTG;f*NZ{A#5_qmLJE*yvXvooo<4(hED)JuK#dQj$MAXsRjiA6W;pazS~ zGK4f~?Ia2FIX5H@bVLbi(!XoMxpH$z|R)0G^gk2gOaC@Ga#a+ob z2AV(2x4>Vplwub=l@lYMD9?cMq-FD%<#hD1$E3zD`Vox!4Ilma!UbDQdE zukS)HOyPj*ezcIPz0lE_W!Am_GEhF2t91=1e;{T_!G?^*^(j4!{{kiHPvzn-52I7R z6ddc)&uydiW>;H=w0;n7v@Bh?QL!oG3cQe6_N&C-(niQL*NGq1Z+3AVn{HGHy~s}# zpu?A7vaU2-g@-u1mZ8l#d(?l^hy5M%Y-Kk9nNp z*W)t#dDJ8Bh)<`prU-GHUWQN^m<^Y5#36?o#ebAZnaqn71%W_I2gE2cb^2r>u~^gC zSZ@K&?IZ67f^r<_=qm6goie?xA{0_Mx(DeZ#C5*1Wwe(Dg}eV&ZQ`U6CxbP2^^NFHPnod)iC=sLu?c;C%jY z%xm3vN~{$fJ2$EHOwry8^^N})v=hS-5fM`DYM&3gUOk>K20enwK01kW_2T{YK+7LW z%Q!UiU$h??LG)0n?|DdEB3Qv>-%?mw{*6O zM{;cuH*v>1Maq-Ygbfq6b#k*qlUv8*S?Pj9!WmEtFPJhl?#eE8GphE7LzF|EWXodc zmgK$qH8Z*=clKDu&H{JnkGOuwiEeH;c&ix6RNPi*0@KfZsys>EeKpUzS@-63s~+*E z!%4O*_f*I|jezWnufb3F(CSOs?UmTy!>{hj58P}2eRQQ~KS)n_$K*(BhaBTI|Dm}q zQI4VPVNw**R4LuaPmTNYQ@+?=w5aFI=Th|zCs`CxZns+ApZ)IRl7*GouTBQfpH&g) zy)frLE{fEA?!8pw>H0u>tvtGw@p`dPqdniwVHeKij_ZpX=JBV%#JG?-V*+zB<_o0( z?P5w3@?8gPW{-Z0ISF3!@c5OpsdnR^>NB&GEmGP0-J4zSJ(6i3@MLGW^8MVrI}C|R zyGOg4Kaf#iu&47frfUhv!wYC$cC9rQO?O>A!Phnwy)Z*)4t(xuRrloz5b>b-ok$5n{wJp}@N5ir23(ba{T zhL)oB%lMhJeEmlRb3F?0D6RS~VLl4H@>)Vcm`8D%bgUpsjhnZFCi}A{@p@H@@Tk*4 zJ%ft6)gWEt&;hUhyd;rJy69NsH^+2k*A%xfj_U8~+N@L>z-E1XRb!g)nnyzWVWk9Q zPs!g#v()| z*xoUvaBZ&nF2d3z@dNiltj5CEc=O44FVyL1Bca<5uDiJb>Os$pB^J*%^ZF(5V{rUV z8kbZ3ZKb9wozrDBz-1YR=I&J-s8}J8nlMS9^f0?;PI)v@6U3$~J(~O4$E_FDM-L{p z!i?TjhF!&R{MeE|u|jCx8S8JwPWN;{C~Osz5GsQ&z9#i0JSMVAJvGgkk$0@pI}z$? z=bFZ1BtT}z(@JX0XD_~#dqCukN1GWBH`4}j5Peqh-aJWA5<970im8xU3}SBhxN*;# za!0oN0b*U!{p(}g!WY}N!~+Fv2Rr*#q;{6SK6VW zwBTn)aibER!a!J@K`#dcWF33{NIX2`Wty?n3rn-A((fLshY4C>?HMbeJL7P``sWAP z5#MJ!V!-P($dRdq!*jXUWR48kwbq$)2rahd9Fy*WLY6RLwOnLDVz;&Re%~sgr(_Rk z8GuPBze)G1B&M67W-vKG6Gddh6DOJ|rNBO7`W@z7?C!vWTrrKAL=m9bJ>?6&&Up!Y zhDAnmL3}S6b6`Qz1-2=!{tpB=Q)vUpk>cNcw_(yJ7JXO2BH3qSy%hfI6fh+>$)s8M zF_DX36cVExR9>Q;-#lnc>v=oeAB$(H$*ZE#`kjlZ8JHWaS?&{C4lP)jAS%D0D+ z#(nz2{fkP8c5v?y#Zr?Nur^wcX0(u?RdzTFd>FDKWBTnD;teFIZ!7m5{wj-!PeW3x zWoecn-kY*!*$4t1`uK|7oq!7M$VQv>W$e{}=wovSKEjN$yTX+NMM_nYo9*f4erGOa zE0T>p%rIdo7Zu)8;8+ortKz;bYf-X4ig|+K+G%1fQFhKvAiv0%qK3z%L1Nl$Q1r?2;lk7 z{s+1||EV-vcf-cxUwGtoHMvEIZMO@1zKGSt8?grASbyv2l_9aWd)x7TrR*#)eS3a>;dMTmchjZ!MGZQ=HY z9G;Edh=3#EYg6@iDPLTI4@&;_`Ud?fB{u`MH{B% zk73)jmCpo4;u@~d*nMc*Wq3bYy|u-XQIh9pw6EXoa`S`a80cuj0Wwr^IlL>**+f!` z0l85?iZJ=Bw(;hd?K`)CwtF^hZJ~0#cMfk~z6l#9uM1~eaCU$m|vzj|Ia z^T0Ed){BQT`t;M|Y0UY>80lHnS}ZeY3B}owrRJ6HYA^nMjN>j~;JP%K9(G-R&W*u2 z2zP4zrd?!0zQ z3((T0NC9D#S$N77G`c7lc_?4?Y_p3RF0|aY?V&9Cpug3bC2M6YFCP`st(&v<{7OK+ zGIBRHzB({i+HDnKHRfWk(tOp&MdWQSq981`$F}}#H7U?rbA8W9V|A1V)rw2H$IerS zFE~=KS#7^R!L1PYERaH1|DcLK$<)1FCy3^;;1b+DC`;h!D`Sd=y;?6fbkZ;q}O zww2xb)FZ7;03r%Oa+r7c1JL=ev;m=JSwrn)aYm+|&8}stFNa6nZfh%x9Ha8kAgTJM z$%VhCOEr2U88&r=^XMp@(D%nu8$;$F?q}(Gtu-z@Ybl_)7bQIqac!seOV#4@YbWMk zmNt3$CrEdl)BIngtAqbt&fOtNPCtLeD??k1-F5Jf{5oZLnUZv_@vO+JRdB&``rrdP z4zp!`OiLi&N<6dt`ONl3$>Z1Xipk=?Rwua=wkLTcohN)5-U}}o>a&J+VHKhSCySpX zk__Kv#0&^ZcZa2R!Gc!pMgx=W*R_P_-d7l@{NVi~m~OIR@9ll_jdE-}_Q}I_^o55% zmuR;Jt7IAFW^T~#oyWhT+ulb{HC2y>8t8YZ5F`ZTsZiL!DJhAPXr$Gzrz%#|meF&E zE-M)f>GOCN2J(-#)Xr>|#lleJq|g=HthcpH_mg1%u54+BDhQ9SLj3cXI(Uj&ZV0 zNgDqQ2WWP~-JItgdc4Y-|9a6|nI}*4K1pcYXzuI%eaTN@0P((2B0K$dyjr<^!{_oI zm1Utl*|^JuxrQw2NUf<4ckhq8&oCfzzSJpG%~JuBN;^5lsy1AGBFmhA6aVg9y6^pK zBPnAl`W?C&3|8nF>Rb6R`wMH+0hddoH(hCkqT1M$1|1&oI>ObW;t*8PcdJ} zK$haXGpbBHv%-G_vfJ&J#g7H_KH8UkgA4=CfjP_4ziLfLyf%XP2yt`l?&*k4U5e7* zAHmf0hRlxPPE{OSOF>nxwPq2rM&hhOh(KRawluv4i!4^v)bM}<*J`^cYKRD}Fw zDH$#}SdlMIuHy2D5KBYhZZa7L^EXv-6ax>sU53=uvHTWphb8-7D-I0-dgbg3D$0JB zf*fg8oLU#{qP}*V!d1q1gLP#rU20=XCXaoceQB;L9g+`g_B5ggS&Qi9S0mXSbMuE$ zozpz6hsp{6^*AxqnMX-)A;df^P(?Hqf~BTzrw8W}aC~_5ZyiftM%sIk?qbTx-PUEh zvF#f(NJdnj2#J&xt=w=i|L)$4T7FTtpSY-4JCbXw=24TQ6N|}j7%Ok--us_*0tcQjwHScf3V|5Wi zl=-6%jpB-<2)ouVn@nwf^yUb|Z`1lb7w*_D{C=ti;*WBolP_Zp(r0i9go?A9s7|V;Z(KrT(|A__ewl%vfnVRG^ zwDT{krOR1AcBgs`MdrQO2(xaX?8z6Xgp%!CahU$_e7T!-iQk#26@>x>h^^1|3p}Qs z$&?99v5-1$m~d_-mzDFA(e7KoHUKEXRR31qQ?|_0)qS3Y4CKw>yz+?K!CWyjg;nbZ z(FlC2%2Ip%xgFBgm_ab5=Ut}P07?SC^~Di~wNmzFsuKc*TREWlXeSi>Sz)Sn5VYM` zaiG7o!M7gYhi?bnds~6Jg^B^gYqU$u7C7=y;edbHccRDSA=g^!s(n_OLJqC=A(@}| zaP@va=i3|fDiaZfy@rOQ!SR2An~voHp|2_+Hq8iZ=J^y78nix(wfr%;0mUw zunV3vIjmGL3Q;s?KklTw8y)o<@IJL}p%0M(Gy%QN+NEhLiix60=yp#X5G*E~*30iw zx3m!^Iywm*a4!z1QA`@M>uelVT9kubJ;KbwzwJc*a#>QkH3q_ z=^jNjZ$6Nw%HsfPcSB8`%nyPdS9bNw^D=p9LM2VxiU!qVahw z1wEH>yGD(MuMe1GmWS`y9KS%JWyN+~VM~OpQz-iWV7k)b7FtRxN?-cG%ofYyr~oq; za&~cg)t2O(DDXtkv0>IX61^o}aLgJ75;(89a!(;}o5@Mm&99dItu&14JmjnbMr}ZI zvH~qRjN*dfNf8VyjqMz9zS%j^!hU9H!6#p|o(=~Js=MTGE|su3=olT+Wy zm+vRPJ*Pm(X6X>mjvj$2EY*(LRabjADe&G;v#WL(ioU2rF_+-!C?HyFJGs>omp)Wh zGsxV^M4Yb`l~apS11O)Gfp_7hF518(uQUsuhhz^pO<1^>5}s!hPaG9qh%uxefrOTsvTErEFV`V| zY4Oe#(q97SHMweN+1#mx$ePA5Gr9MZVD>Czv)~a$=dh{X+5f4n#dYK>c$wACs-5kV zLzasI1o4rG&)g`6Qf!E2{gV; zh5_~pNo2IEiySB>90IkS5p9iR@oesj9hHZ2tT1-4q#fs-mN-vvScp^z${8$H{p{1) zFdvI15e@t4uqq6jFKgWdcP&J_!DZtJpx3&O*i=!L{&TB(NZBzWy(-X|Q}>j{X~*pB zDMcwduPF>csSpgLD{=vmf_8=<17_1fsdBqu~EB_5}%}YCv zs`-DI)P0%g;b;HHqW4?lr|!2sJK-_0#x3uF%W{tp#~O|nAuECvS(a?Geq&C*-Z$qK z5_vC~YceLHy6d*`|6Q6nJl6PI^bYdi2ZVBFe)aqv^N)gw+P(7pkjcgZUFuCOTGPJr zylt-&Sn9P?5{Gg7ndb!B_Os7d9n{(-WtW}sqDY0{7sRPS>FwY-CJnPR8{XVv9PaOY zD!Rff#((4U7p>6j|3DH!$x@2ctq5&_>H9?1%hdD~IP~n>UgwYN5!8Po4=Hht^R9_K zSFi4befJ%|dSh_yX|J*Nc8#;$`|d|v+L<@QtJkD$7+-U>nS_!j4zAX?Y))mLs1yh^;BtL}!@c-ps$3p0yu zA--Hn=-U;6-)0teOW_^@A`<S(B*5|)R! zAq7loK6f_^*Z{4JC#w!!+7{JEg84Lk6(-4CU@kC$zHD`+pjdy$B)RRywXuI@OA5L5 zpF7aort|^Sx_Pm;XHNeGoY?-bU9CgHqU~-U-s*kXSzFF(f93}odI#L9)#2uDHv(<> zn$o+G*WLp?Q7AugdP{KZFI6*sK?d8 z$u8ZeqoMJ?&7L!J5?0ORyy?P%YKpR|7UJ030FN%u%LFa`Fq2V}{++o@zfo_6=^bB> zGP8hD`0Moo3^8A~u}1(G|B4&Zx@YZwJ-y=b8+*p|4f%2?e3En+ty`Sh-N)p5LbGq; zK*ZH0WmywgWHGNQ&sWV{xrEBcjH)%U`eM~mYXe4;XHdOYtvhJ%HZuXgJkL~dy6_8= zSm}zo3RUgV#{reMRI%2ilZs`or)Q|D38o$xc+*P{|JL#-l5^I&JE(P4c8N}+xOFFg zq~qw+keZprc46kt&#KYUdTGW>xA}ds!QjF=o_5>$*1=|9zus^;x;kaP#iSR*n_*%P z!cUdXjcn@#DAz`qu&<0rKzd2Ccv7kMF^-fRHUk$%(p(%#+WdT$Wtfpba~LCu$NSkO zec!O&qs{tU+S;hjvA50XKXBpL8nOc1`g3pKHao6`KB@D0eEA9P0%S@x5P+nt>N&)} zYWV0iqh8F$@1;Iroc1}Ly(&sKDb2O=$v1YFd#{}aR%GY46`np!8Q_R>QHtrvUSEB| z5cRSvDTnVpdb3i0p6i3A*k=iwcqM|Af|=IBGZq>rrX6b&&3UV6c{qH%rp*#O;R_a{ z#hSh(PU&lkEm89+#cxhM$FNDks1kU0id`;~oarFIE@Ra~4>FeC^s@`Flna$9cVaeG z)J?w2)+Kud?PdNfx(x~QLrX8cl~eWp$?K(f-AxQ>{7s@xuH*5aW<`&|->{|+oIna* zXB|p_X@i)3@_^N9=skv0KDw&655?Q{zuGe<;;_+s)Vg$H^KRH2)d+=@wq#sA;*9t4A3n6~$!4#$g1AvFBDAYCGh z(Jx~pD9L0xo0(oYA?g+!uwWRP;o|c81{&L-(Ivk8o|?$ z*Sy4+_CZQ+gJ}2tc+HI_3UpwU*KxRGl6Q$O9mvWLRGG>eoc;q%>HkZc2F32FGC9)s zpH^`FP~BND*}e+muPB+*zEbQ_bC)r~bo-t(!GJFHJo#}yuz72#?Z6@hgA*)}Yd3uB zWSYA#7))mOps3h9d*;RxHp;`_@do^Nr|6AE&G5J8r+AkfMc)U^iL!A4S+cw%MU%9NLWMBL*GgIfQu80l z>DsNz>u-aX9N1T4G(oj+8)Dvtum00!1f7Bt?o_1OL}`$4Bw=83T2h2_W?_j3F|z%! zD!)#pGy1!)sIpIPi7^K8y_%YlkEKqRm_9JF;2b*94fc$UQTWqV$@+tu1kNpMY|D<} zD-iDD0+-$&viuKp-GnW_9HR>I6-k?L)@3%mI4N?=MU2;mphOg# zWt|hE^pVJ0`CZ>N^^r0)p;QBqGU5AzOF?JF4!Elj)F}E5u^BiLJ+DZRN3Knpc0^va zJGqIu>p<<_ul#3nki-!a$;0WE0Vw2UJT-Ne^1ahbu7}^B#c{c`pZ6imJ_(=Z9^yH+BSwefXNl}pi0dHxK4}Of=P>%ZH zXHu(tfSJLKw0Aa6eD~O2*LGAJa2j2+^C17ZM?AOQ9Q@4qy>`_o1(o=7QolrmtgF*7 z5x^Vv@Uy*??E+;zl9B$4(+PC-VuldFYuFgryZb~N?u>PLc5zlKoYQ#^yE|^92Y1OO zVD&;c;rLoS*Sboql<|e~MDpellBlM*#LO{kINKZ+H3;)bsMX zEbl$h%a^USquV9}E~Uc_s(Gd+dK{GRh6LV?M>KU1e^Gub{p*fAT#Kx=EUKJ)XmDvn z^i+KQ!z0Lhx5!I(pDFoNyDM{yKfaAN^r;<}-#DU(ayst^3W2B=mfsot1e~R`Y(H3f zxK@AUcUjvi_WipVzbMPkZug<*VIy3Cew5G8*^MCiE&C7V=ZixV%737%0qciC9}PG> z`EE{uZhWbo6_eZ44^(H5s(7!KZ|siw=oq0Ah$2SJ5%{$|f9DseG_w5R%=xG+)HvC} z|3maa+mE}6;!onFgPTvNsy>a_7J~m8!q;e0FtdSw`rl6m=y_k9TTBOB{%dVEd6V64 zJ2ZE~K&dvY1Fxf!>(+ZE!gXs?!y5F&8zovqa?nGIs*4Po1bOc|yI*E>6G9H76WFc& zeo$>~d%V6bUjF(N8t!zb0nYXKPf=5xcy-3`mAO*fg7DWb5EWtBQfRe2O$9|MeYqz5m0)Zi zgv$o!At!{q-_M#MK#JZL+4COK;_CAabv92U(#}FvS5j!%>KJvnM`@DCGU>bO6~DZ3 zZ1afyk!8)0yRE+^amGGR~#8gj3IDw zcGuL;xJ2)Z&PFBF=Ub^WFh&GV%(yaQb6g3O@wH)5pk?9iPF_u!>mL%p& z8z>coQEJ+$%qrf=SDCbnxT@*E(+#n{#meV z1gmw3=~ZW?DdPmFqbs?9j(zf@?H&NP>_x zIfRJar<`ddVmU%oEQGgzFT07iZD&Nw!Q3&9ucyVx`~xko!g;Uri3|6-I3`x#@-i#P zX~!bY`WYx_b-6}NO|75%fFcuB;YeYSo(Y1AN$g!CgdPr~E#Ev~0|H^=xbLbkJ^;%; z-F1&nAF-5}{y2rkNf7tgH#JK0(mw5@fDeF*tD&IM`_SpZSK+h)6lkwkI7zwWw^<5_ z5%2U)WQ*+=A(+AqWzgfnLO)t-!{7O36P^Co_a9gxZsF=Vttg)eVYsKmO!tIqINJVH znc*e-BqRrV(UM?1)i2W`37iA~rZb$=n0+M5n7G*qrGU6BIITuq9r7c4&qr=2>omI1Gv`upKg&_NDtg*KRV}y_HN(eZu zRo9DPx3y@Qg%MZj3!ZE;BX%X9>|PRx!R#5Aw7tMIdf$V0!Oqspv=i+@F@PIG0LJI( zBlZKbWqFNtmv6mf4l^O^tjWpz7mG1qJ5t1-^gc*YH8g!i_BQNbmn-noRy_qD|8o){CP!A_ws9#Oz>^~5)(h-({L_)}_ zalf3Iqq}8YuWu{n71igJjqU1p;4e-qm+w)ykSm=M;uyT&7{ll0B*%jYDlp!hX~!>k zmL#keOG?HV_r)g)(eNRw3R~n)272AqKg34qfXV8i*@E*t1F_Mhl}|m`pKh>;e9t66rklqlwxMyrfPce$UbD{d$0&sO7N3G&K~nu)b4 zx-C5ZD?3KAm?G^Pi;WSEO)C%IlAIY=aMe6d3qVw;9lNhm1~970875cayw=yyt50qZ z5vmD*n#S%&=bIkbx!W}(dZVO$z-M*kSNHWgx*j2EMC)B4wl;W zyl+dL_k7Nw%=B5EGDengdhfKK3YA8L1!s>@u;0WlC9hg+CY?o1|AX4d%>BbB2jBan zRpKtb-Qsui`yD~zlFO7q<;qcA{8InsvEcyN$@7%-Lssi23S1yCru&Sm3VkQ#Rq_E$NHiZ^yi8U#z<$1d?Yaqx??2G0 z#l%$I?&faDVWQ0EjTs^TQR3J+`6VCdi(~Oz{op}z{e~YV`{WM8rUm+IajAO-f@!}Y zoQYha@-00E$V}{b-=m< zpX4O+)0yBxfmdEtl7@rauMLo0=Y3v_%6C19kA9AO3D>|Ua(Y6|YSaCE*!rcd#VI}? zaX)hP((#yh`=tb?{Ipt$NZ9z$O%LIPQQ9j@tAj^3J3?ja2_|RlpJskPl>ZNup-uNe zYep75EdX@KkvkT$*HQy+rn%BY&3YVKzg9oT1vveh%DMbB;W4v5D%9c%;z9NzVU8`Z zZPHzCZNIqx0|&7;eB!&P`H%eFt(8&E0SpaC@PTE^#EA|6*Zeo{546S-dcE&%LG|_~#4)~dC|4RHQ z%`MI@Ip!u{Mqg<7tn~HJe<1(6H!=C^BH~;Ni|wa90XxwaW>oHzF^on~6`PHV8RCn| zJxQ@sclDI#s7C8DeDt|KX3zTY`hr$P7p2o^;`=~|%3EL2qQ)qticYD;h;-?v_4OrY ztATS&^>=)@x#wU0-`sAxgbL8wchOxW+{U`IXDw07Jxv%N9oy2JQOTnel zS{Kaoga)fqmp|mWh!HXD{>6S|i;raLxFUJND+5AZqJQ{{%rZv`Oy$^c{Zq;O+3{2?OH4UM?YKxyHnDbg$zgABI=W&R%-?L zz3h%M6RuBK{ASwwxs;KOPhhUT?w=RGEwbl6=nO!oW~)D{Yg)3?+h5~`RMd{T?0L+7 zue3F(46}KU9T2NSP}ZocnBB2fnFW6g1~lZLEr;6h$u!HVn5B^&FYL$ie4idE)+rv=+;HZZE`>i&2` zLTNotZ?^T8af%obxhcSEUC@?M&x_GMT z4SQ7l1F99Hm)wAo%qL@VM(GD;1646)`+`G2iA9r?Ln3&3g9P$QPAl4!gawJcm@wTb zgcWZ`T4k5o<$(Eo{oumKW$FFSbLExLdap}!m;VC=;oh-=_Z3zxs?}-rS+e$hr+f8v z7a4ztx!{>v7b%{zF#=IAzH}a~QT=bZcK4C6fI4FyQNE`otG%GSbg5b-SZF$(btUQJ zRN9NeYS#KPIs;h`6Qs~YL3xdh|DSDtfT(w?N=kdgW5IHQcrb$-2ObXd(J@?q+sO2( z5Q3M+O}L0?QLrI^PO)H&SPj=;Em(;?tq$|clg7>GTCV!fx!TlkC>PH)tR+E+z5YzB zaDkwjt>SJKJH)N2{U^G&;S8n}RQRFkoKjvEZ?&D}@dU2SY4waRePnTXqCqNG`5ee9 z3VB;m)mG6os{yhAw|?n-MdsyDqJDAxmRX+4tLXk6G$B#>)OKY+$#222$m?Fw?vTg? zb~T{pazz&tAOj9a)tcEFc3RLlx(kMmS}eKUkANq;pVf~l7nrO(%HfS(uCdWH>(mh{ zD4WN>qmzeaFT3{krvg|r(Y7B8idmERH7U^-E%$l?FG1?-)P9UZTMad_$CR{eZwpspE3LbSjKEdOp;Xn8vMF>mJ9FQM{YcD+gKY33!Rd=k}+0vv@%F;6f3tT>8#dx7;BoVa5(B@%}z-UyI?crrSDgXcR-#hR1SX~8px7r z0~7ab_9h(!7phbv-)X6O)esd9Piw+*!oR<&r>jp{H&@si=+ly0XrNZMo3f7aBWQ(o zY^up~IZT>a;PJ^4TFn@%+uRd3{moL>4YiCFTnpwfqtGYG@tar(HSNi)6kL`VV9|smRO%uG5`Kfv%oV9-C=BtE5m@ zNx5(2DrS`_x$$Z_{VyR#iRw)NhR~|Rbomi%FtjR2OYr-ksXZ0NPE^^Hs_ldf`Q2Cb zP}vdlN47>rlVi5@#u6-O5BDh(6zUIe@{->a6_4!rc3`1*dq}AMmC9FCYgGW&_W>+4 zuqqC4x!S(a^wVp^8P{@M@C;{RKeFgAH~ozz%e4cw5`xBRiX`t;*+j4r#t;ueLgB1=L# zpKOgG5|$>#6208;j8h^~E}Vlk(y9LjpoV1-?puv=eBp`E%fb|JAz2)+Yq^?_j!(FB zF_pljGMKqDuu>LgMrEwl1st3+D2O-4B3Bdg-pOo`nC~gaF~w;`Wh{SSx~TQ=pKRGm zEnNh%VC>Mxmrl6{q%z`h%(q#I;kD$o&KsF~K<^D;N7lGaCQtE;ALE?E=Y zj7(PP*sj#*=4xVWr-8{_Q>nO?A2%hB2}*JpJZHKvj!k3mBN`92+OPb4m4|rE<%--n zKo4UFKa>jz-*Q3H@9Kv*#duzMlnDZ`UPlt#+AHz{HD@%K*)S7h?cr)E_RH)&`)5^H zxHHu;6^SoBJZX@UN2}UX2aE3Wy1%dfa!|XM*D~_5Yt18pjO$XTsJ~Uzae!xV6EO)M zO3==UxzxpbY|Hsb%6C8HXWCYiNz1B(-wbz|Hq`*k>^WPCra5f&&@h>$brc$F?)KO? zP1jpOZNzWVsg%D<4Q5&*$`{O^tAQ6eaXQ+V%MQHN8zO&AThMK~h~|jJ^Ubz41!MM$ zChW8dpZig=Yw288|NfHSh1ro53vX)${U}WUStrdKH~1~HuIjz>og|*R-QR}|J<1C8 zdC+?2>>it#O%9GP3m{lB{Q#|5Vk`70>zae|iXBezc&Weu%oQs`ElfnGf;ou@__hos2POE-nzZSha~{NcR1^ez5BkbB*i*nd}M z;%da2R5;wTZ8Mq*yqXX5rNSkWzEbk{e_0J>q#E; z)mj(5Pz36PR{zEW{{yl9VY#!rv0`1VcW>uI1aIU|&Bot3hBR{zeV*TNN^zcFFNf5h zKz}6xtFBl6=0mz4CGiQq`|SV8Rm>5L?OGWsGX8`GDn9(eZ$P9YgzP}B0^-HF7A=iR{T&==BIq=R%w5}rWb7C zL;13*>iFeHp+pym(HKwdr%9fUrKqmM@hap&pW+zZ(@(_RlJzQceCG88I*E`fMhPwb z+)4^Y_e|_wx0Svs4co9*hyr7cGwd8nF9dQc@3bKCv3TBuhNV=6HA{y(69e)$q_`0}J?9N#VR! zgI3n>C&Lm9HN&^t3r1(kW$B2Ld81yeXfe?UGfbb_DU?uUuB9>fxK+u&jExxkwcH_D zb=#9&Li>d5IInTQ4(^7JO=u552&gJI^%;Q5Jr~JO_RjsxWK-KiS^=H)-i5irOjn&* zgNwmjUA+&2XbjV-9^>(ZQ?pmVoSKJrz6e~2j}DO|WU>%2tR_u}2e`K^Vmn+}9)IOOkIoAC+_ z--k}b$^O$}ZtSL?58tX{6yTM#s@I*q+kB2|lbfhcKjmIE|4nI9>)wzbcDB*Hu@&bcPG;IX0n?4e&1qLUR3?q3?`Nft>-jXmK)-N`($269ogzrhm%x+mJ>nf&b zk4Ov<)cyl8bSg->6o1S~MD*azEQ^niiWxpKI)w6k&uLe1D3aW0fy1w98Di!d#HlP} zqqIk^&AqJzmS3EyR*k-mH~8373qBUH5Y#R?V)ja?f#xVyntD`|;wM9Hrfm>FyrQt$ z4S1ctXwh3^SWJ6xjn?xN;}zZr^pT|GXX%MzL9=&wRO@cPf@7N-m!oK7#_jDO;hst^ z*?|R0{j5{rN!_(;%;**rp7udsFTgo(9ogCh(D>uH%P>r1Xslkj@Bn<$#GPu0j`ITd zLmZ3lkBGiih{F=Pa z>g!JRzt8`_??^F7)r2tjD6hxxv`Q7(y411!Yv;|^%1-&m7g zslS(Bfq$Q;asu#NBiA3cgNzk!3uuD}Z-YW?Z^gG_0AL z8teC`Qe>zkT+zfU!=usTO6qH4K7eT77&cipeoY5(n*!Z;gce`yj?pBFTW7&3CS;dZ z!yEx1_9>+zc5{p`qXd34BY0i`cj`&%I4`R6M0LacE*D@LqtwM7?gA!`g-r&?1KT(G zu-!vuj0yNznB+2n5dmg&d;E27{=p0!P0m9A%ELK9zpeG~(D?Nz;Qlb;E60Z!P7OOw zumj2ilIODj=@K=8LCZ}j&S#uaZpw@9+reC;YF2sO5`y?V6v7NR$AxbTAi;Ab3#yO$UZ=KG-E$^w z)Ik}Gx%kP}FT}LGL#M10m_+1hSaf}V;sVz=eL*K>EsHYSPA7jnD&9G2R}|qU|A&#d z#7Zrts&rl{QhlzW5XDgcSn5BJZkCS1$r0@& z@Z||5i!_^hajax&f1S}5c%<`-9ZtA{>VGi%@~R~O=x#h8yd5^3%;5nhF@}y6>6KI} z`BN7Orly)ZaNDrq!%oTc2HEPLSS3j{~!0x23ou;UZXlX^fIT!hOM=6w_H z<(&SURe(37{Ij+;C|}q9HfOJxzV^~d`51W8K9+bg{J?Ch_YDZJ(c8E<-!Be)FzM_! zsx(d{H1QYMY{(B(idu#ZJr|6@5u-pLtn_p-c|y;Yec~bFY4ixNutzJesY4TaT9xu7 zTGlEGlXq&$2tu65uS~$XYO_U}{f760<~W^MwYRR8`nQ6kun=9!7-j$FD8B?Z87KK> zy;moY4{FVvk2M?l_6QIf(=AJd1(g*MqX`D+T+XT$70GR{m@Tw@8c3)*q%^&e_viK1^UDOC6e>}QXN8h7~hIwthI;uoM>vG;O z0TLOJ79WGZv!Pp`u^OA2Hwk)H`V6I`I$M9S~zFX%k>_s%L6wv555E-{+EIC@#7>_he@ zk7Z;==YW_SL6%1mWuyv&ubZ7!zU2r7jtN|K$v}gte!lTmEZ!IzJdnc21tU#P%-=ZE->#v z*}Uu9>&8;Ps$BbeRvO@#h(-`+0EoFHdrfxq>Q?AIB@cw{SE~4y_zV8CB+o2e2@98- zv)ijgxH<8Q(@QO}4G_bUY#l%P%R#K}*R#Es{^f&Ll!)0sQHytxFn~Oj2 zaD-p>9G5Kp@pRxle7Vsa{h!I7yubN=zUX}=m!el@^NbWTj;iCTn~#k8R&N*mJ@fJY zos-qpR@IVMQTCxT?{~73x(>d)>VaDv?l%<&M<5IRTuKG2W?H<96>q#y%t`{6hyKa? zTljvd=o?$aEwQiHewsi5_Qrdffya(*DRXQ3)xnpY8y*&)t47grZg8*qlWo!IPM>tJ zl(x@YMujy^;Ys5FPAalVrR&j`h_5?612^Blcqj11y3hWHYfRU#$BW9Jg=yhk8#Eil z-U#uhk$=hWZbyu@%t-uvWg5=W{moO#;~(v3A(uDLsN&iA-n`~sYaLLs7-nCv^SpZZ z>dg7wyECt_S>km*ZXRVB>QlM1DIYIVvu)!@RAXl_lTyvx+vaucQkCcc|FA$-swhy` zE~xs4hyb{YLFDy(>cCDO*6SYf2k?}Np3nZy)ktAnqRdbZAHRK zyq?&OrBx6Fnv>c@DeVI-8hB4h-7~Uk z=V@1+I<-Hz??P_#YyF|PPDZG6ZLA}A{q+31B0E()1^DeXMfN|)5p#0Dv+@{+n z5tL=`&4Jk{V9hl<*C8;#W|duf7|lv!4q^|&YC{8&=~lKqBBIF zs~zOV$EG&%FXwh>N>K$Y089=UGRk^))2raL<;QRZo*C&jFuIDkQ`xDF7MhQl~X2IPzieS7Y&KsR_B^f=&F9IKW1*o7w zV!l)=YUXpuqF=UXi#|)h<#JqN_IJ|r-RzFV0N0;~l(~`eo@>d!TqJZ~k77mo+cTLl zJ0^)998NLTz{SK&t~SEv!rTw@=+bAMNz8dhNxe7{jCZW1a0ya1SrWK z4lWRq|DP&grEu3{unh?i(o2kHU|?0|m(+y6irFEc)Af?eM8ePIG92#^i=vi^PGih4 zp1@m-;nk+dDtxqz!A#UwU%Q;Fk_d;o-{^~tPKI!;zgc&wupzI-*+C3FUhoT?qpH5! z&TdDf(f?4}$~OVFsVI5M@V$*`O<@eHbo7WG3NCmge=(JAhlvVjQqR_~Hh%Nb@+f#; ztujuRjxKz}b3i=`^ohfyMp2U)+f%Auks_kG#>PXS3y+p{ah#LVngIM2Vf>GAY4@;q z*`;Sq6&k`gi{7UFDWLN9n{~}2W(@TC4ODBWIz+dr?&zB|AV*QvcLk@58(L#f)ubpt z1MT$s=zgUXckV!NTeVtXg&{B__*TQxiQN~uQid4vc+F1tfGy91U5R&g%9+(7SHp{A4P|mf1=UQM zcjdb)|1=Pa<*P6AA>~2-mFZYt$P9IHE@weMJc(bD_vbp9awVh-^c$euUju&|uhZh< z0;NWeXgnbn#2mth;o?Ie#p%==O99-Qpo)jgdyOIj#1<&eo5uTcTWM)Tk}y0! zw4NsgE7@QDc}@oN8eZe3+*VfN{6iXdvuM(=v1C`7{)N;zRie7ZO-0Z%X1OCjgO`En zE7ADnOw<0ZBYg$)yKQ6XF;J$R6ou(b{i`(LU;T`_uS~7lyNx%(@k{lvv&tttAMRj< zCxRfPP=?m}=qQNH&xtk~M!#Y7eH#DK0w}&T&|SUBjC(fT)TZICs%ZD=HDD?geYSm% zK$f@6lZ-Ob=lqwx^G)LWYr&kIDo`qdOM| z=UD(>j#5CcJ}XNk*?=89Y?@wl*_^HR^M2^W6gFgAF+0_sVv?Q3>ewKotzp;Yi`X^V zfG7d_sva@nH?T78KLB}+(2$0|M|At2S%?L-xMOc{t3+F zZOzqImM_dNdR)Zs=LdNqGp4sc?0ruWfoN(PGZ0hz$k2$Tl#s@hEd&revj-6&I zE{wA);>;-Rx;+2PyAN4x_;oLi)qsUJ$iGqLo0CK4sk4794%t0opN3>pIR@>U%u3$= zW!@@$)VConWnt5Kq8u<2aEJ!W>pIN5{54m!s)1O`#%5WaXlTgS)OTQ9 z`tl#W&F&xWqEA&`Xw6*CTNdq;#m8{4yoO@_e;{7Ew|8)JfczaX9A`Txq<^O~RCq84#4q1J?9+K52uSA;Z zlz)p5gpnf@TqCSb!`N3&N+o}iqJa{Y^E;s8|ECv2cN8#z=>2qovrHx5!cN!14 z`cM}Wd&_sC6xzB*E9*#Qi@GoK7E)fy5E4F2zPS0A)pGR^@Y$bi20f(u$Ju|8-Q@B3 z3jGS4$kl(|>+X?JB6-tyeuP=kJTuXm(`+?VD{gmad`^SlEp7@6Wo|khNpc)G_l7o@*@P5p=fI;_lWC}JG-+W{b~#sZSF4{m zenK1g5^Is&p~3Ybcg&$$*Uaop;YYqCas{=0+QAK|$q*qe2vy3u=)JFEPVc|l$P`y@ znx?0^>{vo*7?&R7mjvA1rQ#ii{?MCA=< zK&bMXd?SwK5jv{8d<`v^%rzmjWfxYX@nYI2ic(7(>ikT{5oNGuBi_b0U)1w6Eh61? z?Z8sc5BBmy@@5ZzcyQfALHF6m^6#q&gwYJGOD6(*y)L>l*Ls~La& zeE>W;*-yI2w=sV9Oul>NYuU?SY?$ewO__@ue)Z&>u2MTznS?=K6QtB+8fL{UOGp$S#VIzGYZU ze}deQ*M4azj`?XCBP9W>WmhvqXM$7^`2(=Sq`tR9&N=tlC<{|*HfKvF)6yBLEK7*s zl~D)0FB6@fB^#<0=+0C4e6=r9^??xiZ*Vn(0yD-S>zS(jimZcS;2)h5?Y?kIXcLb; zKB=-$ry#UQdop^W1sV!fVu**(Tx2&~i=NNd{VFAiaUYLYlk~oHyp;*;_i}}bI-28{ z#P`SS5@&S(mX*Aq7uRgr4k6q?H#(UwDjYG-7<*EIfbD;hUNZ!m0A{*tu}%cQrn^M= zQh`H|s3!f4SHBoqxSAdUv5T$a;s-{9u0F4b2+n-ZPq{i4yzTtx!ICsbe+^qw_#6x$ zcwoA0`-b|`4sNmiS?u8Hmeo6R0Pm#e1f@*QPQ`=p&H=RtJrx>5Ry6FG{12_GHus|l zJ+g~G19aLF-&xKy#q02y%m3TU%YDK&Cpn3G`g)aBYYS~oWXh^6jbk-fvmN-72cKM$ zReahj0j`2EP?}%dy0vx8up7Xo_3F;=a@o;&jgN3J#53rtqwm;B&0K7EbTL2)8;ca_{+S!6>acK(c7K@&0rFS&`34(Hxs`P z+h}^&yp2icPxtOD>{A6`jm1~IRT7UlYxWguO}NQa{h)x8nqRvVCFZ}?HNPdn$q*EM z)%ZLx?5BcuM9S~#2yyj=LTjGyCJNyBZ7f1i?~#-Z%VX6`0xQaG1uP1kuep5E{7-7H z{Ohk20^}I8`+L#mG|qvw{6-DGXL%LIQ#$l2^~aUQEt@xRg@UTb-RHG$wkK+-Mv$_J zf;V+m-mBY;cM>jaYBf11d-*-t!`Wi1PI*(Gm7Ho7%%Ao%UC3f!ZgH~`cwREq;uDrb(t-(Wc=$)$(iDn{xnnS}GQXPE22hEM86xb%s zKN9!3>@^kGOiS*yL6SpxG6#P%8`a+zXV&h5lzGb0u!H!YSAL;V`r$#U1m$NmnzYea z3RP@vdUhPf#z0WNs()JuI+fdb%0rC5`A_4{CXFpL;k8&rT=fkSRv`ddDbV+Q0^Di% z>Q`Oq6D9)B;EdF&&3mKRua$b=w$P7DhzFBX@{@ii%F7dejoS$5Cm1Lw9&?>Xc47mn;>S*s=bA(Sy3aRMvNMzsu5ze zs`iQ?M(tU&1g%lKMyqD+O{wIuj_hTm+J7r&dPi7 zg`a8X7(jUyLraYEX3hf)5jUGhn-R;^S+e+ff}HuXF#?!NVd+>LqXFXas_<7W+R@`%mGFuw3AqpX0{>STE@%vy)y3?m=5J+KlEkwmPvZD z(zdGzQE%7_eGaQsi|an-ctwS6Z1-N;}nua_Fq^v!_!jsBEJtZ4h2l5 zijJA##kHk{pJPRgX%?Xr@g7AN3o8iZn8IZ27qUQxFTbF8GL<{^CzqgTE$GdHMW|F) z?PqNhrNomLxn%|h1+s?>esbnnhH}xJ6g|$WAyS+9qaiqY{ZZ5` zItTZP!B2h1PMrMYenPw@})ymWvaB|g=jH) z&y>Sx;i&WgT5KqnF~;gAWmY08P)Fax9t2s1j3;hPl-1@U=J&dpO!c@vUrpbK?+;ak zSlj_q=RNiGGw~2rC-ggdBY^MRM&Qkbw?iBO4?_D7ikC#Q<+446@QKUzOmy0&h@}bh z>LkA=$;jzcS3lCVx2AZmA&Zhk@R=Wa<9go^3S&_OR!y+=;^Up-#<>JA6}-gDi7V6l zhNnsF%4`_xs!toCAQQu6kDIQhsc3c5m|Fo@FVLTx-DspH{KSBbozdt1t9O>6G;aVV z(Zo7LKbmMaaX)**4ePji+r9t9mN%|r$?uuIUgULa0+XMdS=B8TTJ+U=AnVJ81?PFQ^OTRjGeqdwu6zZ5eHP))=kExy%I3K%*pZEWFwd$w3kkrp??ez+( z$o!JP3tr6YeDo(NlE}ZOoYxGM1kzk0aPg6ysQJTVO8%@(jIO=Fx7`a1Cvhq)p@zGw zp9I;_m)1_2QHi``N!k@Ib*WyjWhtF($Y<=HXGVWZlrR3=nE@Tcqr3m{)xK{YuM9=F zE^x4*FA1NP^wYh*E(GG!geAd6IQ0LS6ohiBSI+JH7CG6H3zRAf*#-7oJ^m|;WVOoy zYLX4;yPT5vycR_c8Xq*?{v*o2vD1i^ekvAnGbK%FK=gRg=eyYlcYafvq+aKK@FPA` zL0Lt{&D<`r)`WzRP%uPWsw@gZY}-TF(ZclNc_ zk4p_njJm5Mb<3#OhppP|ik4>dEJN6dNIel#3=O_9l=>(Ug_|gk4_>91qy?!ob>=2# z2X`tvI?GQ29|?Rsffr$PMr~_EC$ar_|&Dm{JFR3 zin^+IKqifGd>wrIP-Nu4y}yipHN`AFiifC>Xf zD2TuJj|vHDsFGY*h9^Srz)O@?f7hOq{Y-JL`0bEj{M_INjntw~NtgEf;k3wJUJ~RO zxe@#F%M}@Y5+e$-`n~1npHEh%%q|ShZLUbZ_ftduFq~jGOg3|A>1unwQMEvw;G1m2 zA3b?WJX!JYw54@cO`dlyVX|Rs2J|wW+px=~?TZu5(3`_Z8=%gQ%AXeBe2t1>Tk68u znD1FLTo>{jnL;eFY&29x$=YE@sp(A)Xr-q17uNKnzp&QNgSfL5N|jJHd@J4=U#Z?n z@{)2D`}X#a&RTpZ%Nv*>orYn^NF!i%A_M+E3XgjFfc<3vf(EJo9W}mM?S^;FN5J5{ zJ9aHHQEID1%@ThX)lV`b&0>S*>!MR#bzb_`VI|qioa7(MuW!ivWaxTpT*c4mcxslc9F3&2 zb^A&G8%AFp19lnI(ykY6wp@9q?atBN;#q&!O3-)7_5x&d?EVpWx16;7p0GG;^<+5o zz)l8piPKzER8W8a=f4E3kN@4b(|IcAY;foRFRIFW7Q8dvap#9&x{GaZW=SiZqEU?4>C9li-+w-q@P3u$Y^yRd8F^m`fVdSw(iOAhnh>=3USbnmHngOIilCV}-eZ922t4E|>Gj8uK%R*W;;b>lZ7RU=DxnC|xmu*9#{5wz zu|JobP8>qNShF!4NVH?*F~$k5SxacWpIb_rjix$}t>i;Nr>5B~kj0Ut%*845yM7~8 z+3jN^bdY%*Wb{K{ake%Nk2RjrlxY6vls5$4vV1v z;#~YOxov?lJk|V5URxqX)0B2yjOiYTf~NkMk(ogh-~-9-l5cuT>__afp-5(mn{silzmY z7N&*S$_KnyrzMK6r#0u*nHr8Q%p}GA`k&IDSs*&~w0P|0a0VcmW@1?BkkFoq{2f08 zAs$igKb^<)Dd6XZuUUSZeNnYz-;*v^^UIS^Yij>2EAVP)h!Y5mEHjWDTmB@;+^V5r ztLW~%VqErnrL|x4_2NX!4<4h`C}qGGJt0B;dBL5lhwh%4P_$aKa^@eWf-+WZ;8md% zdK#w#u`OpnQ}f40(u5XEkQeVS2|6G*EUHshQmz9<&tbm+7fUhmk!BEfBZ@T;z@hJ> z76AaM8>b{2Q4mv0iGyz~7y~YOKI&%*R@Kia8zT?@^)v;I%Cs>26Vwk-A8N3*1^Aj` zrf_31=Il83sQg(!cAWo0ApsYgI3?j*6Ekdi@^F2(SHy{w)ch$FSI}Lq<6^x7+ zpUfk7L;P1NNZ)xs$EBnNf4JlSHQqeGmFOnRLR0Hf;DbTHHXU#$b|XGbEu2GhqDj)Z z_~fS6FPXvMvI~US`KP6)eI0A-Fjwzax-O}OyNowzf7gtzw& zbeD~5%hFN`vA7P?GJLy{4Rg#vEfQdMO?uXGV!%wnF48@f%wwKk3){4)eiaXUbcU8# zy>P|s2?>*k!(78HidbA5(D-Z=HxPp1Z$-q*_`8VPB4{oFAErAk(r$_;n@YLk^~2Cjc@;r{aM>N5Fd z{D@*kNeemvcS2C$H|}GsQ7C#R0L4F=3dvaZqImm=bU}N!XNi3ZQ@C;U}7v*DdE-OLpfDh$Hlae|Enue+~!FeGy9bz&!SY{Ux_*Ab7dSqN}!4FLI<-( z+@(S$$zQ0s8z$#Z{?`8kodZWI&sFEeX|Jfb4_sbE=Z2BmH#fV+r@~IRdAv@8`o-t6 zk)9O_;>IVB%i8~5glZb137;Fxrj=QC<$ltX{oCw%m7XIWXP1eEGdpeLM|#}wujVev zMkDP~ziE5Fj55QAjBWo*l@)v}L20Z~Q@Q>4eZJBpe7$>>{zo0RUASDI1z-5rn=jC$<41wg}`e+5h)M zpw-c=(>dazee7J{AO~{5U0hrOs;kwVB%XDRQ5y=xZ0_6_(XR zC-Ns|r!Tzo(wDg82#Dao#3!khoe*Sx!A4&Mb~)mlQZ-taR)<&K$a7WR(Br)L>crfg z|AG8#pHyF#B|57c4PY}cE9iS)ah&eFb6_|+bB4nu|5!{BxSzOvR5Ai-UL7rYh#uyO z`j}YZ@!gVLTkfbZE4_%P|CuWzh8U${(lAmUr)JED?HdHO*;?CJ-N24|%ne(lJ31do zgK1@Y-UUuE7rf15XRE*a(t!aK>ma>b>ZgV(_DvMV1R9a`Nyf9+Mi5BG;HrL@icy9^C<%c;h#B5pH3s!|tmwXt66Z&j>v}+v zDDSFm=q(y%B-FY~)|7)|O56`V`-yl4bm%$8%#@>D*#BO8G$;`@;s6iDEP61Y@7s7l zF?|16o}G=YeGMIYJKY;U_59}*#*mh9w(oWbkq+I#;o#FCvcPD+8jaoz5aVAz&9+4J znq)_qtJ_S;)dycAcr*YfIPN+qDr9`haR#!k zgZ=TH+#1{iIK?JCergXd^M;!w=&5+;ESVh?duT7lh&a-9s|)$i9Nde)V80vOrp@mF z>8&flg+#1`EF;*Pbu_hTbNh$!bB{kES{^=QgvW()ydQzphOCF=#d;@9)%(S$AoBUK zkZ)0Mr<2?RNpaAMkU;KLiyJy`+qyDk^jQ@pm?yTlR}l>7Q%IP!Eaoq4$>W{Pd%wWF zoUXT58Bd!ATy3=1DoBRBeRF#2Z*S@K0;8z8l#={uIyh}t&Gb9}mvWssz`m!6l}d+| zl81M|MRNqwFAQxQy|L->TNy+3hPmL6;2WLgNeqv%+vsT;kPSB?OhhN)ffuJLbLbE6 z4}vHN$2kI%2lift9;NO9Jk+pRdNR`$s*#JI-a1_DF3@N`Yv1x_o(4aUX-hX41*YUcDn6ca} zYqH387}d6_)QFYk%n~)?HZ9!v3O)pQ+Do?J#j%H>4=pRs8(6jM?(0sbB!}0E=@DT` zFAbT&=ba(D01BLP&3l2zds7SbQYH!jhKx0>;suLrqhz1Hv*o;CeLRmJBVQ${$!)@> z^PNSDjg-iw&$GOSzCkNwkvOy|j>u`vNr&Jy72C%-NB2nBm?>vxr=fDy%-IctMT;KU zZ$AAwUZG($5SdT_EV7POo=;-ZDUy|U>ye33&KzIT#B&(xDGW~QuH(J?gpKXMU}}-Q zNhY1iy>#GM#&Ci(9CBRlvON$ByA%Q9^%IK|a?kL`94-_$zE+aZt0;0!5Z2JVYj8}~ zYqIg~tWEp2n62=^Zp-FYKJiAh5lXQ%^CgGLc^NCX7Z}~Q65+7E5<+J2kh)206kZxX za#*c^SN_;HVDLL{=V_4)!~GLow5gX_tgaz{Z9^(De-HwL`*w^>jZon{g+M%h;f)91 zlc(#R-;_Jm`T&6nm@7_;h?TtOE;-&>Hsyueo2VysJZwqG2i97HG^&Vj+^+7@_)_92 z&ZPhKGIrLw0GKie8`FDz?$HNcOU&gBXO0sh{&s3A?eCqq@}!zA$b@>cIqT*uljK;* zhPAP{JFEG==WwlrR1od9wEXf=%eo7{|jNR_((qAYWR@F zr*O9Sp2WZE{{Xoa?Ptss$@4-B3-b5-wxkhilf$!~9v$uEbaZ zc%%TEHij*89`dmZE!RfGJKpxt*>dYd5Z z+CzhLhs(AcvEf&K>*$UZwbZ`qGG4dsF1X8y#^Uv5rsl&u`~O|}ZF|x!AiX>Bk~DLH zysaQlq`dsO_~L}XF>ajL)1U&|m}nQL^+wjcyY{f!+8+I+{&iTvy;ICD1c*clyh(nv z?R)WS*A`4$SZ#=&!Rd=j2wtSznYmnz&j9oSB-SDPL-n;Hnt0XU?2IF*&$?wTW zKRqATKBWKLm9fG_tNuP2z&(5}ssH8;7?@YuSrMw?{Hfjcj>?Z+VVfYIW8dE?6dkV% zXch6vH@b3Pn*R1CJiM@zHNFRWdFSPX0w8mdzI}M_JmKfV$PaAiu=xIe60)8+!oM=1 ze!4^h-t)4B2bN}c@_l7Xv>)KWYbB3<Z?VE_PIK2Vv*L6*RzF%TZX$?;V|TOodlk(^;fCV)5^#8Y&t$H;)!DPfHNzW`3*tE z)dz`uKg2wkB!0bn$4L14A+Wz|>IQDlLPqWXgtxtCh>t8(v@wdT(&An(cLvO`W zd7@-XO=c^+SCHVD+0tXwR6#h%B&0*E6#TFqn}~9BO&NQ|mtdVQVSmwO0y21PFK@UO zLr%DQso`^(9h8}SY3)7#5L2$~rV}3Ly|al&!wz$*tO=>vzA8~9i5MDIt=azYVEDbMOn7qAQKHXhJ{OK7(v{>qo zVp22>mQD;;1nv=D-#aS4a$KE-K7LtNIypW062ao4&>ZrS3csl3|i z#OUAgw^D!#lYG>@5G$?)=7?m`9$8XDW5t>IF9dy)Ah))1i5+C8$3rxjpvtw{@cBivt;dB0dptuR+*yZ{ZK8t^TW0uci{ago+?I<(JyT$TLJhTd-96eHAifNdr zX?;7KDlDTP5lp_c>L?v>wveQT?E|>cRQ(b7UkP2)2kC=l-!An;3(<|#E;yWr7Q|Ox zz1T_s;abaLkdf=26)>uD-MwlyBC%t@A&yq~fj=r8&VzTX);)Zw%O9TKY+mkY`_iiN z(Il|Q#<)#KWhFmB#Z+#RV)$z2^%mcg3<9mqX6k}?#Rb0FLR)f-00pO9owpMBX4ABE9NznZxvBy z(sLi6F4_o$m)hnjIGGPBVm2~T-IbqinNq#3FZy_|)IzJg?@9}#^Kff<7djJOM0r|Z zYu(E2rbHof*zq!3C0=k>Vy^A9l)XXmsEo9c8oIoGqzSIQsxm(L7tj7`Xr%1E*CZY{ z8t9^}zsh|q=l;AW?14;epAlc;LHASc4mBhuJH6D0>u`zvq7L-Q*D=J$BE&y2>Z(PK zo}=Eur9@}DeQhI?!vTxOLmH~Ew-U1X+t_`N_iPb+^W)<3Cl0dde5+6Wxvw*1mF5#m< z@ZK#OH{lTWkd+pR^oHAC3Q~Shob;aLo)<6#4Do)1*KYcg{I$45KZC%(v?acV-f3!V z7&4e-WV3dG(6e+@>gHacW2M7^iDKR%bT!%i@ePDv zvO}!$XRUAE7wZHP<5zA|s;uYzaK^R)?(gPoUAPG67}VTc9$)WL$kWPuP4!FoFm?&1 z^(jAQi2uU-j2cDqkmiu6cdGG$ju{{gJI{$E2}O=xkD;+TAZu8%5j_%DfX2Q%cBrQ?g~dNn#LyW#&pGF@YZ0 zYNLAlO=`rM`&NDDe#8^Bgpq~V?6k*OP0$Z*>e4f>MPr2<65>MmWbtm~zUf*QKolmi zhuj~{2XShMq*~bXZH@g68ZdsB`By?cmW9{Q;u`jc55b!D;e@XR|208L-$GOk{aL&Cb16%>hgpR*RdHEH$fsXDKGTl%(ifXXX!7$qnEo6LgWt_0+Oh z(T29ih3O4)>CO?L0TbEPL`};%ZJaN<8W8RtKO$Rmqqx7Eg~4Cpg1DHDFV3QYpB;9K zJ?v}}#b`gWnYzx2f$j2fv_d#TugNQxc;ZNG-?@B z7<1`+vDM}E;4;FCKxD|$m^Wy3qK+r+t4%>a8O6qFh-SIuner`y<}Gl%lMTCT|1~<> zoNaHSmQeM0xugkwQ!V<|l`+9(3sL*>ARuwN{Dc7$AHN8uZC(rZ=SzL5kz8Ko-w-J9;V)HOPx`Rq?wF1(t_>>I`d$}HIBij&g z?PbETT32ODY{Hsr|zpuz- za)v3L(Y%>F%*s!p)7d1D5Z2-KLwsdqw+7{|hgNZpTOtRdzshFN&g?>5R2GbgNp%>| zvmvb~ZYX`SeMZ9#`1nliPkh!NJKV^^ei9HEt(3iSt@bZkvfR{I92z|BA9kH>jVGLoM8CYAL`LwdcL<$b{p&_!58Mw8e)BQsQk`n_ z+4sV=yx&yK?69j6FHCp>CzhlEDVfdk(ZGtYTecr|EmXhZc0OhcxBkte)r6#|?KxC> zcvgP9*O+{vCn6C(?^ogEptl~jv4e%E2+Dr`QLQJTnADyi8Ag%+qeT*{Tz;?vJ^k@~ z@Ao#_!emT$U!6GYQa$;zQQ4%Y|GIkVQ-MJe%wbb1{OSWi2bm3Xh;PsmZsPBl8Unn9 zpSTyNzD1>kW8jT7XXwnP0zXlM2<#a9oP?~d@6<1jHCvz8rYzftZVOns-lH6HiK2Y6 z^b*(`YrXs_NI>}-9c}X0c27mvR(9D?SDDS~1={k*)IZ5=Wv_;@D}LGE z9?`IPtS7-QG}yy7KK~Q8uCA*Y7X~Mfc$I&w;D8?C|BV7K$_Dn`~p6Ted?<6 zB2ma1lnXcI6PH0D*DdwNBWTVMh&5CZQe>@#8ln;-} zv_Qz)A5)o%T!k{-flWmcO|roYvE@v5Qhpq0wMGU`<{@7BIP+Ef zrEck`d~4b0{23=tGsI*v{8)i?_oFgyoVM4`=A7NA@F118V1v)eT`uoS&1%|AAk-dV zbBy%kIDYbjcR~6|fLH4{cZKmQ{@6a7OhR+4(()rnIO?lQlLO_KXZ+X`lLEKJ6;&CN z(cMbJ361s~iS$ni>|nM<$i8Qy=OYoN7XwXntl?=p^XLQP1}~1(fyet!c01%nTt#;O z)}Wz^WyQ?}t^;w}7Bk+e&hx78_1TL>SJM8~t>6;!bZ#*XFCEg5Le&0zG?RjQ z46kiCWc_5ZGak;qLv(8zozfBU$?6l_UEHl;TDW?vF=`9)o3bD}$a0 z1+PeNCHQusX?N$_f|Z-cjUmJC1ic&fyM5e|Ei8?Gl*E0{q}GO@rNE?r$9HEYBjb+~ zupdWl6?|pF^k;yp;hv@PJL`JO84>p# zsfY1jYLgI*Km9sASClqHQ^8ANV)U%y zxV%RfXtdvNecPQy!L->^Z)lT<>aun6{Ejt;HL_6wJw%jB~GrXas7Y4{_p!x zd7-F;Cqi8T`G0aa?iHlmN+*MkT0oBjS~>5hM{ddYA#?wpP)362y)q0vLm#$2>3l*{~MJy?O?;G@uAcnNz5 zZp6FewrvES8@aZ8ilk1Y<1!BhoW*-Wc-@jxozGX|CB#E|Oa7F{q_ScAHXe{|^bowf znzMCP=3caiK;ZdUs(E$VklA#UtjyLBPa)StaylnhbmBxZ=@D^fI=85ik;l2RjyJQw zjn0_n527{tM#*)S(6{jvIuiBNMq2(?A;Eb4ugBwDk8g2?Ot`TCCK6SrrWW0F{@h z1wPJ?Pof<-g4I};!999QPs`l45z-;z4qlxoR!=BUO;A%zT(eBsVeR+PoVr;umFY;( zTPVzb&sYZa=G6l`XCukgPmw{8v1Cs3WECgeA5(1*@_le=v&pqK>bST}<6@m1Hu9*{ zz|UwR^Dbwqi58l^q)(7yWK8Zn-Z5qBga1+*nV6?3?IrJi0pIMfPSu;G^6%MjMH&6H zn;kUVa zFMiQgjRJ5?iYoMG>#5YWQ2GV_t-UakUR0sA^Cr(OKpKZo0zD+CPy{A&%v>g$hU~$W z3qiqxk|-}nKp~#%cLrZg$C{C&5nsoSagsWfg!>9b)QiD|&P?AR78B84yd>CtQ>hW! zReRO9DMS)O>iF1Np!UOX=KUInf`*>f+S^^;y#NSp#k>%mrYw;4OGF@AFah8`#a>*8 z7B9OLFigWNy0CvE`!WGG72PrdN@$DN3=F?wG*#1l7AL7yYKJ#)S%gy&WN|>p)~V@8 ze85e^O|g7hl_x#a0!`WF*XW9KS}&JwHFpj_kw16eg_=^v|mcHz%kE@{&<&Yci^VO>(& zMn>pZd8t3|h%@m`hlNMtkHr`c`C+b!x9FF0D6s2r0Csk~+ep$7@dRhe$NZoTcS5$1 z>pK^pcTbizDk0(7lGOlyc14UgD)LJ&5v0XO34jlf8k}U zdXw|M`0-JtUE)8jekoZfAypQ(wR9H)1ODV`g)#1K%m>CPTLrUW ztcPU$?vzrTS%WfBwKKasBxY5)ZvSWOKh{S@d^I-h8?QeIOW1vO?6?PdwY?QTrTgm> z_s}&*;KTf9H7A>L3e>5}y0V)IKwx!8cX-iMkkZe)9UL?QWS7>Wy`9c(MT!UaPTLqn zLWWAm!&ZGv-$vt2J`!j@{KIhFvc6I`M<|s3?~B?t0VRn6XCs1M&*<`&$mHZhu6D6= zw!f*Nsy?c(DxBZhyyZB4NDGHuwmaSBxN$eMjF>>Bwe0@>OY;uE#&0LDcKxUs$ozh7 zl(*k#kzzX#{!Nyk`t|3N=Vp^w{2#03*Jb4!0wQDEby?5MK1?XseY;@4ZkzV`zWnpd zeOj`=W8TEnLL$`1uy;b1q-T4eeS3mf|XYzsqRvyNF2Q(Vp{sYu(9vJ@= zB=k3b2{7dtY|*huu8cG1MD&RyQ;~Wb8lule`C-&l95(Z)TfwEgUM_8E`-BUzHX1+N z@TwS>gNtWo*H#Crq_ueZ%tmO--epfk*o|Qlrhd~>!ymId#((8YY3uMUjV?T|!DI#~ zdFp(gk_K$`(zfQ!h2qlgRE%$|d#ni<2A?RvJ{fW#6`jfp?IVVM>aV=M=OOqg+i1}? zV0Ydd+CU%YNWG;fKxaD~FHDWlrqMwWMpb>z_-fXIL&|4Q-}pt45_}=A;kgim-p|vG z=jX0$0M6tJIqiO}7WZ>D_CSHC?2>_XkTPoxf6WJDou5{(!HYn+^AGx*_Tf}#toXbW z)A*2~F6l;@fZgdrUHof+5EYu-lxxRzAk+76jeK(DElK1D(E$uK#O+O2YL_p;08GK6 zb;#?h=t`ldRNImIx*LkVC~W*&^Gy#{6IcH_A$lrY8EZK%T^OO_uu}V)t#WnTM&;(e_g%&{#H4#v{25zGw z<(|Td(H}H&N-k6+vIDYQ|8?o|I?i9k-ZVu6Bio91=M%+88`}A?h&Ue8*D*G22JuH( zY=p_zdT+8Y@TUumm)|z|1~&K2t%Qo)42WnNQjc%&@{}*%yQ0vsQg z`N&B$>vWYRvC!}&Ch-YNy=k@t(kP8ub&CEX9VC~-j2TQg+blKCE0B;tw_ zZZF=9n{Wl>u}WW)rtkSvyQ;vxVMy{1TS#vD<=s*?U){cv3IUCcH;#Acc?4ia677CU zoZ?a`o`ll`OgzbFW4g`#2?+s(BD^G%dCBr8TRM+uSw9A`?q%<`r@N(be8UkLFV<5&cm6>r&3@5J<9}Q_f%MWywG)*_3Yhxl zBL}hlgcsFwS6WR!rDwUwGIUMp%?r||T|(_P+!i5G%cy~HL#}sy>&fMVEDAL8fQUTB zyE-M1c&B@V-=w~<>QxA@T=iEvgr5nn*r zXJ)lLZMm%3rxn5D*bshyvNrxn9XHPq4gPQ0<;dngz@-z3cNw3`bKg4A0rI8yGVH;c`unbmuLsg=QU>8L7)6&Fm&hVV@|3C1;q=(gob=|c%> zng)me2PnFp_LW7rLUALKVmM;QidTlvv?*gs1s{Ux z%Wt{HSIoNX>Mj(#&FEpSDEWdDXdUmy(+wvTRKU7sVqhFl$WJr|EEss|me(gdNz~)Y zdI?N4o37O2=xf~dy%V1-flY#c6bLHQ;06Gk1;$g$w4T^Ty9i5b1EBm543Y#W*@~=9 z5FtYJe%XEIP0)N@pW=JB>K9HK-I;Wme=Jj_pP(FLcc=>Tm*-vM7#4+`DoUS<;wPjG z#?^&MsTh2Co$$vFQ8;jfB61^+?`+SKO`i_D@q?FL?~03ituuo&KUx8Q0*aij40fyr z?@SPr*g@c(ctjG^;{d>$~_vK_dj0W2Q}Wb2liV{m9jc zhGNe?auNest_qBY!?nYIotL^wMOYAK@wAd#a%)aS(15KAV&0iyG2TRATX%KVGO&@? z@>T;N<10Zr8mWUM?`h)QY1AuAHTWhPRc;&2mD+@IXVkvJ>n&mB$O7gswX^PI#SG&@ zCZeQ*);283J^Gt{nO{UU8{aV5FA)@<=dvttZd_!z)B_U~9s0-?VeG-X`lzczezME4*ObyVEV2LwZB&&kYI73&i*ZQ1>b!yk^ewbr`gCR0cA}z!`=RXgx7IRKD=>g9F&z6) ztIH;|hHXI>z4xN^KY$T+U)fNkr(D2Fq+R^+hld@#_4tVUY>vAZZGrV5hX^yGOoX4> zMn=HLp$7fG6EY?ViM>nP9im1LEA`*-E%FuT3$z!EP`de$+U!9KC7RGz*??7k*t+@< zHu`%GkM%5u2K>xLf^NPNx-1W@>L??YbRhqjnc#e7Hep|!0ee*RV4g{Uh;9uYJ7i`% zJOg2u1+0{*WwC788CUogL0`aI(r?@M=VD%E*eAVy+6x}SJk$=la-Z;h z@P3ba`@=>IL89CLn&vHH%%2fBO=BNX^t&Lb0)BCg_E=Byu4yX|Qk4R%WLob_>poDVcyRNV8IPl32VCDL^{`6V5wr^5Jj}(Swyp%eQFVnEA_E?L%7z4-wtJD{+ zBVH=v0?&TCuX$vmnZ5N{Yw8$o<^Ul*c3Rq`y8g-X&KCA*nD3~_vqufVKw0;I?-5~I zZWl)%h?Gl@AVfHDRS}Y{RCu7Paqc3t&B%*c#AVlL&B1&r^%jmZVb#ytYjB?VaLKCW z9J#wZQ4^N`Yr?no1Xp)1{gIpP^{rq*lw0fjeumf9X;!zs&mpj~FsLZh&OCSBd6^)&Ef}4{a?LPa4qa4jz zG1p_irQV&xFI_#onsS_0i%bt8^{OZ~%|S~LpNW&-TU#qmeOZJur6d~n@Ci4zhy5;8 z_k?YvjV{PKbT!T@59=ORx^on=(=biEiQc~hNw=o)hCG|GJm(v8M`dn_MgbiR+P*Sd zC{Wjya*pg$#bq7)M*bLFPCO?iKeyM-tmm2j{>GT!Cw}=!Nx7cNORPl)jS+Kg=7|?Mcl5L(GiA;OCP>!=pZv*+bv2Cl#iTP7=fFtaC+@jTA7Q zzc8b9YG(ZGo*5_#|FT9*ivW_G2d9dvr1^k`|Cx6*io}0m);%gQR9`xn$;fKX@Bl>T z7&hh>R;U~I`Wl9q|IjDj3?5Y67h}Avbw8TQk25uSAloE0HDT~a5LY^%k_4yTtaQO< z8^TM!6hbdi`Ndi^mgEXiDKi3fLmNrk)wdH{Zh98Fh>Bpd^WPsmwmJNhX42o|i_yTv z%LFRd2L<-5WqO-MffPtTSpu*1R;RJq(XjhG)k)S|nc@R-oiDs*t24J>3nRd5(WASR znFJ=hO$8!su2y`=N6B_l!&OLe^Lk==#sdM;$yspBQ z-p^nb4#5!M0A?Kb&g>o?(|0Cz1UO%z1In+%SC#-i4ZCw#Xb!BZU>T4jO9+!Sr;D@( z6&=~p_7ORikIZ!VM8QI0JJz*t3vak_kp_W&(!pg)R}duO`E0p}U=l!&CqEfdgaPyc z9B~%u1*<~t8@eTOk-t-{p!TIoIVj6vZRaR9HUfGREi(i^DqU}BI$5(y^2@J%a)G%C zycTI|(!-+ci!Rh;aH|yt60kJCf07M%+~BPDPn{xVZC~AMMvmxvph`%568pHr*W&w8 zSLpx|azm@8JwxqULWgBvb!sC@d2n>`)(et7(x4}n zsdrYRbdUrImvX1M9xA)9?&{b?`JGc}B3VJ?^0yu>Z@ZZYO->q7>A4R%bv5_1=ba~^ zZ8+B17lxvEXO$y`_b~i?7{+!_H^MoRivAV<$~MzNR3cTi2EGmd_tzxH5C{eRdf+c9 z8)@#E5-eJ^VqzGUi7+ zx@=l-do??4tgT+jeZ#n}>2Pa+kqk{t{sP%mFaK8F+ehExK|3#e$U+UN9xB4|kylFm zb@A7(0k6*a=xlaRsmD#7e2dZcb=_4$=Zz}qbM;WdQEu5`K3pxGy8+v3pb9U2e93b8LlbbiL z#Y~y^5+?}9oY5?`1B+mwFW*M4L3H^lnW@3%CCVFxI+`o#N=pX-G*9E@n77 zCzBp2VESiX6tT$`N8~!s6p4Y?toS73QXN(dzSbM@tyZ9)6FF%a@O^FOvkCuFrl2#+ zSa(%L=gP$a>=mMGrhtmKL^aJfBG4xBrdd$#(%jIpdZ7U6hB80SQnI^U9sJj`DnlqR zEKK5N5N;Qf@!+NxpY_iQ$(|vliTWR#>o?Md+T7;9j$SQY z%^0H7&{la!ai-}Ly0of^w7N_s8 z@MG6(hQ`9g2;C3XrSXXd@nx8pduD@rDPuR~iqF*suNtI;cp8T>5Q5 zmIj6el_m$2$sV0QQ2Pj7v*(;zekW$w1Uf8lKwYASdwO)hu!%i2_G%(emnIGPzvAn~ z^BhW3+^er@$dm9%RF zmDut|q)f_TNp(^Xt=qLFt}KFX-uVOXJK=rm_j-LAA@=H^|8kT#@H3cM0R7z zzNM^#WXT?qjEp5@kLdT@{r}I4=R7aGaE{|R!`$xsdtKM(6GtC-M529u*F(G-Q)tOA zUQ&c?1*Z@-GPIwW6YE(>S;HDf6BArz(#`y$dx%^E$e%s)loTv?okD7zg1eBuiK?|u z9^lkQq<`el_$pl>DV}#q%2#J;_~_=mIKd853nBS&9EDr4wVcY>2biaD8RyHU-Mz#m zWOYyQ{^dhK%RLs=D3P(!b#6U$z||yC*?RM4mY9|cwE7dvlh3E2N4&(1X#JAAth1cP zd)!wyT{U#C)XERR*8>6=8BEso%SLpNEo_Dmkq#T3G|{SWq2t`%j{w`^1sD(=E`!*^ zvy;5o0G`lp<>Ds9-Tu0nJB<4QMco*Yqspk+Zc}ns{`3ShDaAB63MTGn*VV8r+>*Qm zN{Q>0bpCdRfjge*BcCkOY7P99F&Kc*D@t+EGHuxHvdL*0eYm=mkBT8ORa&{J(w(sD zS%kn9?6U&$=HZVy5#MQTow(B){1r!ddCKr->c+C9C+QvqwC4jKPzNmUWevP28PS^y zDoZy{uO-S)Si45|iHobtAW!6Z+FE7yJGSi<*r;>`!KMH5D}k}n@^K3>A7S~vBDtrX z)=nI^v!kFR?N^M_^cn(~=x?d{txB`N7iYjY>tdbxAmI!;2D<8VYxYuCV~5hg&fs5g zXz626WTHKk>c@N%Sibw_SefFj^UN>sSt9mc5~7C(LTgWeVQr=OEi`|xQs7_k z^rQXrh;Ezl>mGIQ-!|LHZuu5UuIdMg=U1;6bf7~YA1+^#g{ zd9F02Qq-1|J6-eb;+DSw@~`m{gcIL~mpQofmnHF!p8YB0_uzjZ=GWL8%T2!?1mRfU@P6H)ntL#>f7i{J9xB$cCy<|hpQG4x8{2%p)iWX(p*p40@E_>OJ8hKb9-P_E z+(Eu|nVp~YmdOEyTXax3KxKOW5^%cfewK4_>7(YXM7Mx{kXOa#sfQXpms!b6wl>fJ zb&#RW*=F(lr_hId>*3S6J+{)e5jvzhzk@C*-1+&nt#9i3#*p2CMm111sLpj8&JF*A z;W|V=+JqKP>iw{gH4gUe3$2rqV{we!vvz|c`k+7M5rl{@`$}of@u*RJb9J`now(7# zOofkwcvRn1t-DT1N~{X@o4?A*pQ(r)E?fMaz&r^-!p}8)Tr@HdOcf2H@K&QQAfyHT zr2&=@^5cWpsD{9=pGVA$8QJBm4# zc|E1HQ@ZwY$~}bt!DZ;*%l&7ld&XV9qI*+yBGSJt@o0zM1&M3Z??P|J%R~~x4WZ$% z1iG0&k=m83MRFfg=@Kh6bKQ}RC5AgzJDW1>d4|uUDY7H0Xqzt%>*|t?@fMF>j_&8( z23ai&KP`y*5bv=%mgN&8aV@nr#a>Ta_-}E`0`BvwmX=0(*RvYNLNa^!nHREPhsL;b zV{IzmPh0paas&TN2m|13G*8HlwE04qy)~nnWiWbUNc|=KB5G6TU;|q;$qS{-O(G!QP`a8w5QNArTcC?k9!MX|{H&9?!U&o~o2#4F)fH7U8TQb*g zGj5e&U(zpo{7{#wc*QUDOj+z~G#vk>a&177h-wkZta7?ZlkpS~&$kpj5>WcpohfHn ztJQsMV||7!8q?;Mk@gKQ7Hq^^fFnW-X;e* z`;b2!@JjJjhqW~9iAD~7a`u&&YL4_Uz0OBLONi6eR6j&%xED~>jphcunCZc;*VV|! z+h`8=la1)l6yGflXFI66d1M-Hc3u3su0nhZ+#(nsjqeFss*X^8pJe`v;^k8-yYNy~ zvQ_q!Er7I7)Iaepe`nap5z%^XvJ>I=B^ zJ*x~mN_1TH;HG|_(H|kgm|E2TyzV<*al9?aO0WZsz7{2rA6#Xybh!vHb#5+go zKN;>+S-gq-ZV|btU7EfaXF1KXmh5~YEGddF;8jlWDCse@Y4RL;nV9E@>RjFnS|I@!Ow7*@Kj)gd{eG0DFkZs8?51BAm`|4W3} zS7)K{tV>E?|MbriVVN^}xaw7(jhL8mjlmslnpZF1mL z|FkEb#~eXpDcNzfZBCQQ)NZN;ylA#}{3W8@pN40zR#0SP@W#$_Y_&;MGG#UQv*6If zO*>OWa!##ErS#;$T`8IIVsBCk?}Mv%ar%^<)f0<}fO=SO3IPKxSb|nW|u;nQ(2e zLd+2rvy&H-*%h=qE5-I{$#CGl{%%@>bTz1H;h;#G1!ynW`&byi;bMj=S^UEgTv2Uw zK4LXvf%2sP@cjVTQOs$5XhvPIxDO>8IwxtV$fcjXR|0`I2A98-Ka{%BYnkS0B*n_p zG(J^a{KJZdsA_T5U|=f}S>RryFcHaP?$EjZ5cp+!IRoExO^aU?Gm#I#YWJpcbGTD= zN;`P!qn&9+g!jKsH&N{2D;9n~67-GM13l4V+;}(sqm`CdT1+jy!5E&0@C5dv0obAP z>ZqY*S|m3N*+}L6TF}7iCQiq{Wo&fG`vK-;ltlvHAgaX-u)D~Q()Y(!=AF0Uv2h{0Hjhz0~sNGlS8K z7~)EbWy{)ZVIGU?41342&PF`n;x?(Ia%Z5paobB^?j^jV$;Yp zeVFHrOYLtl;Z}JBE~t2QdtkRW*C4hqF>hCOsVYwh)vt~u+*P=_3kKToVKj8fD*H-V z1`WbI9xc4mu_hFt{4ITCSv&6R{o;6xc>b*Mx%RYbybu&@tM{teb?6D``O8ZYzPvM)U!kbyGve+Mt5*7L1-0e< zG|cbD6)#9grk&tQDtwUppo*mPtGJ-TSl~8Yb1*2PxY^yl@$zo3r{9DWsh=ty8=l$q z%%v}t(NK9kiu1P@Y2)ZZ-}%DL_NTlLAlWAci$^~js(u$QeI*mgFy=vnCy$NJ(tcn1 zBj9;Lj|j0(j8$8df?0Tv7QD195JpxnyN@91>dlWweu*E-O*X7uqrQ8aDQR2wNMUBb z`ith1Z*?MZ?-)w1=9hLq@Hn3?xK;f~rx%QbU3v8jRDPdZ)3ZAIL!DllSBMQN^kwgk zo>=#Hk#hs!@xoH5!?WSb^vMbfrvj(g-)DcEPAz|LhM~Q}${c^2cCxkks&V9h^03A1mg4MS?Y-7fOv4ZnvFsjD5gAZaV5tdSZ(Dt{FG$m$AdqGxaMy z$LW&Ym`@24gQ0oWD^Tcd+=24%qa91ijh;t|_J2ZOEQvh7q1^T)^>)e3MJbPm_y7y$ zI)D&fMyU*QS^Tbbave%S!5x?w-y*QJ(w6B*uAH|B*<)C6GDrw6Z-NE8#@VL;qUQh; z>C_rAJ!b1=_nLtrJM6r?h-6z?Vly>^Pai$)|OY zq5S}A8UliD>3Qm63-AjK61vVXaaK7-`Bh|~3H8@Xq3$6JAexg+eO?=x7z(76r;QgQ zPcKkLv&CM}Ut9!!60v#q#{vTbZw0}s2_WHi!%Ao#Go#kmrb<(W_Z@KA|ln#wyqKl;hRA*Ed61n0eN2SoDbyaDwQ|C+I?-13U9m!R(;u>hb0;OBX+ zGdG@K*C(r|9xhgLn)X@PbgHjqg4EXvaLqPc5r#i&Pv0 z^?#S25Ey7p{UYd;q5kFh`An@5zxuUGlSLj6JP+%d(@bBJ*SyFU!E$V-@R4-cya6cX zOolfqIIE&W#3QJ3*jMS8-`~=i!jBBCrCVOO<+_DGXn>%h$2sSYEgX8ej1)6fjUSm< z5yoKaMor=&b2THqbc%Ujq8+9e8n4#cu+d4kKIV8_bc=ns$j12_q3nnR;oEw(JD2o{ zW$2zt`oz71tMxJ_Z@j(~y7w{f@ye%iKqkj}zb@%qREm$b*2pMP;_Pj81Gk>f3mld% zl)tc>mMMGAZ^*NF?M{Ze-3y#|cW!0dkz(Z%t=l=WVyp?e*Y4t#tgIQyhY<8OqQZAi zg}XVLU8}F?j^y06Xem+HA~C%{Qu@|Qv|)K_&#a2)hnxD#odP%6SNa}UUxLvUVsrdopqaOF-U6o)nTz>maFPIbqBJ*LMh~JZca$-o<9aXm-Y+fo z0=1yhR524%Q#0h+lNO8a)Rw~C06?QNfv`+lQCZWIeDmCqIHygy`nn#3>J*R5Zs!pN z^?0bW@N^Wl%V9v$R=HTi+L!5u{ELL;lpZV|CvnTpo1Q*lo$-Nki(X$Afe`jxOs$$akiuwGlft1 zZ-bXB$eF!C3=SGN)4Th8#v^S2*!#;ul4nNu7g3f^I$=WVo|L(n)JWu*uHmqz(Lh^G zO78{p{*4&{?_xtb00N?Cur8$o7H00##%ft}qQ>d*#mNqFM=b}S$#?Rn>g)G!cn%0f zdbVn}=)Nfdp$r%S)N?o7kUHLQ2Yf{f3OjnUR4WMfkY?@}Q4VwMpp0V!QSsE>vREMa z`aKnp))?P=F9kSymABBpXwY}x0%rV4?oO&|F|UVRZQw-5kyUMUfi!j! z@hZo_q8p4?G{J4Age}Hc8p#W*#&wF3q782+X=jrQv$HQ7dkw5Iwc05SRy1Ozx4ZnI4JhCN>1HL_QtCLaF*T&-lun` zUl*DqRGmV&IFyUPMsZMcobv>#<~Tt}g*D2qnVS7lQZ~gX*U)cZlyq~Un6UP2F+fkY z-ncD<>Bkzv-89R934#c62plZpBUe2^-KgeMmp(OT>=S~iO@>Z0?QCG*XPL?Ofv2Tr zO=?mxhrnN1=?Gnn^<@b)qWb!N1a(?DecNB*-JSA85qNV8L#ExO!6x^lkl}8;O!tGt z3`5xUq112>wdrum;E2Zhhzan|5ahIS;hi7FH-Ndlp8}@4U*j)qcM#Dwr@U;XyZB z)qU}h?oG@zuao?@_o0={CNOnVdq6U2UZfm+AInPv4-e`^I+TG39?)L1I0+D~;Zl?H zlf)O3NhUJn9%EwTMb;w~*^5Ifnamjh_jdb)Vuk|WUvD;WemZtD@G@FVvO{M3_fo}g z`#~dkl#(G-b6+CMIyhLPfayGk;`_)wF7&{nbr2sN zK70ay2PQF%O;v*6m#1@|K5Ii)0c@N)tqhmUuQ}1xZ5jtZ*UGOoc6$eK69kUGTrb30 z&yl}jHY=2$3$TlvucKpZDsjln=Pn5q5ctvIa(WVra&j%bxn>*J39G6zZ}Fn>@Tn1z zue#}MmsGsI^r?m&Bly4zV$G7uvEpqqrSQXA``R)CfOasqfkwM-; zF*Nx{^ZrUnbK2&uG~-x?-fo4sY5JWLmy(+UA*D037il1&V-K0)8Qwq2?{$Asx#|7# zX>pk)un&BDC)Dm{hQ;#9)>=04ih51VhE0Ry(mkCmNs$OSBiHzLa}%1rn4Trk^x`^)$4JB^Bt4!y(tz$y9X;(Il37bD);$bX-6 zIg-5$))&^+tPGkqe>p?yl)!En>Lh-u`eD(NeV1)d>xpxHT#}3T&ks*prEaCo`v^M6 zn6pp`d#4*eUbEUBAHDVCw^G}=AASlMQ%e-wlubS2x(M4J5&8JuL8;Qy_E7F^UCT!H zy%sD#U#!ay*Lh5cW_;*>pq@KLoQ`a#C7?*-@NOO7{grENhF6{mB}TOGT-^qm`hpIS zMvU;}qIrF}&;}x+rmE$o`D=;GG$NI^xg!jpF>3&K zaNMX2_aZ0W43&LE+4|fvAoH>8AgS9|@#m~{fkMR3{8xyQ7l3D|LkoJU7o>4++^u2! z0p3TkS0MIdGrdd8qe}ySR;)qK(9?~uYNprObsiBJsrs2;MZ-AZp#a(d`EfqFtR5G1 zwN#9;<$O~pz5ig!z2doB=8$t@A2Mm!16YKB3KElBJq*RVxo#fXRdSaKgkM9^kt6nN zR8=eLcu@c*ZU?{83i*lmB-OoY)szPoOsN6$uD}kI-R}AJn%%Q9aojX70`tn+bG`~5 za9*pMNo2U_*u+w}sIMms5MJ?MfZe=jJ~ zyH;vZk{_#_vvGgu$b~GxrEO=w>yC>VvH>2u^CGZ69*9_IlS7N;I>o^raW)3S>WV|U z8*%-7>XOZ4o+RVs^9Apbo#t`Tisrm(Zb56kkdIR71H+if@Y0OU63gM$5ypBWIeCFO5J5PiVz=Vuh>(%yGN?8e?W5N6!dys#$^4*^(8Ac7x+NMhMspM z;!8|tSQxbi^w{W;?MvRkkvlBY5^LCcu`4_@Ys2bge*uCr3njZ*oI>wzB5%vh8*CmL z-9gXMl`y(|5<=n-W(1uGSl{Iw0AYbU@Zp3lOVsFC9^ZhF*Qb`n!?Fzcn5qYM@sVXv zi$IS83*RZy8MG>;la?ux)GioIaOFR$823HCIGORxBj)dc!7K3+Ok(q_I)m{A31M=j zOf5@CA8iEg+|eqCfOOlR% zg9-S=W6hw3Qf+#70vjDjpY8Wh=cx-jdjD#+wnQwGaE9FN`6U8pC`AeVKHA*#`Kh|} z#b>b9^GKijrc-HyPMAGJBb~^{N00xky!y;}DoUMgWV72ZCCVv4kEmGZ2Lm9#Xb$8^ zHk$|jZ^maeXS{0AO4*<=nj8tsYq*IwTKd+^S8*=K|BGgB@bm?WT9oz3yl%i3aL9=n zGOK64GjaZWcQ&;oL5`X>Z}TXGQ2|9(_s(p2!KUr_2f>wy$D(1YW?KV7sy0F2_cbtJ zbkkUiQWBd=&@$_GgetoSIM6BMi&6hd&)2TLHxAH5#w``=ms$KK?4jGbh`T!g-ipFQ zt`8jk-hV1nwYtmx$jZ3u^0*@FYf~IcTG_Ez>#NRa4|>CX62g|7wYLugz#sWai={1eN8-FHwn(+DF}m3jmdLyghM4d8)+lZu*}7skC#Q=@C|LE{gko_QbP9) z@wll*d@0#pF(WlNcowc8BTQM5?fjk?m^LS!+TrrH>XKj$(Ny{N(-lieQR-f=RjN?O z4(B0$HoEBJivV0L2wCy5(u!95crt;*1yis~HoF71B1)4{;3 z{rTjzkZaK~{RdTET_?gB0&OACflF#hKC#_q)rt_{z87`mH;TQ$I$3 z`rdrle7-qwpj-iNMMp^N<}DHBhp>kr!uLh&7jD(0|1?iAuuK`x=QZyW{ewt58@CpM z`d|VpMGs)>q@E$QAa&`&wtu`pwD-{X{92;L$V=PjlMUVQ7GG6A>RU>W4>U+KN zwx1rv^%?ov1s&JT;aIDHt9XX;!%wq!0wjc(Pm!XaVd#*psBnE(et@akFKLP_vveLk z;fUABN;M~G308Rpbnyu6F;)JtQ2yW=a*NSRM+I$GyUaZtZoT3ttYElluCN?PZBVdp zF~BQegr!M&^WJy7gvv>I*B%>r%?!QC93SzqY$Q1U`5PpEdo~Tc4{^!y&cC9+>a9My zMGi+40MjVCgK|qYxg+~Ag;qKht4{vyX*XRx--~IZ{h1E#0p1 ziKI6|H6{yJZ7*157xsrJq>$kZj)Fd!4_kNQrW?%M#p#6)Mg`p-3DQkTDBeAA$|xUqNJ>H4lbg0C9*>~BS=tRJg=Vyzo2Sk?QO#PSo|4V zu5@bgnYk6Y%>c}~W_`{F*rYA4ROt`v+4yT$-NScXdI$oAo>((3i$twdzwOtUG@b?` zkxR~&KAWN*KogcpsGM?LtIymzi^Dl`t$6gpTPCo_=)(eYLelhDMQu#1YG7d}lv3%% z@A$mudFV$1RqbJ8;NV*QpEmD7FJb=56&t+4Jc*XWMFxV0W3Z`;aY;QkQ5WB>PFTFC zbBKEOGO=r{XEXy=NP&!-J|wPH^&JHLQ41aEdfh1U^O5^vuD5yZ#5=>F#RrAINA9NH z8=w2y6|AQi*FW!~5A=*rN25kRFr8}u(qF2^X$bwsPs(lw8k@h%5_S1CNFQy;O`Z`< zFQ|47U3m{A=CJ{%;dQ^|clPv?dhB1$sEI6Xs!&ZPSJG?nQjRiO%WM=bI3o(6`s-*e zmI1og`==7Z7ewmsKdG#|?iVO@b11^>SL#*bU~qqt`Di>>tSJ*Hs5dIdo^wQjT<|8B zDs;2rBIHO;x ziOt6iJ`!}&&D^|1M|0ObE5_$ncQnTJ^|hVX@dq3%-Zm;%OGGllkxbcb{xG3-lC*8M zWsge8(MxTcZmUFI4cv0_B4vtb20kI~&-?mo3x!Yg#NmSals{`GtUM)Kf4VPKVRtFn z6uI`Wlc_|!0lg)PoJR>XZDE1(m;aROVF$KQaWSG6QNQ|>+g0%C zQ~6ocx@k~96dW%|B5vR8V#GzUui!CFUZjiZB*SF}8gexUU5dgW$$H_H zd%D=3#GYlaP1eTf_8^XX_|s$Qa^fzOyG~I<;C|7w=b2=$1sP*|rldicmZ(vteuK@i z_HoOxtq1r!#nNN&->>Ky;40b2!8Xk`?_&Rs9;4^(){4`7j-l>XH~8wkuPd3mXJ0<( zU2dyOHh782lHcWE=A{1M-J|#fMJPcH`ZRJ;)LE(cKHo@p%ysH9-xineTvM=QZ07REu?QY__ zIw`HaDJb43v8K68PK|ZfgE2=X@Aw|xLYI@#T8#*3H`oRVkGI@&@X#uOoZR%E*x~3ZZGh%FEmb+oTr^)I4V(!zV?kOr%RkkGV`~~nKC{bJ z0$0`WOYhzV31cp;C+V};q?W5u`J6c_??UOT*{}7G4i#SoH;*cH@!eo7KUTx4Cqbnfb856CJO>#2w6ectBp;kzG1#;a zEodywA)y(sf)%*SdLUTsuhF20lRqlvOxCY_xRmYs$bwZ;oIdHco~ukV{wDCro4U9< zrR-LL!RGM8Qe>|#g?+}B(#lF`zDscdaJ+d^?ZZe5x?*#vV`Vd*n!B!xLsMH?Eg zHqhPzd+e2tSS}O=Xi;$42>=D)ywOpCGpje-{}>qHO~6nexV=#k@D=@^Jw33g=WG>j zSGBb6T>RyAaPE9bQdjI!8bX*NUSfnTL>z!AY1!T&7TTv(_T$>S#+giQ5e1grs~{{nxsr|Ms8FGi-R z{Ico16NEyD$J-kv?U$kSCzf`<{51t0E`>^9Q%YQ5g4f>@Tyo8cZigehlKk|`sM#ue zh;0A-PLe&gFe=X~2!;69AOPUK@1&8>nIM0x>T?W%_#fm3(BRH@d_cDNtKwYRgjCcr zoE6Z$0gYOhmP3}>a)rh2X%E}tXG$B^Id6FqtG|ao&Tx3kYqLcEMpao4%H=5imT8px zUUmHb!Oi$z@QFVQ2S0!g;t#iGtVSta&Hac)n;K(fx}nP8B;gj(k2445i@;&#F1V%` z5i$|0=R_AQJn^0TmMrjSA?j{Etf1m{Q24ERId~0G#0T8rNk8SMrxMR!_GeO$lLF2iJl``n_k4gt+K*<2M(adVmna1AIxK`qAy}4d6Bvs+xFBYN|V8UUPn#{(= zKGD#MkWYITn+||ny`&BLEc@)eWYWSjm*~Ni|3H)Xj-I8EZ=~pQs)Xa=8iG4kb9T*)+(KdRVaqq;h&mH*QaL-$>9SFVJsAJP$Yw4$=W5% z6Ksv{cB*2GQ`+LU_>VKf~5t2#R=HO;aTn{g!wzd+zNu;cE^N#Z$-BR@>uH-%+fE+1v4y=XDTUSJ+b4N>0w?=0}j$_0%W-9p#1_qeo#(CK| zsr)dZ4HM-NZDNN7M&V7>K>l5DRC6VLk~r>6-uyN{w9){0Q^3kpTzQW8!J&P7y^gwc zO>0wx8h~jk#E9(iv`y09G?%iSNQGQe0dvpRi#?$fb5u!*zJMNW+oSXeG`L7_QE}{i z4cJ4dqcSJlxq0<5t3xQGB91L+$dgQ1M1RW$A@wrpz#;S~8-y^(d>GbI(vz3WgdXyp zT^obW+pu<*DcRMgyJURPIrpGjIFfO}+lcx7(VyJ&p0cTz&z7;wKczp;=?=7BBd~FK zqEg+Zx^=N;4(|wJnUvz$FR`5+AO;&AACPaGUzP9nkQIEhl`ZW%SH+Uwr@Mw~`e zU2*Hi%!F)Jh>Ojq>kD1Q^^HAPd4i?{Z$a@F7|=InP)o5-wkM~Sy1sth;0{CE&GwQ_ zt{b+kTA!16)erEX*e}*E)`du${0xCi_SB~u<_soqL@1OA&&bmLqFS4#wO2IUW z)7=?(y>EOX`>J1Dpo=wOmy?ExK0pqFLsd`M8&(qwU6gd`=}iR6M@p3B>KOQCCuGt3 zmsk*98c&nXDeGx8sdZLdSx#2yOEr-6~C?it_vXjKa}dJl1#Nv^Z$V^iSwe~T<)vd84y0WocL}<&G{Cr;O{+6 z%ZDGL+BK~_`m@XhFX)uc^{Px=7&DyVvrWM*RlFSl3tvE4>fF1v*)h+odE1&_8S!&C z@VL~@&#FG*Ts#C6Hmu*^sTN$?lZ^Wm;IC-js4$NvLyNhiRVfW#Y`O)4w!=jXu!TeL z5v}5%=7BzXVHtr9^uP))Nb_s2XRX?k6hJWDs|SJ5BgibP#+D?D!XU8Z=b{@SXj!;H z*Vd`@ygy@#7Icv+)9ZxiNs7G%Mi1QqJHJ;&FBDHbEVn^ieG1BJF?%AXw&(QdJH)c; zRL93zgu8bCKafWf)v5F&stGEHiPSu^{_&pqE2=DXJ%IM*j$3{@y8;iLJ={BE?quSl zzaNt7CngHr-u<)BL*jt_2YMfK|6?_?LmpXvW`YC)>U$eQZePFDt4~>`aPI$ z0%m`NYNM+^&JWa$UHGZIIj>zR`nlAi!$yu!y>?b>9mCL$vWwS*7fvVp^XXQ^lkQI3 z{8H%Lnp_yEAcvZ_D&v$O!jT{QQkftpE|rBId|svjVL zV7x9+V7)Wl{=lMWNSupracwk;H5=Bvd!vmL{%)mXIebKT?IUPSR4@8FE&Kms3F;V^z}hj+A66v4jKhbtHVFsVekp znrm&t$KQk8eGqkuXju|Q-d$ZlOGlB--SY?g!(DU zZ00rY^uAvRz@-JWXMq#;XHqI+xM~e+?kt4Tnu;Wv^t?g!4R!L2YJs>Ev9G1gfT9(Z zr+EDK)5M!Ltjj@aDaVh~5H3x!X-C>TM31kcDB^*NBJofAvm*4J3=OJd*m4_pg%=-$ zB0G&mJG+W{2&ZGK7HQ?<*RpGq{bn8a`YiQ+_|j!wnoN9B^B|_9PkY;jD5gWCsxzo4 zCxTcN;EIS`4Ksh0MMS{M=8uUPdie;+<0IiW#gZMw5_PGMuTJ7NEX$YoMr~w*JZsGJ;eB#p2yu>e>M&Ib-%aX`l zxRA6vC#RoKwh0g9#z?Yna@59_9=`kVt}V>ze*L^WJStytH|b+~I7e3&{R3-#5Cez< zS<6Ffx^{o8C5nqE$idg^nenp!^cJsED0VpDYg@FF++&jiY0>?@1|?bpx=M%Vp-e4R zdxSvGTj>51>->ubFgo>wLhDr^7w96Lw5 zNnxP4#w+FY?=BRvPp(w+IprcAt(3V@)&c<>$#<9$Gxg;1b3yb7aXF`4YL)?S?X^9NGM*2xLT8zjDfUlDGR#aUY6I+g{aE}e;ogW@K;RP1+dZQI7Hct+rF`{{Ri^Op1q)O&M!)p z!+v?$JXqiAz5=Y$5+3eJ@qguhx8`I@?b7*k&Rk4W?FBs;e>wSCs1x+Cnxif8O>W;^ zf$wCv)oT?MDdj5<@3pjCdj|0UqZjIe{rc`lSl)gqF7d&Bs=h9B@y%_XG=rbCh32V- z>MD`mJ(U*LZoysh#n|NA1HSDt&7XZ|r*BU+xTg(fHO}f*Km*fMclJAqzy8+mu{W-` zg~2~7o0%Pcj7Fi|K0Y}hqdHH=emB3 z=(y#GI?f(#+N*e{iB?lAy=TAubF72oiYrasaf82J)6G9Z#`aR+-B{Byb<=iFoh+Gg zH36*B_Bb8vs8Z0gwtHIn#k&$$s}|>Q({7=gBNjV1o(9g1F=VXN_$f0=@Wq8LJia!a zAA#AyX+U-sY7d8)4uhY@nT)-Q%kUKaNb>l`+Y@msx;v~CiR-*Pru5t{_Zd9s@?&9{ z;caT_iX9|nP_4F3m3sg(-J(*S$$5vQ_UiLt^9R!jcY-$cy5Vrxsj2-$GoGXA>p2fD z2bP4GZa3b|q^aC|hFH@^Q##Z0jZ~r%-4%bE;}kk6cTjzs^E&WS)Mu;< zr!D$whK1cc`&Itr_0rW3x~IH_fCdlv(=rya}4Rb`FJSjA}Rx z!5ZFynP|2ZhPOKegbu7s@e*ay|e$;E` z#yS`oaqTs7CyB?v>3fW*&$aIPwEH>>U7067A4KW(qi3oKL5B*C!z@cM))@Tc;^zbd2t(z5>8XkXRYYFaI3bknlzh}46d;_~=>3R(zi3lus*9ScFZ?u@M@tMFsJd+i zm6&1OxW3FU&V*lSKGSY`)Yk~CD*|*%7{B5lE}kvHmg-1G4^p;}Ik+U0BRZ#L-4cs4 znxh$zNJUCG6;v15&n{$eQPaGHY#$EkNmrT3;Py+Q1E(_Vjm|qw);xN8nC{mh82$}Ob^*{7a+uo(K_p!Ensm@Vk zFn)w<5;`+hWMUFZQnNmKfpVz;9AV|$UZSsaYRD*CBW`5>XC-2h{^FUw(oSNxy5`f@ zKQdAA4?F*^^*UgubmXi=%9miRO+OVHGiCB*4OK7J8iby;2<6yiCwID}>%M8WX>% zTYEg7IM?FeYgtxfA{onp01Vr%O{{G>^pt1}Yhoo)SunjSl>TIl1wvnN0SE+|kz}vg zK1Z;fOHqu5KOA#1X}y3sTywaX@=FkSllD57eaVm_3CQUKyKWDHOLe#KQs}TL()XRa z4Pna*DA#M46e)yrq+_I~e^c61atK43n} zT=<%BxygrCYh$XuAVHF!SFF@2Lji7^RIMZZX6&JHqahbv9QJ(*hxT@_f@BUIlrmNS zJx=`tw*$ZNsy>+MgYZEC&+TXsY*p%GG%ETrYa5Ri*LSCSTs}y<0Xcj|=HFsreHHK@e(mq!V^#Uf;#0J=iBX-T$z5 z=dJ6vsTYtA(%V0lte9$wO)y*d$r#_+-T|)zjaJ^~$pl*-F;u+LQ-R~INRifO!U-l> zlJ2U(j0gRhGzo4hpn8`mc%Q3j#r2J2*QiNzzbd>FcG6}> z9fcdAe@dV7qvr53N$m-Vec&YIzF_}O+HCXO@0zx1rR=*4*FUXLlBmnVrj9oGzaPpy z1V84mbhLoe(8v9r&j0;@DJN~(8Ay#4NELQ#nf>(6$*{xH>m77EB=h8&MNXjLR~B%N zZTU5F_UG5k>p1VRuoMxA*QPcpCtbtE`Afu1D_h@y$kxYA z$7R29>fxOo7HpBV2bH?hy7m0+)mv>*I%r_qy zu}c4&HI*9+hett4Kbw~WONG$G8uc_I^Ia;O)9+=7ZA}v5DQmsKw>W=kf~S;zJ!P6I zHd-wm%peGTeII~|Wm2<|DYbW#Cr5@>>$Nm3@62UWt9`ZUDI2!K>g*qbl-2Io)ysXz zhFWNe3vDnEO8K@I+D)zA!ohj&U;D+CyVe_BSj63F%*nL-D=iWib4N@vtRc?*%`liy zxb-D7CMM~H$r907%%QT8t|*&xZ1`uUKrhvqPmfwdOF3@uSF(r+vTWnS%H7#bR1l-T z@nNK%;S2>uVsTw=IA*FXDeGBj#gS7%F$S>{?7=pL8*qm?>w^drey`=1!zZUK)3%WO zR&>XRIl_1Y?n@Z_u-Dz7Fb&@`RZl43aLUN;X47vE>PBBGm*LRHN9x(g#EpEnd8@8E z>1DRnzlNRYED0#YDnK`^m+!`ghw%F2urItiHJ%kI{S=G43ftVit9AhT%j-Mps+nKf z$QGb_FoZ08#d9xAyclC`cd5F;DaGGdYh&~T`vy{F*~foWEZ48mUOswPLP&8TPZaA_ z|8O!v&V~seZiSYl7jfJ}7%){MvF5oS67ADCiQOw}(?#%ApllZsgGlH+Du(h&G?SWn z5{9+GOXUWyAXMlfX-h?*V&hw>x}Jht6*j#O!clGi_M<%DC(#-ts>W&OAlw65R?1D< z!xw`{X9<0`hb;Y#nwl`EV7GKW{{U5#*`cO6f&iXSMMZ}V07r3IA;Ig=@@r4r{yI+; z09|?L+K^RB*unCshvYX`6}|pt&Jktvi4^=|P$i5}YjeoTjg0QE6qoUPXvP{Q)KPlH zG3oX`smJ*>vZ^~(#%jO0HLC~M`&s`GWdA-)S+iI5pU0)ll7>cvZcg95*}}f96XSwe zBGpowNhUo6RIk=B97M;rzfy0ZTW`f@Bf%trEnWR0!3X(euxYbQe^X!8SRX1*EC5F- zzjMFDahqR{Tpsyp1xIO`R(34gQebLu3SS<{&3pjliuq`y~JoeYfu&{RX*w8;oKdkg+{itWxc}UJUq4^2Lt176J z+4^iTet$W5Soxm*?lJ|smQ7Ze_(I~(fssS|?5j5FDPZd*W@zcM=}2?0J%)KmG8|*Y z_Dtf~dY71`Xe>rR?ioO_noX9TpsnMn}9=2Iy;Q815N}E{9|P z=6`TWdJ)BYR{Fuc|2^;T_lN5DdohpVOUBeZO1_cpN}A>oQqMp3jZw5_)hJDDTD!IwvG-OHGfIt8G!3z8#NM&@C`wyGsl8Y2y=RN+lke|&e$OBI z`<&}OSI%|r`}2OkUhxiZRva+=e_WM12o-9VzGpPQ&)M2DW9Avg#nO77w6K*)O}~w5 zw}x3a$`wDB+{JRps$FV=2&!WjHLvHLzRvMI4Y|6$zm+z5<w=0fIz-H!PmzaHP(U8|~GW%orJ4Ck?OqcImSw z_X?6D>f7LPQgbIpFJ%N(8`!GNHIfUjJ zeR6V0mPhlwT;i?P*QT`aN1hTPM(1=%AlZQ7Dlf-=e46<=(!l_qw~&#J@lVKrE*y7? zx~7M4Rxu(z0a^ck9_mTf=3eNdo`&YPlKd6a5{t_uJ zSyoyH>9TD|;ICx+Hfpf1_tCN3N}`gz_-ea$c$MDxdtT^a7O;u2yO{nPxOiw#XrR^2 zQ3U&J=()0dOi@vKs)PV0>XXxHQKqy9?V^FHZ6F_W_e&GM0o}lx4^0I%k$Oa=(Oc&E zq`dw!zG|By0?SqnMs#z&)>e0Nx)9)vuYPJiRvPzGeoNOe8Mm@5r7yz&RDRUAj7+b= zT}d8tp^tnlzhY}$BPRL)2ey&+E1I-Yx%Re}4^{?ybs9{oLb;h1^yG9AJuG}p#J(`B z$Iywp3SZi|K6(hdm-F(Bt-&*byN$+K1w$|Z0~D2VIax#?ONmMnr=4EX*0#RQ`rkae zRi+uJ+29V59HQSIDvYyg7Y)Gi4o+gD|CYs!?I*zd7tE4hxdAg2VdVc@dw)qC?~SL zil|^6eU?Noxx5;r%)*cV?cnFdcF%Zbwj1j-y;N7Xm?Wh9qB2G+Ii|lVrb+TdtY9xt zo`~QmFLZAnPc7k?gTMFsNHY%o6M?d|fruShL_NFJ=)O;9>B#k{L19Z-re$I(#@{=C znqcL+E2TZqsv}vvMRBre?r^9ApZAA1w=Z3FmH4kxlLPl~KAfNnz1oqQ$z6Q0=KZZV zuqUO1H7^fCwD7?SZs~L9egMnN_Gk$F?aLI zbQR$^Qc_W9oWtZaxr`V%M~Xbo zl9JztN^hS05BL8+vj6}7^xY-b4~ls)K^DbX6h*|8z<)^rK0Ef19cYo0Jp1AAL9$cI z2UHi^N!RBMxlok9qW`{ zD;ausDL0Ufp_=~!$MMa}nR-T$qV6fl>;xS}^|487p>EE?R(U|bkMU;RYf(Mi^T8gTyga2n3~~Nq5*46+Ei3JC?!s+@8{lI?>$5ZxC0HdiyI)h9 z_d7|u>E3x!iDLnJ?Si-tuljgmq zPP3GZ))sZt5$Wp4PH$^w%*{V_*Y?kd%~zDj72?E3FAn%PwvuTdlaqkHYMsgVYi=ms za!d*h$+!u}NO?L{M4dGLTPf-kf@~>6=O=&n_aC z*6?uj7#Q|S?==%wVBX&&>5{M{|8ldu#CcWl(VnMjxzp0z8d` zTgM%_{5m-XX__N2L9I~isRceQ(|9W1GMZ0SCF;%zHXO;keNF1Z%APv<X_p9 z%5ZM@cN=cRk6PXm&W>laKxgJ!d}aGe4S>RQt!?VJ{H2f!>%*q{Bol5rNkOw^>Dz%r z2PS7K4R!Y#;EBbqp0Gi~NmkCo%#N6;2~~{Ny{*_6#mT-#zXLQPHQstCL5v6hPa2~T z^EvQiHj4Tdw3|e8?@I(5`CB{b2XfK#OItls->HmoiS4X)qi^*87GKj|-H6a2{Wc`O z9K$S6N13-V9UNfd0K7KmCfk)h3)Tqof8+hPTFU>>WS1Ej@vaScATdkNh{=?Ea{7bx zC}quHIcCvZbiPei*90LI=Sg%Tw>gcKuANRCiG}LG-*!k3EUoX-6Fc-8i*G@G(TLn! zwyNI_>Nc;f?yhqRmyCMZ$E?6ny(-Lx`;N|K4@Gl~zOl?xaR z-mn&wXJ4>M9sei?rluh-rDTgkl4N~U50%X~ar?+r7vR~?pA3CgYP85~-<$@7};p{yx1e)4t2uI6JF)O* zHcm7*mLtVxIQ!X9LANBCKazC!^mH0Wc_O!Wje9y<=|+@9Ea;ouDH_1oU|K1k@@OTy zDQ56Z`JXl~1BupDvpaSEOlra8LvhX=>pYUo&u{8j^c58iv*h8{!_02yH<}m#))8Ay zl}0%vYibf~2_*37s&_{=x=NHk?cO>Bq^aVgc+M#V&S&Vx&_ua;ZCUM^wg{v2t z_a0Yc<-UQCUAWLUPP)%eGA2v;#tT7ZRE27_d8fmxAJi2M7JDnr!$Nck&Tl3$BGkCl zkr9EYgg7B_*0IlSmTj)48MgbTiF`<&O*f5fR>Mba;gXWZw%9}zzXuR?G6z(1AYoE> zXXEX(bwXMC_sn41TKB)2gOB@Oa&v;ZD>D0#yt?te(r8w4G4{kdsNW(8&gk!B z&+cJnS1BG1khguVJ@ATa|I*ZRtNm8$F%co_XVi{)(_#rmSU_JqkEwT(9`GzMey;eu z=`f76RDe~ z7Y-X>DY5)2RYyunA)*}Q+~?uZ1bEYyI0h>2!NsSc^@N5y8=v3;oo>h76-6)Cxz`3I zGu}}dNH(o}sM+2+)39hp_YuN*Um{+Z^;a(`z%q_pVocQvq&Hxmh(8oR>*=S}7JVh& zcd1VuH#Z)gNFS!+weV+zhk}xbn_C5n34L&Cf6%Jv*rE;s*l-BPN87b1sh%yD2iP~Q zJ~1A7Fa-$P&(Ji)VQ_}YzsVWy`Vr}sgw6@*Ll3QqqYCFsiBemdJkOi{z;8yj_vRlt zQ2Y92tgI4wsds$B1QG5|EI$;CF0C^jQmgIq`E}j45O%=F^yjODejMl-0P90%^U0#M zKq~s!V^@|yQT-{S8evoPktn>n{FDs62Wl_6iUrC6rC3kh;qjmi`yBz*F6nUN2$n(w z6J*~Yl;#FTT3Ab^0lW;#O)+w&S>X_(wEiCF4=N;3k|7DswG5&6iS)BsgZ}o|cL7-A zV^KvGwNRlf-0_C#3{-|0KAEeeYSVpb3$N6nD1CaS@?L zqdIZl*q5b+4d1;Wl}+O3)hNuyt8z*|X(AFnb)r+UcYp;#L_God7)a)8I+$tYnu#$D z*IXTEI9LpZfR|INY8;jgLP@)ZPCHdNW&`C;850U-o+xrOE2(bw`!RZR5=>X36FI0T zH@=?~j{(7+fi4+_t~p;m#i|T2WK&xD_d|<+IW9A;qkZ!gxx&yiCxRvn3CVoBSAou7 z={wv5i?ad6xw?IjgS?&t&mrr&Y<`dJ4n_9V1mo>UY^;Ppe&>mHI+p zpi2_nTgH@I>vUDJ%J{o6q8=9z~HM;K&SnY)T$4etFajCTM* zoYB>KLUM8oqBjsaA}3uC7JiAAmJ7G+GXBQxCKjUz>F>{>zz~g(&%thc0rs_=F_vV@ zar~n4;k534YZ+B{NxG2xdVCnl^%+LNVd1R+Z8CYGERufzpQ@|tq@>tUy`ft+9t2Hc zgO`Dlmn5o{{0t{oju1tzQ)$Si;hRc8KLahi1Zr9xg0soWHZ|l0^v&MzTg#(RlJ7!v zIc=E~zB*%Ac!%?prLrtHsom1bcM_(e}v0{uzOY+oxY5(rW0=z2#ekW|`>cGP19?x40nZ3&haNmvJHIUaQC2es2$T;PkM@6Ok7 zDzpbm>gL?z91#nGG$Tf)8xt?n%Vu4LZ85?FH(G2uQ}a@j>ZBVD?_Ip^j{Fw-xBD;u z)G2>*?N9NoBM0G91Wx3X?1ZpvA^q)P;!3K5ZK~lMXVsQQP}Jg@3E03&>ScXs_37J{ zFQUtkUKTzG&ybe!=P!!?0KlUs%1kqHKusk+>vX5=Xw2;B#;pYaQEp!!P#J^$&|4q^tLs-zW4g%L}H{HXZ1(ihrw@aD#a7rVbQ0Vx2A#)^R13LKva|a z<1PED@B2O>Y3?)bFk9bZVO6cXr-ZHtpYK}mri`x|$YcuSdn1Dnz0cFUWqPE#mmb)# zystQ_5J;T;VZ>*7tgR1)(V}~br{o+VB(M@g&ypLnX5Oo~Q z6`JZyi+mE-12t!GV`5A9$Hcoe9b+>4TZt&D@h|ox^n9?C=i| zvEUYc5D4#FNk4n~R_&baPM?g`$S8?vSGq9qd(r3%wr^}CGVEKzUE}o{%&|f`CSvCkU*Cp*0iHOzx}Oe2 zrh?B!zQ^5?hLjLZ9K$|1wy$lo2P?m*cdU(S10n^*i@t~6cbC1{`NpMK(B<)`^2oj7 z4~6&L4F)>@Dxbaioqs!XKk3L37NqZFXGa)JJ@`fU=E_zn;%|3TJQ#4EVY^b?p@r!sc0=`_IO)!RDbXI$QnG`ZS2Ug>u(50(M_pON8y4yA-^U8hveKrucy3+f_OJ<3^#Bh(}#xhWwXA zSLsI0tIvXkj|86m$(%UAhXBx4 zr2KV3OXq~(TdlHyFW;qEPFN(rwXtxYC#5=T_(cmxrIo+sJ9J$7<%#zIyj?Z>lhDMT zI|WV#A$xk822DDB9db=)6+WB=beX8X_nHfqdz)}3MdkC!(Y~o6lJ+zX;8%jeQ8Jce zs`^cKeJY;(Y;OA6=vjgtkyM^wtXuq`eI63MqR1+LUQW&6zJ~%ujB1uIQv2j9gS2lx zR}0Ti6oHCRC0(uf+Z4N#yW||SWjM6fM@9ImWLKHkGYU9Fe4HD=2hnVFx#>=dT-E#h zc#*iL5twU5ZI>KQYWo6=%yd4e2)z*ReNeJ)R>l?0A&J7H4TpTRccW$;A589cZ}Bfw zA-xgQ``^d(izXLZtO1pcQU{HH`H#;Df{(sX$Q%iC$Dl5^3AEK+$gTIsok4H>n)L+p zy-;od1bl&U#YfNQ+tU}xLuDWPv#GE{)xgGzrXZS46M_8Cwp8}Gg~W9X|8fP@wEwwC zH}Iksat*s+I@nfs(YV_~wNPsndCt)+%ABzkl|IV*OjK)MvfQatTcs3>QL)4>(Bife`RD%F!^_!Ldx6zT%S>#mCGC)shl!?RAuq9a!ii9 zp(^uk4WH1%4LK=^xiUV-e_x)(jP>xQ7vI661ENDlk7QeM`g4JsGi;Y-e+=B+Qj1JH zi$KdG<0<_{q`w+VUNf=zF%GR9FWWF-LQKY(&jz3FUHZq}=@!X8Zv~-fpTS>nx6D=FT~NYDi>?Ht^nUVz%nBv)A;i=nb8l_yYNA`zglpXHPDH(Zl1J(3j^4NVU>1H#L6hB+uqUKMk+KXRB+~K zBj9(+3i&9uN5_Abl`lcQa+}f=BvDYmV`t*tM;Yu0-z3quQhp7e9*S=kam%-|erJg~ zV=Q@1!k)zPdo6_lh`tW3TZ#d^-GJal65?JWz5?5%otKmeVA^#}j0m9sda`YJk251M z;1FKKBtB(k4P5|V5@}ebF|XB9tUJJ@n0DCY;)WKY9j~MEp6k6l4S)-_sxye#;Q5Hy zB*1H{_DFwK7TC^BMzIQM(&n4U69DTB9A0F|jGLOp6Hw4eR+k&mFD?`?iYMX92rJ_& z?uINCQmAYI%4`DeJhR0960_x%meteAxp{t4B4`RZrCV-YJ%7n66iGkY8|Ku47&XSj%Z_AYQ2Co^Bq=R~Dq$4r;lt$&YQ>jB|R~^JM zAZp>15x+T-Nnq4{NzgehkSs|)eq^fwTIXn(^*z}(HoO(~s{%#tIe}d@@+CqXB(>32 zF-7GUo5C3_=bfI0RrlthS49CVl&4TdbP`~hWxMrYOQhxwWLhL(Le{0QrT+^rl|<_P zvr8SSry$Z(BJEvt{PEg41aNq=R=~vWzK%JSaxNv8AZk%n&N6_Eg%S{WRlgb8rL((k zZ_OHDS!~p3UhytIQh@_=V?x-gaQ7Wb2~rl|CCdXG!NC$C<@(+qil$>plsZ_(aoklw zIiiHaN61MoUv8FpB+-sbO4T z@W(h<=7cm%%zh|gB`+6P#Z)?y(swPTYw zR@L>P(YI^9p=Fc$u9(I7n1}0Py-a}^tP*pYyEB#|Pps=@c@sGEEo^r~{kCC|?8`?5 zE3pYCqK`Ep8%SeOZV!HxV)uqJd3B*FeP;Wx-h+^ECk1C}Z-z{r zh_BtZIps`e@fLA%`4h(V>9O)lV+!4Y`}_o%)9r~tjFL=Z$Cmb+c;&FTLa~HgWI1lf z|5-^qZe5yBR-ih@y#2gH@1e3Se{I(aU213(y@x|d9#%o0{#Ck`tl9+bM#^7) zvF6M&w|iDx*mRCiO#rZ-UqrEEAge6pJ`=&YH-X5qYj??a_ZSsTD+$!lO>gX87U0|V zeqD_Bq!Hd28)1uFOp%XJlD-_{)e8;0?If~{{Z(h+&F#xt*X(0&@21SLp80yXi~)>@ z`I?IuWZN5$DmW2IaMkwiq9}kBYRkMI&*JeX+SC-I``!ZwnIi=m!uHW)&FI3y{QIO5 ziUZOy@bCmzLcXcjmJnic%*PzeI@UVKSf#C!6(Fe#nS4Qo+aGhqto(%K4r}!hr`0)@2UaousmP$bX90tz=MJIQ%tU z(%r}%Lc}J+o0TC1j$Eufi{xn;L;UT&agYq5fDg!?A%Uqwe3E`ks{PE?7US89N3UQF zL317K8KEVOe@s{|mrCnVUO(mKZ$HyUOw5j1(|Z7Sd2h;E zXZ9cGc{q+FiIbGY58nm05dd7kzN#`!7KFtUoJ;sbfvjB>iBt%JOd{zGb*h=v!^*O< zKBe#kxUhYEX8!f^I7CzIuE&7l<(ZWNJw ze^Tkpt_p&`OAgm#B(jj3udGJ4uXkPqUwe$%wU6UArRJWx;v z{^;Sw;ox{YHy0MiT4(%8ij|@S{U<8H)wX)!AK+=eaF=0$oYdGRz0+Ayv^$vtS?ZW- zfg-w4v+aEu7k6)9-c`9QyoO_!iw<{o17F$YM-;bhhTX;sIbAVLmUw{Bx0bF zM}wBU<0(s2U9c|~WlK!hQI+FHl*u0_ICpfGG5wVyrUj(i1UJ~g{{ZZfz*OuFw9 zE$xhjV{_66&KPkw;ZMRnHJ3Ti(DlEmSG<3#uK`+@GH)Cwr0!GK{Mu*`_c@;?e$6)| zaYYACnL}nv(KNKH@V8YJU53A(9BwcE14QlZvrfpRnPPgszx?ca?~_F{HX1`S*U<#r zo7j!HVo*K{sQ#NJLu+zna=_pt(N?ukL2G9I!q*KE(K4d%pJu&GJw>gA=x}#ARcS=f z7>u2kl>JgWgl`iqcQ!}Jz+CUE{O5Y#1R)y#0OXk}{{VwrE5qmJFZWj3l7{5Q~2nDmB=;R=t9sw8_u}#)i9y|JOZIU68&(;gLO3ZNueBcYLxCLyhQ=jP~lv{ zO8VwoD1XicXqQ9zm7UhMaf7h??Y)*T6FcD<_T$uislb3Rqt2u5Vdq20)L`h;j>`js zYBOc^)*4QMLwdH$Z~0#5PIbT7N#czyt-4wGoA-32#+&T@GNO0Jt7lZSQd}PDJ}5H( zHC|_ly!k_Dv&DgXzQm&4tAgJ2#kpncp9GswEwwrh%XJnSgw7nZ{AK-)Z#)0TAD$q@ zrj7{B@*%?zoYzYO;^&^2a;D(u*+dv z{%%^FE}lqXQc_Tr9&k_eM&f9$#~40DaL+f-m%O;0qRu8BSd1a=t0mIAgaT2c4zY?3 z4mOI9ckKFb>%g#W*njr)!E_o4@LF*qu#9WRQ9r?!c63Yu+OZ3FO@1*u#%+2@2MY+H zXeW3`Z?K1rgzc->Vnz#z+5M-eDQRl1IJ*)km5eAMyX?|ocu{Kp8Em|yfiJrb?Yc}kZ$cOygb+9dwKIR zZ{`7--!lSAiXIbZnzroUP1qy2ydJlHTU#lQ5pFShXU7Im`Ylr^li0cS>9vzrwMl$4 zWo{fN&MJY(O&qLyAg;$Qt&fXd*x!AJR=3$(hLk&XCf*x56<`iOkMjC>->5c;;(>PM z3$gsqtHzD}sv7~QQRcdqqv>8(j=oHxGz2$jcmMbJ)AkR^`zKG-U&bn~Z+z*PpeRS*3C$ps0 z@-IVN8XuflL_(BrDp$xGX-a){;>ipBlNu|1%|X^Lkj_AybMq5lAyCW(O)&iHEfSJX5S zEf~e=9qH`}?XvF=fYd#8A+m}7NyV;id$zhj8h_Gym#)4=RCM5giC}sKy+|}q#bdZL zs*e~G$PWL%6a%%3VB*L-0F$uA8*-Nwv8_AG<>oGmKGY-T4r>2@(BOZk}97W*--Sc+Fa>dPeR{Y(FxTAkW?{;~W^{H3-^M8}f=!j0Z8B;8d3pH&|x9$DpJ5ilr7jc6uk4 zTtiNoO-HwKR&+219%*yM=TO4O*Bu;6bNkV&ZQMn4M0MP2-FNJ4DC-QNS%<<_rs>HY z)LMp|6mwGhJM>*%K2vhgp6Eo`OX3yPwK=>AGObRDP@=LaJc9N}4DjgxE}Oj5<4__S zBDe@-d9qxm_Fy6yr7I&mP{_cnv^g0a%~RTxYGD74S}5?d4i3#aN5iMYzT(R|#;BW^ zltsMRy9h+%ovNPAXg9TG^o2f*rvE>7feP_i$bnQukbmdvg!Rzt6$-qS<(`@p#mS5Q z>tg)@H${kq{jSS`QpTS30zRhu&KamN#rk2^`n#NUssye>-TU0rITp=4_S4b>YRsOo zk;$u3=Xp8NlQ*Jym32%qpeTQw4A!(_JZ0?YU1Xbmm6C#HvH|noxStYeET|2oYsq(R6yy4gZ3@T z+KGACUSDg(so1nu(81KKavW@#FY`dKshno_T!{5_YvJ%IouX)0zYzb;D1@RKPfl|T zd&a{-*0s_*wB-0)`Z~ji^))fR8&GQziKW`GN_u^=mH2JF8#IXNv|1kMm4@T1cw2oP zEk)=6WY-Wh`~3#%6;&*v3ql%}3Rh^iJtdeLB&poYi3OuCE`~=)lte^F#H0<2UtoZ0 zjn5)eU5O3>GaZzpEpd_IjWn=s*3pC%33M=LrbKoImmiYxSEKy?H%y5R9$jl|OnEsY;uXNUleN&e)H>D?5 zw+MS({I0J*hes2e!IzUMGTi|V+}tv!?mTDcQ^>Z?*WfEcro4e@Ea0?4P%C>w(fiKI z0?pqotMK+Nqf>^P)1qUoyea?|0%_CZtg11FYQl|YHQm^*R-bw0Y{W)~H6Csl9ms^$8ALNV*ulu+hd*c1`U(C6z!K>!edd(_j6H9Z z1PzqRMshF;6IzX*?eq;S*Ktn*?uP~4cTQb7z{7tmDM0Ds_&2|VThwIamTJ8gIMZ6K zA4yMlIhk2}yYn~4&u3clLy6<4+~@ox*PGgsaVW#O@E?mBNPTFbfj4U!+rulCzlnKG z0Yn&xM$wGsc<|B}Whu#n%f<7rWz;U_JfqKIXgBM9R##d^%kQx(9QlOxvf1@iQ2qXN zY5Yd!w*%wE`!?2T!ZI7fS1RcfBuN4Mi&hFjL`e2)F6-$Z!g6Mm%=6!A`1*=vji7zw zOoaG&|ICvzLLSkdf})wKP{?;2F)dNB0hQIs?ELG?d-doeTAM|NsFnV0)xMGn6x z4YBd@F6L7Pdg_ev&wi;3oDk)$-ul{Nw4yE@eyuQ(nw0Npm#TDbGc)PI|AeAnaW1jvJtX8c~ugAH(pZ0mkPD(H6*KupE{Rs_;QI2uWiCRY75 z$tj6LKbnuX?uedpjpym`ki3;~W{3e;dT7C`v;Gl#l|>uv$wb7)7>A9le5VrRMuB`t z9F24N1&KQyruD+Qe)r*QHqp64U^JheIY)HMlYM!yX?DBXHGEE)PtKyhWa@IweN20NEumjSRycx}Q;2Wy8`95Uf^_gs%e>4UX&ouPIT24-K8X#TB>pu9T; z(5n(RL-B(cs%YX?5CgQtc)mRQVU%j?k@Y9OM8QVOfr|^i~ z_UQZgPaHT@8AdE}5&@0akoW=AR#~?G4DRnp%#TI~Ze&#jB55J~4MwFW_D<`CL0!wO z4#ND#ZN2&r2-cKcGnV|ebBn!`ewUZnOg?4AaAP>?0BCC^B}CV^#}@O{ZPV?CGXefN?fJ^1)5^6652gD&>*?eEv-KIjwD zCh72?_MrYRRho6nEHX3tqv}q~>h-$nAG}4H*QG8dFxQ=#6 z#q$+&dWEhD_mHmx;T~HRFtn`90O~E-WIxsSf%xJze{!gIM$*taTweZ07@txeCvIDp zdh=$bBpdv%W}Y2&zL70mcZGWe>FQRRm$Rnm#!OqA!11r%v!})vl_t?RN z!NI*Rfw@~}ao@yRdmsL~Ct=ukf84Z2XeBI^|AK>vkc)gnU)^F712Qqm(|Q(A=!K>h z0&<@){=SW$YKEeVNRa3!cHf#3wuTUygnJ{W%K6EQi`>kyzq!g|o~Mp@rB(H^CQL5S zQDb|gk!I>9Hfx+XFig2&o)9)*YB(`cnz!dSoHrD~4())FC@pUrP)qR<1$<7=9w}JT zH)$>_Cn&qF(0ozbdv^+Tela)N>qs=x92Nw|u|0NTefuak#*@daGqdvSV*HzeoMaH-R4g z;oPZkFHYo@fC`r_BS_4rf5tU>g*tfdwu!Li(;prTmiDCzpK-RHB<+moHE+F$FgUv> zahSihg%-cKb%ZG4R z*^tUnwHX;s{n3Za;J54oaAu^7YTWCaf^g0-AYYpQObG?&WC&3fO^eV1JX{cE2f@^f zF;>I5_rBv6BL@&L-maA{nl82W@9U&k0|PoVNz5B0yu_8<56IHGx$Q1G0zUbvT&om) zs~A2%rX?xQiAxlbk}LxlVky6;}9uwLM7lFiz{cbZpUnKLeCHmpXuhj+F+;xOgAXO&@+ z`cgA*J*3N7kIB{2L&9>jdGifl$4=VZuLDp`jYk&GfE?eC8l0yQ&HDVcK>h#fT^b_|e-BOYy+V^8IeV4uAYhoY9Gk&Zo=pzOy34%m8rzy zShEajGm?bL0&-gTJUk_`ODezjA>&9$Q{Q-0kb)PYhhMe>Xyi)&1f)u&M_0{w&-J5V zzq3g}VEPe84#G%G@lVbBV5+++?|D_Rp!Tnra@B*S?$;;eiE2&>$uC)@J@~2Pu-+{<%QTpBU_KTCnMOYe9 zjD|&oln}5L*&LVf5eDgWRAeyb%!U%#J~KUC0eUu?a;Mvd%H4U1mv}AK#Gkwml%6}p zU#YYjs%t!C))K?1N)#^?X8+|M$O>sED*|NHz13^dP=~j*$z8H)R0)Lbg||blkXj1a z#J;%N^@oM4a;UvJMcuxb@76y+ugxLm5uukQRa5n{L~-NGfF~W36devVR`kimPIdFA zAVVz40?|(eT57A-&xK57N-{w{=e*RFTyemrRyR>;t!ifz+(mKrc~3HCQTm=rv@abC z=9jMz7Exhpc%qtTGq_S_VKkR(nXL>^P*yDEEV+Of5OV`?&s4ziB0zJ*c)3D;B(ZiH z@1yuQRg4`CxPbwb425CC*Ry^aMDCJ~vL@y_Gp)X&0m2W3MNqRRJc_2f&m^^;eJzw+ zjTKbRN6|*vVkR`BCxA#BVh|GEb&wzXoFozy0=>Y(TFg*fjab+@mwLFxi}^NsXz4O? z_vpuKEUQgXPuq(DvW!G5^PXkk*6Pf?*rEmBXGw?q-r5qCEc`v~BhJqQHMfp5(hDQJ z)|)dZu&N^6q9K!pZN!tjpNyHk)M@(Ol$)jlUjteB(_j2dxPg5v^sO~0+|OdMAwrDy z{dD?#Uec|it$i+(z_}iuNOLH|!yVNfiBWswI)NrZyFd-0MyNFm`zL-$hwIg97)g9s z^f9HFs%65CwGe7z(1OwH`-nqxH4kS~eL`N(#@9Am+#XSV?C`OxtMwM4Sl{EiKR;!s zjbh@;q}c3(G$r~IUBObss&)HC730p(8?D2Ji1tf|s3&fDb9LD7op%$2J8FmP zE+TuNN17shC&)yw8`Fkd1-e3=MYnZymu7ro|I0?;O{UeuYzNJw{Xm)Rpu&lBwpxb| z;-D{!Tp5l7Y@Dr@KMzgJ$CFb|Rqa{+0Rm!%+Z>E3pabcuvz-?bq>=la>N1vuDuS&*@6H5@Z;+KmZ=sD3cs+g9>RJRChdd4P#C$vx&@ zUR}*7UjB98d@B52)fV|JJ5U!VNLsq1BJE^bxv|;uLsNPcGbS%1al>3}%(l6?729(x zSZ<3w9!;|LzuJEA6|2hl^_L<=W1x(9v#`)Y7H1? zr%FQ;i#dGMeV}L2?SFt6QnN4NyJ|D)(B;0@37Lh&7jdKGHw}U6*yjWMk~cSQfe-&o zly24+OlU^P`00!qOhS&nx2S&>Ew>@k|1in@f!kwmfQ2dUEwi+Vlor2T-sr~74uP-gFag1C7bGp8k=$bMVUWk`j{2h?bBKxiR%_9{gz)5t1J9C?8 zZzS9Zh2HA;Y42n(uaRDBarsWGIzvoy#A1i zrZTIk*X`{v2qQ+&^mojQfj;K|DZUtF?^U4WqtYLFL5E+ftR^QyezG4odq}%XUOzOu zWA!TjfxEqhho9Jjo+&;5UBt21PbH>&{+jV@jZ!{kW5%zp07llao1H(xrHDoFXHTP7 z+%491)cqSR>wMMlRl?t%o!;tcphtU`J1^rx1J27R3ubAcowj}LakP&cw_r^(6b$)* z1;lXnB=NlgPY(TlLF+8e+7cudK4K_wx0?T;5R2V6;KY(J4VZsTwwddJfQk)-L(s7| zU*bENdBipCA~F7Fyf9m%WJ|;VQ(gi-fPT89rp7Xa(5i2nUnMBWCGxFbS#uJv%e&i8 z>pb)@QtEhHhMpI9oQ03{saKqgZ=tdwU-v!2kAk@7E>8ZRI zM_#|Ey3D(X<^5%A?RPt4lCh;S=Zr0p*JvURaE@Ouyc>9CC5BPyS7zyamBg2Y3tZfU z-{wFs<7g1t(p}_o#1dUw6)0QcE*4Y|1A#!N`A5q-16ruUM_c8)KQaC!E)r&nawMKw zHhKbZxyq=$=jL=`l^wULYBImJ9BtWZZ?&^RnL53hTbI*}0U$1q!0moPZeLGN31svS zqxzu{t=nY2*SAM+bQC>wC)*q-1YYsu17FhDnZ}<~JU(dI`SeK}otXF>Im05G?z z->dC3f@;ei^5gT>v67@p2S+B1C~m=+N(gDXhW7)FwM20 z{0Eb10^d@YYDP~A82RHHaTz40qY3Y0m5a)uW5v%)p6f4RNvTiQrR9or)P;z>e-jDh z075gAEhQ}j*c+WpOs=S8-0Wt$a^CDEA!w<&>MRoRGF1$(Oe%oG?|Fr)HJ9%`w$~QY zbO{=tiUg2`pi|w-%KZTVi8y|HF`Xm!e}E8&)cHio8mf2!xeyP$JxF!mf9GEsU;TVSh=74xs$TFkHZ0F~9jsR}b)m$wrxKVJZw%RofC$s){!{I(U*#u86g_`a5FN|3;8JXFBwvyx)81CcTz z{;D1fV7aOCp<6nfkC%%TF(zp2Wlp5syISd^@#VGL*J^dOv(u^Ovp&e|jpT^(>8+ zl%cUt%Iz7_=rwm4gOe*KX}VHzLAZ%hwogbm%gxsW5DaW6Y=x}5su_{xEyv#l@s{G! z>}nm`fj=k)q+n}kAqWUB%)8uxYQYE@PUG)xAoUCSOwwR?qax3O0#IXP&GKtpu z)D+SnPrk&@46mOwISznN87o9Zd>w}Ev7$Hcbx<9XYj;d|{vJW9fO!`LtrZDUIOW$eAH*9)fU-RU(&3?494NQANB{ zPy%ux{*3n&1h20&J9eyGlNY6{xX*B`W5$Yo3;MI--eVfqnjVZMaSj%f#?IO&KUjK+ zXnNq8@#oqrkF=xQylcy7pwDksiB@gX8v96F`(&%NlX8-|F`QyOOy3qHEn$$Zn;3}l z%A&eU9fRs6a{l}4Syaf_H2Ed$`iXFky>#WE@8f}AslC=Sq_@o#w|B4b?|M7Bcu134 z<^0dr8h@(Mtr4fnT55ZHqn2U64IdR1&B|XtA}q`2(bA9V+3gM^LLR@B_-WTMptEU4 zUc>gOSmSy)RKC<0!tHor$f)%(qmvt@QYduOgaxS9PsH{?2jD|UqToZ~Mf~s7XSCMgo#uG-RRRW|FD9Us&hD#1HCWFtQwR?u+XF%XeRwa)7CX zt8}}kdtuDf!AIG-TsGh`r4MO;ob3$(vUSckXl^9M z^GTq?w)_j47r;mYF>3_r`1x{cN|v_(T%2%SJI(|gg(YdKNWXV}SAt?DTbeQ?g%V`y z9&qc8qF|p`XxDW{G;+DKalNt1Z&6VRA^k$m$4|ALZ#4E3GYyUzAx%l$zQ-f2azlUj zpp4lZo``tNm}xn3k1xLI?gx9Xu)->QwV?lL86^V}w?a%+@ODWojEL|G5z`n|p|l7+ z2y}B0se?@&7P(PAh$M`pmas!Fyh}=jRIckti>@fjNJ>(w?@%s7rEeQjQLcc`)@oB| z3zVJ9dyYN6#AZdk%p1CqUw;6E=O`g(q9?uo7^4uzGXU;C%;m3rzI)CkaK)@G97gAI z`ALNl5GK?@D!Z@@;a>+FI0vgn(dWJ05Ecxj7@bC3*dS@#ZD#??3{@6q%G{aw88Jjr zLL{P@$5ha?5-d(0xK+aq0^GRmTuqK<{Z_H4pHA{xPMN&Eq;!9_KkZF@Ak}SF;46LV zbkVDA`S@(kdZsNX=B& z@GgXfWtO>e_wWfZ?V?AXa*$kg3>{8?7Q|~GD`(_vT3a}Qo&wkcey!p^(e0A5NTyDv zknf3(jzKwRh5Sy$qJ8X-J`P1MW5O!LV9lwcNur@D#0=JWQFq2KwYiwyRBbyj@z8EW}DARb`>}-a+M2!f~lxa)r7@^U6Fb_?wMZIcYmO$Qcl`bXG z;ktJie?qqX*fcL4HpR?{!Or!LvI1=d)q6*?_4x7s zQ7WiXt3@NU(jruCf*4h^_FhGeB2-1psMe^~h!CqS5xYi=+7vCV-P(IpRqVZ2>HBv- z-{1M3-}(LF+>?KTo5a0dujljez!z~WM=Q#{LVI{T3BK$6dB_DPH+`}Ac<-(Pqks8Q zjnWRXQ|HX&$o`}~V8?S{I2gM zMlQ%3Z@r&1*gWFg-*s;CXO*%S8JDf6{{^~NW!;l*PB!>Ai!btA=CpTeyl8m!^l$m( zK80rLp`neyn{zz}KV~Lvw+%C3_fzf4|K>571mJc~%;)dl!&7wxm9Hfo?VoVC{53dd zA3Dd(2}emkwrM@{+se+-^9a9x&rYP;fk3JtgX9x5vHxtJZ+_5bEaB4Uvh=z7yxmf} zuNwUfwq~CuQzt^n8-9F0@7+o1!2C{6_smoP%jr}Zk-?7CtW^A7fx zMf5e$T_sNqZxT9!u@`C?^&(2G$f}!dZ=n@ zVu}XxClwy}P)Ee;9+s69B0q(A#Ma%t@_vEqrsdPdgzOdkY4(qwZAJ(X-Sly&aDHm!daMK|q4dV?|AaV+vdLz~JUb zGh?DF1$9f)zsHJtLZRR};fMGy%~ggvZwr^+^hkEK@((356B1m(pLkaLfSJ-^o_QTFHKZPZE`)i3;O>{yf663<%pBo&psd2ojstB)#&|BqC-!BrdM^a3L(pRtd{#_sP9 zK1{w>6X=8BvCrfd%=7=HS8xZ8yx)1W-`bDu9d;D*{wDULR+m@uYyDAKZ)s%vCvD~~ zCk4F1e#{xkm*Q8k+b1efrO0np%QPMHXL3U9QoZmm^+K0iYO3zu!>Cq*Vw(-@}8A@>LQ_6f33J?DkmP$K5`v`6NOG_|iRF_m)uwnI#?(^^R*!Zm87eYbE_LPMiM-o!9=1C&8iG_>KYAMR^%LfCWOTD@o z_*`>Wy7aU$I1UjoQz^GE$`1HLK;fKF1Fkaq9>c;+waM=qZ25xICQCvGs)f^QrJhim z_oXieX$Kf1vO3URbK{Yy+Pes={4mYb{5tY-fqfaEW|j^{YU+N~E*(iWVh)!r)48>tcIFcTr!VzWpm)+L%Tjul2H$p}h=?&OGLLzzGL!#=QTq?Sm0J^T9?c0F zl5+fL*&)HkX*b$wXY<})OJh!-lfoMpWcH`0x2SqB!)^MGa6*IEhmz+UtEJSiiT^-0 z0C07$RN$vi9J%{RD)kqStnpgZXTb!a`yNo9uI-q8arTQEdd=dI1Hs-0#jbA$jen-Y zT#gS4$;+g|GuTMZ|8fS;pQ!%=7WH~7GQcC|;0OSM|Go{*?BxcbI>^rn{lBt<|M!?D z0LoNVp=%54#J));5{LeeJOU6%nCj>s{m(8S*U<$yCW=A-7vk`LkL8*>kw=G#qkT-W zf&#dh{h0B==T|chB>c=?d;)W2>kXx4KL1f9E%X#4K(S8Y&8r;7p)pNi?x{7!Qo;dh8K*N}?Cq?wZm~&oIPEpZxnhNDM<=y4D zDas@tt}qfA$_Lrkds6SkwK|kE!LYoD?CQjNFhUiMiY%QSvlLm8!t{4D36FuFx+rmt zC4@%N2kS(L3IFIhN7*g(KuAsInP}O~o&v#$-GPyj*Upal$rgOT*paGrdUGVrPrPd4 zCEwUHt5eX1AF@~Dd(m=4A4&zYiO)&R|0O0oZ0YRu6&1AhvO2lcL3q%>8^K@x(mnZq z&JNW)?nU6FhTm+s;-@r{74iM0^H4qV)h(leXL$+7)QZsH7#G1cdgN7p%y7I;w8mWZ z!$2_10UFq=sqY!_JJb3Zi#4NQPErA7wkkmPa-k(C@SvzzmghpyI*fJqmaeb5A95L_2RxQI3574#{}4Rn(625Tm$?7J z6nTwDI#0ms)#Ti``ZMa+`!cer%=hZ3&-bnTGvRoN2WR7G7sZ@M!EO~WR?RnWxo9v+ zcINklwE0)}taONw#D9b?lwRdTxruOG!_j}B!*L$j*|P8U&P)}#F2>q24o7{L(TR|X zgMN+;mxBuu4=*y);%K6qF^5>#JIr}_H=pj>GJ-#wV@0qw@HRXwMSvu0Qb`T*L(1ie z+<@&F=niw7p2kyRMul(NCOSvf&M`Nyo|xi)eHAwXI~o#(r~;}`a8_y2cjG(4x7>1K*6M^3Op-%=LgYRU&<3*eLGX@wCt*ZR%Hu3d!9(SQ#T#ft9 z-$qD$lD6K^HLx*Q=TH#Ms(JI$B|)0;bT_BtLiE@ZlJHPZVjC6Fk<9C5dH=2m&uQNI zpuEhU${)>q-Pf$%l3}eS(~Uon611s%zL98>fxwDF+J3KnN7an5&1&H@N17#n#8YVQ zP!p=A$k@7S=(T_hmZ_k8b;?*aQ@-i1ww6h%z);gy%iX8hMG>sBlh2|NF-j%K{u7IP zuu7dxkxE7UvglS@lKG~{(?&m2ja%_9(#!p9##Pn&Df-t&Q|N=3f)w{{K6^z3gz%Zh zo$~YE!z#c2a?>8Q-$wS@uxe1U`e$}5`)zx2L79L&-~@BgidX=)qYR5)ByRC>_msd- zW#%soDC@QXYIo5N{w3$~!T3n!s<@08oBj|l0JP6_8T;Ctm-!#N`<0@TcZQ#avg5$1 ziJ>lpW`5j@pD)9|xSpi^HD8|^pYm0Hi`M=V2ic0NDd1f_zCt2T@u?h3Zx0u!UAcLW z$-m@qG}HE^ZvNG`-{CSEV|#%$1}reOjep!jp}}vEO&zP8$4>Ti=LQ$KFZs_6@XuuS zj+B{CTo!-YK?e4@fwiMZW>9~*>ZTpd{$tyjUZB+_J=)$Dx#d;nd>@7ny7PX|U@YG# zX>;}y+0JD8p;TGTb-DdekfA&y+JeJB^s+|A1^gQ$>GhdIK$)MfH7t%v!TD@76vWA1 z1f{i%MDDq_s>*@+nDi9n);z9%zEnm5b^yQReLsd$wH%PeI-AFl^oRK+@24xT3qI~* z$V>XoBW!$A!;Uj@wVq%sE}q26(Bwy(>l(l>^72F3J4U3C)!I*X@`#1?ZR1ca_G>Lr zIsoxtM%7~0GCeHK>fQZ&i9;ctNNQphLbjJ#uBEFZbsSe33JdI=+4)c5IUAz;Ehc594WHfp1zJ|f0|?c=@eJ~*#W>* zvZ!u(xYpIsoFaFM=imhs##&i0RRfZ-RGEZ|??2!vk3M~wS8?|z#3jDQGx9Dcrb9!| z^;;;hjvpzki*PpU;J6WH^*~4brHj6(i9rSYY8Lv0dr(8oiyHc9nS=O200UKE94%~h z>ZgV`qVnru_0TN5V%!yZH1!7y8}HiFa7F6Srm-py2hGi_W1Mg~vL5SJK^6Etgb` zl8>a%-<`|uZe{(jVc4};EUg%Gp=GGE1LHLi5BYFmr zre7nsKTKLV-j$Q*nq?JEPt6P7=%40xPK4<^o`4rFq@AegUQ{(H-%D$5lOfBgCL}@F z?Cb%M(=^h$V^M1OIb?yryRCH-eZD?b+F6~iGvcP*-|rm6UjGQP_Hxh2XgfIeFZNt@ z@ITNWwoH{b^YqVM)EcO|x9$73YP^&N?`nc5N{2@R9nWJvm&cWti9Ub2fAgm4X?v)V zJ^xzhbw>^TE#N+xF+37$I|_ec67WiLqO(|P^;@zMCmC|JjfPdcW!q|~jYIziaa>Q) ziG~9H%8|TtU2#uxPiPDN8 z<;RQk^C!EdolV^c6-0`%taREy&f=#aMQR)UbWNB~;H4t1#^@E$=Ht8OBUy$}aU4k~ z4r<}{?-({Z@cM`gdLZv88HR(p%Ld{Vl0SgdJCCs@*=kJ?e`GNl`k(^5brw9bQ}`R_ zEkDL_#|0B893iNFFWkRKPGG2>`BG#EbaOlxbxh+0&9lg_4#GbOgxWaqU@bBOS+9Nu zb17^u!+4L$@+SJ}r#Ga7`1AHEXRy1*r@^4r6SHlR!Wp1R2OaZ5ps|t*GHZf`D<-PY9LnN*UP3O+`UYN!jd93 zV`6`Bbx{PeIEwGt?){8v?!&|+*gT+`c|`QRYLhR$;Jgf8a*0HWX#f*<&FB0+>?AoD z+Cs&&D~_Gn95Va^pkz?jxXS9EHKuTwZInDneNAW{#Y~v)erNd9x5TyP{?N0Nbm+%t zNUPdkQIc;>e94X~?A^X!kLxPD`6F5b9TpRNTv@w87_YJ3t8>aG(_aI+{2t)!!qnR( zsf%!_cMs@?m&|x)Z2y^7Od(qNsb|cJ_*LWUe4DWf_e_wJ7@^~ z<5C%ANXv`p1=-7?$^J)$(JfQCTs)C>c;y*wLLvlu^}#Jh9}O*lo|>O?n`Ja*P#Z=} z)KoJkV3``$4OkqGpoegwKbo+#?q2RZgax7l{KrJ&^#T>?v|T5Pok_yLmitq59b}JL zPMUsVIC0T1kAgps(5D|(BJP{(EG&mJ7v=BO|B47386<<*W3m-8zs295ElsB8!@tS9 z65gnRq&A6h1b{6Z(eLN{^4)Y2=~sgI%;w&Us6fh5P7xgjcY=ClKc!Zxs}OOnWD_C?p zlAjvqr)ats3P`Pj&GjZG>De`@PWX-hOA^l~g{7!r5SA(BiH5RhAAqjMZ^E{x89vf@;8x&Bmjpu0m^)MN4wvFfjlVuAL#(66)m`xE<^rhp@V zGx^y0No-YAn3cEBPwj5GE$rx`BNOs(%nKbFkGkx`q6bO#!=^=r4?6!~3iJ6?`-hu# znG4TXa~DBaz_H_{)c~tof&NBtUQd9%1Gu}_L`IVZ#gwYs41%kcl4;>_o?EPXf+%X=lYdum+RLynl> z>}8JPqHV`c)@QZ~`NpbqOO*q`kfXBc?=rMoTeH{b_g#!ymKxpECWj@`mk3*#%$xbu zpZ4Dv_9v0f#|KGLyJi>t;fiz#jViT|d}Q0E4bonN0=LI%U$UMHRP9P1;!_lpY^?62 zYX>1KFRg)2Rl4|`$7I_x>3__nfACcxe-J&mQ0x{>E4!2gc`E>8YVx1|@|+jnGA~XD zKcSTO^g?U7<50fo!AR!xFQS^k!5G>GiOP>f&$zs864vUw0UCr&)9aEAW}2k5<89u+ zDWvtq!3aw_-MztQF?hAe!}IlTqV|1&Scz1#_^s5=&b%bRyg>grT8k;X^!ApEYKOkO z35!H$9mKpnE~#2pRvp6_7-tJa7=TV4yimQD*k}IGd?1wyo!I7B>*Zv5bUU1(pf~e7 z$DO#()q~|A>aJdGKY=qFgeial=Fp_MuQQ)yH?SFwy0d;Wv(jGW$&*^1Mynt$;OVR{ z)GpGSYyt}I<=S`MiLq>9Uh>I1;;(sWHd(j*I<943a_md#iPbLaJ@)*n)5>>#Q`dlQ zexi@E&Yz#RHjAoI3_bl$$B%Ry9+55cA8TYHufaH*Meh(8XH3;Rw%g zL$aFuTz=^n9r#5SWW4m}bz$#<6GP^n`b@?t@Mk8Ki>ZLavZ4P#3YQ$7eo0I;%nM37 zUSs!d#K4MW_WFYT%WJ<}aSJX#Xcvzg+m*`HfB(;K&-INb?l^Uqqi-3Mv~8dLmA1M$ z7CER3zpe3hg2`hEWBo3!F=OyuMXr#)X0s}jV%30P-*25x;I|&y7wk8qUqW?$_MK ziQg4~g$;+<>E)^(#py8O;!Y9eYP892GiPgm^Fki)V038bJwm@W#&DROEq%#L;M`<( zss%=ZqNxM!j}V}n(b|w#OdgYdY>NQYM|+OS_Ue9*u_9#b#YC6MW9GCM{(E{MQZD}< z#`zZ^CC8e4g26#Nuj7b^)G>$8IkcA^|7vjj>B=uKz!oz$gn7+l`cFmV$^%UDg5AKh~H#34A6=cWe6BpSv~ z(!Y7TvxnO;vXu63V_&bQQxBXNXwb13gkumciKd&o79}H0@MSptEN*L-Rmce zqr*P0{UAAcRt}*~_a4F&Rw)VXVLlrxN2i%2^0%RX|7U&N0A;5^PLLOPC0?MNg zaj=+j^es;!WrVv_F}{kS4Tp{}CqbMkL-1x_t8<%y~5>vr#6U6ZL~ekX_23tHGM?lNT*hoMaboNZ-cJAZ`9 zR8v}s*%Be0dN!GODFl8yz#kb{();$VKGu(`KmYM- z;xm3~z--wO!J|A2NJ+9DobY+0D8X!ZV_C9YXqbG0dfjbW=xmRVKf?GqJAxL*@|%LV z&K%R;44Fsw3i#WHAr_nn z?Vb-MlQ+wE{rAHG6pkDdOcB?`FgE`7Sa9~0zzIki(+jVZRe6kTPNHFmibYt z6#T#B{(IUG=Oc2(UAc}BacdRNNLkV%*QA>1WchBy?Z6IN z)DVtJSB0*>S%+2&TFnO2iX|Lw#$fIm6nqg{LvU_ra!5-#nya_+^r6l|es|>VHT3r= zrQ8OVq9eengS){?f}XRVI)><@a#&nJq>i^vu`T~1Z*puSjvA@}l2wZaa3%?*6o{8S zV$-~ZMl_g7NM=~T5h&siGFwcJ2w@SM?Up%7&$9R?^d%b3Vu1Q5B&I@3MT$bcMCR+H z2B7$>#0vyx>7|H@+gSvJBIbPk;Se>{^r#ORA`B1Q(Joz0g3MPH;I6$SrD!C$3I{+X z)O}%R4ke(%0 zYs+LlhoIsc;u5^lHy8_+fQh>?1cGA%BMP)R`F0RTr4-@ZM7xC;Lz3z$(LCJYLS?>^ zV#4;BEx~3Hb~c9dr0*{^l8pP{qG3>ntt=uLT*`^V#AAAGs1+H8BoGTFT}R9&dV-qz zsG+Pa33pw)(D-lF)`TKb7qcgKROcGinb|UKd1pN)ip(^5W_Vptkq@uAY&JWoCBP}r zj3P|Lx5PN{@y)$tA9iOGa7rijYVj2!%G68;DG6c|bVzZ#xZ8fH~E=HCDC4uRXadJn#$@25N_6`(l3LRfu-iGSH(F`B|(_sD|PCCoe*t7Ve? ztHx0}36_U{^NgXY(CWuol}|m%@!zTzjrw_n?abuPuexeAa3N2!7^5wC-&*&5Kz9}@ zj}3Fa1;J z7v1*A{O5j-xVk~o2eorD?!#wbFtS8^Zg?oze++NaZ#m!x`L(-tiFa)$UbaOt4;BAU zSnYnfw7YPVwK{??1eIPcw;t2X*K%ei)2IK6VT9tfmQb;E?ZQ}LYuluIZHU7afgOt2;-&pF8$+US#Sgm zX0{erhQm!-fa}J{?A={R>{2z%778FJa~WBc^Nl1d)Xi<<7Y*e^sN}lumdVWpGwVbr zbDSGUSKGbO%~9Q=0|GI&yrs4dTAkR)WD0Gex{Mt~K;ToMl@If{v?ox^ROtzDU}_fB z{|0Bt9E!F8pPFU7U*-uOhWM5uY6c7*^lrBuVh#YqM-9x$wlUaka@T%D9w_M@eR2RpjT&XUxx zRAco<{Gil?TFLTM4tW7j4#oev>QTvRP+u%j@Y7q~Us?kbZ*uPq@)dVzQTOU_D10MI z@({xP){DMR&P4TtZXt|!D94*#(ZP4sd{P1be$}utQSQ9>bb>=SBtZI{vRnFme zg}XY(W57j;q>XrH6l z20nRi0vxON6vkH>DBwMwQ?Z@9J~?fPrYuqU6DFx$`RHO8R?9-B)Gsr>cGOOHry!UEXSEX`A2mFw?nm6^1Wj z9{Cl;tk5Ysac!D0Br>UXz7$3+@!{8}6R$o^P;JfG)c)w6mz5^*m`(tBhBKpoBBu}% z*!xU}xZBmK3Vh3J8cfVay1jXWQ9njl`T?Iv1tenC+UKFBQ3_DNhN)#P>Tk4vjM1%M zaOKrc{>ivZtyJS#!~t!c2@yBVFO0$0BdFu^AIv2nn~T@8r>6^~uL$te_j#ChCw_*P z7rel6H?Spu9l_Eju3o3>X&;yX%Z$Ugnb4Fb2lhqRzc}T&6nmaqyLI2~*8Q>?FL#qu9$JKH{mx6XCr&#RrA^L<(O4%XA3(*=G$+FJiE z`@AL>y>#98av(sW4=n&wOV59;`VZ9JYd=@lwtY45w@&?@iT=owe?z$TCT)+U`)T$4 z#_0ewajyFsJk*}AkuWVnwNIbS1g6TPZ8$wX`*O4yqaKu08>C*fhbD@*s=6V{E z4_mhG(Yr|dWVcWb;3BTaeJL}@QBD<~z|P*>4KJdr|5IdVXw0yet`vDefonQuT~FnE#mM^n}{i@e)_(ZhV6iZ65t=9 zy{q0{&-vzHNiu#LW4Ce33U;MB`C!P{%oYr6@FRz0j{PH$QjF(+o-jT7Hf z_5cI)HvVmyaOqIq{yhzSE6xbpCS#ESHHnK!L#X;Qch-&f-}J8=dIKM14))$9W0g)L z(_cXdU{ac275`#1h%Kp;X9Wy)B=lME#!E#?HqM}pQ(F0!Ge5iM7WV5O4r^FjE~kz3 z`EFqGDf$nW31c-+?QE#O^6C`aUI>w2s!mC|g>se1EaZXHnh^0zWu~HH24AH~i>+(M z1TBP83fq07^R;zc#HJzfnrhBg2Y$&kO2bF4cu}jr#!V}$j(@*64>N&3m_Gy;bDz46 z%q8X~ZJJmUWwUA1vVe;>HpJa<)4_mq-G)?}nm`aP81@D>JKqE-4x-t;-x+bH$&NMb3C71rRZS1Ye>=Bldla^ne!f);!mB079$#vu9 zPD_!3mK{26?F)EaGB9A?Jb|F*wR`ef`{QF|H~$a3Ezg>dl7kWUZ2azB*$DX^%n>G5 zDQh<)G+342hRm~;IHPu#-N9R%cNu_Q+G~Br!|+4y8FsFFgPK&Ku<+mo$1gcUOSqcR z{J#%U13(%-sRT$kx{VNzE=7xH|AFX8jiT`oGBWny17#r*-GP)mHRr@8#iBLyV+-i- z$mahCDqkfrh}exSN}EOwY0U(YlNwB4G-|IWg}zLg=UyUxow7&_GQOPXXoF`H2(vq$ z|4UAnP3?1S39GvvS+&H^#Jp~26=;W+JdH(ja$X7NETRnmF10-!Zys1cvD!9cNn8Pp zVjp6^5v#85G5wo8{Q0XBQlTgkV}<9K#EzGuxHaF=zjOF2^13AVB;ja#mE`gE%dokG zu1|gZiQ0YTe2lq{cGU+BZ+}JX)^+Wld=BACj@YzeQ^GSKW6e(f+nPCl*p5OSp~?~+ z5QY*h$A<+rkFK_tZ?o|yv}-7H@8#2c9Gv8mr3w64j(WBQzCyNUh^TRniK z=m0_x|Hqs0e~x|taU$#u{e=+RpNj{vY54!eB}9+^7nhK}!v{nF3jepN|NngvaYiX` z4c)5^M+ZEEZ@ewPJWGnPl}dP#7tr~9-NxW7e$me3kMQ>0^{%Ji^!92Eprfm}1}G&T z8^}OBK-ldLpU1QDKAN1rxpMd)NTTZC90jj1WQmB`lzUzHD>&@1NN`<+!^#K9#zjF~ zXUxLsRtm&gk2dv64%wJ$ zSky@JZ0(QKALjbx3Ulme8pQ~0sCFQbeL^X*()`YAfGJ~SZh-38OB6&l6F5IuINHU&Cjl4gF1dgQ`gXsdqVrBu)Vk|bGq z83+GMo+OSukf1v()c3)RD~=p=+EL1}V6dwr+#9q!4o#jCk|7k1bAcP6DOWA|`6#%9 z;srp>rR_zfr2*XO{_iwiBNpY)2F~%?32ih$=d+6jKT+d|zWwZm;8d=vy~>>A(y>YS1=^xn!rx@%-SZu zXdyf6&+nnaSQZ#4!A=15hA$zc-EjDpyR~30FM{7)fa4cx%>O@-i)3}eoi@+nXvhcm z8%Rs+o?eh;nsJfLxY~w9+F-W&-8#iw%O^)~KnKyUQZRuc+Ux5!)=5w2J&FGgLzUwk zEe8H%cWd+i*@u(H*?;>Zt#%_7nMRiGsBm>7SBvKyc>7X1y_IUluUPK81hWjM1c=#v zDCp#JqzAiir+wl#;|m2U66UU)wGWwla))9pIKAjPu#O3=n!h?ElhX30tm#W1kctRN zCyr^r|D`p`2c=m_54Ed#0LsebR0ryq!H3X=T&-T@qb6bSLJ#hzE$$1m5qIeO*^@&T zsMjCaNdG8G3U~tKNeBTd9B@Wwf{|&Dk36!bp!1nB@GXi?YJmQbLB@AVikr4`Fw*E>!nc}Q#&Cg2NQF|wdkvFMWl)YHbs3XW?|N8Fs)^EO&45!%P9ymjytW7%P*gcG>wq%ANXG(bX)JaIjlRn_+!~hIalfwlhk%1XLI3HSHvB~E{B-x z9M46L5#)TsvRYc6#48eff)Qk{L`TEzO5S#ena`qZVGE z|LVHULh;KQCw>l}^Ut|(>&Nf^azoQ|^ebP=aqIz2Cs<%(=dse$suxa`aq*MYu_B$% zIF&b+m*fCYvvp2^Eh`wrFdG=_&T3m$J$RGEw=45?zVTGro@K2r?Mt?h@|UJ}x4=2~ z)uG=i2emtHT)Eg7rsy($x*2af($g@<$ZTr?%Uk#+?uM5;+L6$PjON>VBf1MN~vruwP5U z{kDXbPYctxggiGoumDf9I}Fz7*W&dd&rBqE{xhmCExp=rU#vjiC%mb8kLTLYs9-Rc za-2=z&N8y1{rc{|U4<9FuDYfB1SwexlBfi_3e z_o3{&N&iATZr|3QA$^#(W(il}UFOKFuAbJ^$tk!gX@{6o`&$h-TV2rRO?z{qGV!G@ z9UzEVVJl3$UmxXS?PI>;&WMy#*ym+G;xlLyoUcowyR?l=!*O)~=V(I5xn6J)J0rnDOn3>hE0=INi83a&GDUEfR zQAJ8zby0Z~DezI>!;U%>q(~{nn2xO5@bKjRQ8B^cQySdEm+Pm{w(aEo>I42#U-O@u zu*E0@Z={d)#$aP>#$^JRTv>5<%;qwp-A6vyNuY?vudMdx@nO5u-BWs;MYXHDoYz-yH) zzn~lZ_aQwvxG9{V&~9Ml-aiIgjiVDiyA1L{9C z!p{DO<;IK)X;qRgMiuA*wvka;)iBcgQoTmPt64;^N&S_$+@vlL)8Xoq#qH^?#p34g zaQ`2nh}M68wgu|Ef&Xf}Rs7!wxc8$~4ch>;WXiF9KSbn5UqNs#KbP*j zyXD&si*lh+7rsVkX&JWO?mc_S;H1Fu<%gzv{(pa^SL&|K(|0LG+4EfpNAD<#k|5QO z-!9b?!*B~+S50po>D|9SVr-r!f17Gu1qY3gJ0|B$PBz?>yx4-b)ZrE1cx&-1sLKtJ z9UoiXmx#i7E|+QkF?Wp4=w@gW>u%|_dd?>t86UvU_^ph;!YCBDoxA?o{jc0TXUs^7 z)eCX>MWTMmB@pdK+PjUx!aFk+JeLT@mje(Yz>;77n--^Wh<8o5{K3qDg?zRKio5Yz zrjRKO&SCk^Td7DQjxq8-kh58)@Bz@l&cpu}kOKr5F}?FBDYrsn{l6)c=-)+8DPN_X zAAqR^%AZ&~RMogd`OU!>>((b`pc>QK-P~)e&9y`wF_I8WrFDxxykNJ~6ekOF!UIjK zwgf6L>07}E##{&w6Z25XhS&SmZHqRAz$s<8_gm!K#Wa%dW%k#kTz1NhiuH-n|3Jdj zoQO^%?Zy4ydqv9*rimh_f#3_nA{rw>2U%8?@4RDWdN){D1I3wUm);W=4*I{q7mfBM z_<3j^^f0wwNo1yIv1G(T^A<+iYaN5+pz|dq;($Ey@OE}XH_94kGLXgZ$jl>8Wpbxc z=8*%_^qbe zbY{FHoWgO0O+@+WFXNzabsVeQ>TmXvCQ1>FsZ7l67XN++`H>>d4xd+WlESkd&d-G% z{$YJ^dl6v{PzrUpx^8mvi|~6o=4up!nI5{iXRW-}QAHH|%zUW^J^uNz=KjamU47`y z7-Q_M;W6D3_JvDux7=lPSL=Mnr=)lTREFV>0(L$(yBHe}z_h$Z`8dz9BYYV@I(0s= zCvC2)K1}!z#L^;wdrGoksk1xj)US?`At}t~IZkU$hiY${r&R#7L0~7XV8APh30pNV zGn|Cg)kt#jPmdKCg>BEiQ&98P)c{@6C-C|eG8$RB3X>yxNeY`Vqg zdnoOUtNdHod-ppIz*baNEW7?~e#L!LoB9`Xv@GPPb=AU&vrpbJ_hAPJ1U(_UMeOuC zFSun*O1%;lAAO6hj!pUvIV#N2a0)HpJSg=DHUzo0LUz>kYlYKToKbIieE|Vm4Leht zi$SfrwHc0O<^n!1Mh(m;H!Zt63x%{dqF%!QPr&e~lQ9)yxd!oOOB<2R&-Wk5))dix z58N}mk#UoYA&b$Ee5J8SfHVqmPYUakA1xPIE?>3m@gd|nJtOFEEh#A2#1?7M)2K%Y za8a_3%QHeBk*CRHGcSh~=7VUwTql+V z1R|W6e$~<8#&7uGh|m-VAvsFKY@!q&H7-1ewuCAvY?hd)#a4AE5*Z>#zAe@X_%+fz zbqsVJ0HsORTSx*ubhO>tTz4a{th4nBPooC*Y2$yOt5*NcbzA-e<+i;vnn_#SynSaN zW%c+=%IefZdzZS%wEHI~j$17kqvZ*`!gsFlYNr}4W&C`GvP{+b6XCs=+uq{R@+)&k z`rG60Y!&WzCbZ`)RLgBMC4#80IVWsFe=k_DG`>rx=H(L-JYb`uXTIkvqc0USe-P z`fdxYguLCMbTR{Y)+>Q~XET*{lPjO1W@H$)o`T{(zS7yrjQ)MCT+wJQSUC3Y+xijT z;HAhi7e{N$h1I0Zu*aFv2L{&y!GB%VQpcoY+pMMRrAq5ht@Mw*2YkHx;Uf_i8fNf)9$#>iCDcXLf$f9p7mG)TVGqo$svkTKld% zSgv%u`5Jw$c&bEZ6SGbS?3(;?L%9s52;Q&9G;3{`Q*wA)H9VN*apFsg#-|$HumE8L zIsTKkJ<2*u_cmwe-Llp1EG59H#_xWfKGMF=<@mUwC6>Do$$)=lJFaVAmGwJ>iMC-4 zm>5Qx+k}G{aFX#xF+GAZ1l+=z`AF2x>h3iS(L3eQfC zNJ>MhLyTk?(XkQ9Z}LojOW|#tc3kI8s#V*687$gYf}B6`P+#WXEvuZ?9a(Cu4IkF= zuTPt!izs-nA4u6j^g0=9&0bpxu>zJ ztgLIXyc|(_MUUO#+Hg;lSkNh6x%B0mK=(ypao=(G$#aha_z#9UsuoDv$;P+*^*mq4 zKSB<7p09OghrL!>w+o@rpOXsnH!OaNP=!g9XriOfuQpl#W_H*GLv^f{~6(7a(;;lHE2uGnf}`mp|GCVfsFnnMkLj5_3b16Myjuh~Vko{O8o>VFXvUUC0m!t~$H z1M>lchWb&d$mH$rlF27ClD{iTMQeE95f6d4w^x9ADPsaKgWI~QD7y7M-ntp4cyP9> z%fhnR|HM$*gKO{m-W+q+DBE@UH=LzQxFb%^6;$K*&i_Dm5n6v6KR7B$(7NM3Ab3;U zIGN>W7}0ME8DYMw8$!3YeL_`<@jZkjJ|bjvmpkkqdl(3oL|E0B`vqLl?jsHOhPlKv zyhD0z1brqb;4q_YdHWmNc_Fo2857W>D0+WzUfivZULSxSvZxVR`neGK=!pXF z#$0|7ZjY{9q47Z-^FXHcK{v+(;sI!3d9NHfk|RFz^&@Ra@!c(34tx;j_gBxn#guv_ zYLy}5W&DYSZQd(%K;nz-fLcbjTfK9hwARyTf6_rK|O;?Vc$FT*jn?hV8+84L=S<)vrTlfCTUkRp;ve z{?IO?1B+AP>R^&L%d+@EQ>IhLK1%I)Or5XYVh*Ctv|cF?=I0WY)ZjlhC?3o4J??d~ z5f{um?0;foxEqd5Tah4jB=Z+gED{`C$BG}wLHx;I^KE%>(JYvV?Uqu(WQacyxu=?r z7#NG88u2@5N4dxr!3DvPwCae1RkH*ix%twM=&sIY8n%wW+XcF=ncs4Z!eUbBXyWaX z+3sY!)6#~Z{J0RU)OFGf)GcI&JZKZv&{3D1fFT*M17b3i8K6fYnzm|kLCywf1aY)t z7z37yO^aMr+~IN215xo9M%#?8+p(PMr=z4~stM*6Q<8l%vk`1T)4;4B>#fT&fIBmJ zJIv0Ul3&HDRDu)Fq(k|W0M(mQsGiDCZ|(EKMpivSPu4%b z5AZOOc*iU5ul#$jbn=BLD_3n@o!zpA#kN9?gVF6ibv=2!i;rD;ynsFE&tGjmA?lBw zrME&~6?eXI!V8nMxnMlCggz|yhM!>Ty-=(#N7p0hn4)*c)hA|qYgp5i!Bs3YOCZ|k zr3>d1Qp!}@pApEuopA2#O^??z6A#2D0n6;c>2VHm0XtGK(NQAOJJCf0m7Fc7q45qn z>~;MZbU@Y?xQQ7asx6Sl;PXt>g$sB0?;8z8!DgsdLf}6O?eS>*sJ_%*-gF)m(FRJ0c?rc<2q$nb;7J(@v3@H)7p1?aRkIKA{Km z*?XPGdUk?{j*wyrx^){i0cR7pPA_Df$}Xjr+0#7C5c@BMO;k=XVm3k+bKCNabXpRe zvMg5y^piU6tvegyw}TB$+2qJw--CcO5l0xD11S%E^Ev(fQ3Fy{aZIm`FI?puZKM8+ zo0HVAz|vLtf$rv8;q_JXym$v*>xj&BnmPR2d_#^qwz$Ljs>j%f;Tn%>Pc;c(N+EG( z?i`E94$yj%Nni0-U2TP0R) zNRLDSe`0rzHIsIRZS`~Sh7&)HJWzG7R(tkhd zVC5A(2Uo><(!gkdqS;&H5LN0>6!(x!?Q65fnb5>QMPMz=hzCn!o-EhOGeo}QL1N7~ z*}K-5DP}WNgcUfY{FQzUH4YcnhsqU}xLX$U&>0XrOHlX@$ic|9yIftRUe2RGSFbHl zi2j9i;8Huk?LJNGlJyxTg?-af485?VlFwvC>B z`*bAjo6UnLr28TJH}79Em$qfUivZzdgsGtetZZHX9Sj|16EN9qDRqK%2}vxk8zyL4 zfBGGGfs*Co+Cmn$#Bvq#Xc?o|(<&nR>#Uu8rC3L6C1(K1*L7y4U#MZeD+l~(@owGy z2o7{g!n8K~q`o$FOwsLS{=L%cwtg$>X1;5Z{iW8awbvIJo~Qt6a&v1XOjm!+Lsj5X zC!YhcvMmSK_hU=rJ2_st@%<&C8f?9e*Y#vR+>0-ub ztMzl1|NI1&ozN%mQ>5C`!2;I!iurgtQ;=Xd?mrbuJy4J3m3kGqXldW^{r@ z7ML1eN-vW;%$~ojhKD~j>ij&ElUVYB0y%%VeDyDWx7= zuOWv@DS89Wb5M0xE=w=Q{!Y-@sJr1|r`?-sF7Xupf*N(WGb0+<#|z!fnJP|Roi|3 znd|CYU8i4C;^1?oogdwG{MoBhW|i$n{c&|Q7z?Dsvi21