From b43def20c1b918094f364a7dfe203034796d83f9 Mon Sep 17 00:00:00 2001 From: Martin Owens Date: Mon, 19 Oct 2020 02:48:09 -0400 Subject: [PATCH 1/9] Move set_filter_area to SPFilter --- src/filter-chemistry.cpp | 42 ++++++---------------------------------- src/object/sp-filter.cpp | 33 +++++++++++++++++++++++++++++++ src/object/sp-filter.h | 3 +++ 3 files changed, 42 insertions(+), 36 deletions(-) diff --git a/src/filter-chemistry.cpp b/src/filter-chemistry.cpp index 29fb37d57a..0d08e36a87 100644 --- a/src/filter-chemistry.cpp +++ b/src/filter-chemistry.cpp @@ -56,36 +56,6 @@ static guint count_filter_hrefs(SPObject *o, SPFilter *filter) return i; } -/** - * Sets a suitable filter effects area according to given blur radius, - * expansion and object size. - */ -static void set_filter_area(Inkscape::XML::Node *repr, gdouble radius, - double expansion, double expansionX, - double expansionY, double width, double height) -{ - // TODO: make this more generic, now assumed, that only the blur - // being added can affect the required filter area - - double rx = radius * (expansionY != 0 ? (expansion / expansionY) : 1); - double ry = radius * (expansionX != 0 ? (expansion / expansionX) : 1); - - if (width != 0 && height != 0) { - // If not within the default 10% margin (see - // http://www.w3.org/TR/SVG11/filters.html#FilterEffectsRegion), specify margins - // The 2.4 is an empirical coefficient: at that distance the cutoff is practically invisible - // (the opacity at 2.4*radius is about 3e-3) - double xmargin = 2.4 * (rx) / width; - double ymargin = 2.4 * (ry) / height; - - // TODO: set it in UserSpaceOnUse instead? - sp_repr_set_svg_double(repr, "x", -xmargin); - sp_repr_set_svg_double(repr, "width", 1 + 2 * xmargin); - sp_repr_set_svg_double(repr, "y", -ymargin); - sp_repr_set_svg_double(repr, "height", 1 + 2 * ymargin); - } -} - SPFilter *new_filter(SPDocument *document) { g_return_val_if_fail(document != nullptr, NULL); @@ -205,8 +175,6 @@ new_filter_gaussian_blur (SPDocument *document, gdouble radius, double expansion repr = xml_doc->createElement("svg:filter"); //repr->setAttribute("inkscape:collect", "always"); - set_filter_area(repr, radius, expansion, expansionX, expansionY, - width, height); /* Inkscape now supports both sRGB and linear color-interpolation-filters. * But, for the moment, keep sRGB as default value for new filters. @@ -246,6 +214,8 @@ new_filter_gaussian_blur (SPDocument *document, gdouble radius, double expansion g_assert(b != nullptr); g_assert(SP_IS_GAUSSIANBLUR(b)); + f->set_filter_area(radius, expansion, expansionX, expansionY, width, height); + return f; } @@ -286,7 +256,7 @@ new_filter_blend_gaussian_blur (SPDocument *document, const char *blendmode, gdo SPFilter *f = SP_FILTER( document->getObjectByRepr(repr) ); // Gaussian blur primitive if(radius != 0) { - set_filter_area(repr, radius, expansion, expansionX, expansionY, width, height); + f->set_filter_area(radius, expansion, expansionX, expansionY, width, height); //create feGaussianBlur node Inkscape::XML::Node *b_repr; b_repr = xml_doc->createElement("svg:feGaussianBlur"); @@ -412,10 +382,10 @@ SPFilter *modify_filter_gaussian_blur_from_item(SPDocument *document, SPItem *it } // Set the filter effects area - Inkscape::XML::Node *repr = item->style->getFilter()->getRepr(); - set_filter_area(repr, radius, expansion, i2d.expansionX(), - i2d.expansionY(), width, height); + SPFilter *f = item->style->getFilter(); + f->set_filter_area(radius, expansion, i2d.expansionX(), i2d.expansionY(), width, height); + Inkscape::XML::Node *repr = f->getRepr(); // Search for gaussian blur primitives. If found, set the stdDeviation // of the first one and return. Inkscape::XML::Node *primitive = repr->firstChild(); diff --git a/src/object/sp-filter.cpp b/src/object/sp-filter.cpp index 28bfd3229f..2525075900 100644 --- a/src/object/sp-filter.cpp +++ b/src/object/sp-filter.cpp @@ -324,6 +324,39 @@ Inkscape::XML::Node* SPFilter::write(Inkscape::XML::Document *doc, Inkscape::XML return repr; } +/** + * Sets a suitable filter effects area according to given radius, expansion and object size. + * + * @param radius - Blur radius to account for + * @param expansion - Amount of expansion + * @param expansionX - Amount of X expansion + * @param expansionY - Amount of Y expansion + * @param width - Width of the object being filtered + * @param height - Height of the object being filtered + */ +void SPFilter::set_filter_area( + gdouble radius, double expansion, double expansionX, double expansionY, double width, double height) +{ + double rx = radius * (expansionY != 0 ? (expansion / expansionY) : 1); + double ry = radius * (expansionX != 0 ? (expansion / expansionX) : 1); + + if (width != 0 && height != 0) { + // If not within the default 10% margin (see + // http://www.w3.org/TR/SVG11/filters.html#FilterEffectsRegion), specify margins + // The 2.4 is an empirical coefficient: at that distance the cutoff is practically invisible + // (the opacity at 2.4*radius is about 3e-3) + double xmargin = 2.4 * (rx) / width; + double ymargin = 2.4 * (ry) / height; + + // TODO: set it in UserSpaceOnUse instead? + auto repr = this->getRepr(); + sp_repr_set_svg_double(repr, "x", -xmargin); + sp_repr_set_svg_double(repr, "y", -ymargin); + sp_repr_set_svg_double(repr, "width", 1 + 2 * xmargin); + sp_repr_set_svg_double(repr, "height", 1 + 2 * ymargin); + } +} + /** * Gets called when the filter is (re)attached to another filter. diff --git a/src/object/sp-filter.h b/src/object/sp-filter.h index 08129d1ce3..afb72ea13a 100644 --- a/src/object/sp-filter.h +++ b/src/object/sp-filter.h @@ -58,6 +58,9 @@ public: /// Returns slot number for given image name, even if it's unknown. int set_image_name(char const *name); + void set_filter_area(gdouble radius, double expansion, double expansionX, double expansionY, double width, double height); + + /** Finds image name based on it's slot number. Returns 0 for unknown slot * numbers. */ char const *name_for_image(int const image) const; -- GitLab From 6ccbb4b4d00cfe4b458822f3478378e043c69fcf Mon Sep 17 00:00:00 2001 From: Martin Owens Date: Tue, 20 Oct 2020 14:47:11 -0400 Subject: [PATCH 2/9] First attempt to calculate blur bbox in situe --- src/object/filters/gaussian-blur.cpp | 20 +++++++++++ src/object/filters/gaussian-blur.h | 6 ++-- src/object/filters/sp-filter-primitive.cpp | 9 +++++ src/object/filters/sp-filter-primitive.h | 4 +++ src/object/sp-filter.cpp | 39 +++++++++++++++++++++ src/object/sp-filter.h | 3 +- src/ui/widget/object-composite-settings.cpp | 1 + 7 files changed, 79 insertions(+), 3 deletions(-) diff --git a/src/object/filters/gaussian-blur.cpp b/src/object/filters/gaussian-blur.cpp index afad293ee2..b1f5b6142d 100644 --- a/src/object/filters/gaussian-blur.cpp +++ b/src/object/filters/gaussian-blur.cpp @@ -120,6 +120,26 @@ void SPGaussianBlur::build_renderer(Inkscape::Filters::Filter* filter) { } } +/* Calculate the region taken up by gaussian blur + * + * @param region The original shape's region or previous primitive's region output. + */ +Geom::OptRect SPGaussianBlur::calculate_region(Geom::OptRect region) +{ + // If not within the default 10% margin (see + // http://www.w3.org/TR/SVG11/filters.html#FilterEffectsRegion), specify margins + // The 2.4 is an empirical coefficient: at that distance the cutoff is practically invisible + // (the opacity at 2.4*radius is about 3e-3) + gfloat num = this->stdDeviation.getNumber(); + double marginx = 2.4 * (num / region->width()); + double marginy = 2.4 * (num / region->height()); + double left = region->left() - marginx; + double top = region->top() - marginy; + double right = region->right() + marginx; + double bottom = region->bottom() + marginx; + return *(new Geom::OptRect(left, top, right, bottom)); +} + /* Local Variables: mode:c++ diff --git a/src/object/filters/gaussian-blur.h b/src/object/filters/gaussian-blur.h index 5def69c036..2e7f7c2c81 100644 --- a/src/object/filters/gaussian-blur.h +++ b/src/object/filters/gaussian-blur.h @@ -24,8 +24,10 @@ public: SPGaussianBlur(); ~SPGaussianBlur() override; - /** stdDeviation attribute */ - NumberOptNumber stdDeviation; + /** stdDeviation attribute */ + NumberOptNumber stdDeviation; + + Geom::OptRect calculate_region(Geom::OptRect region) override; protected: void build(SPDocument* doc, Inkscape::XML::Node* repr) override; diff --git a/src/object/filters/sp-filter-primitive.cpp b/src/object/filters/sp-filter-primitive.cpp index 6c0a787f42..12092172c0 100644 --- a/src/object/filters/sp-filter-primitive.cpp +++ b/src/object/filters/sp-filter-primitive.cpp @@ -259,6 +259,15 @@ void SPFilterPrimitive::renderer_common(Inkscape::Filters::FilterPrimitive *nr_p nr_prim->setStyle( this->style ); } +/* Calculate the region taken up by this filter, given the previous region. + * + * @param current_region The original shape's region or previous primitive's + * calcualte_region output. + */ +Geom::OptRect SPFilterPrimitive::calculate_region(Geom::OptRect region) +{ + return region; // No change. +} /* diff --git a/src/object/filters/sp-filter-primitive.h b/src/object/filters/sp-filter-primitive.h index ed74c1fd20..5d147e7e9b 100644 --- a/src/object/filters/sp-filter-primitive.h +++ b/src/object/filters/sp-filter-primitive.h @@ -15,6 +15,7 @@ * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ +#include "2geom/rect.h" #include "../sp-object.h" #include "../sp-dimensions.h" @@ -47,6 +48,9 @@ protected: public: virtual void build_renderer(Inkscape::Filters::Filter* filter) = 0; + /* Calculate the filter's effect on the region */ + virtual Geom::OptRect calculate_region(Geom::OptRect region); + /* Common initialization for filter primitives */ void renderer_common(Inkscape::Filters::FilterPrimitive *nr_prim); diff --git a/src/object/sp-filter.cpp b/src/object/sp-filter.cpp index 2525075900..73cf7f2fd5 100644 --- a/src/object/sp-filter.cpp +++ b/src/object/sp-filter.cpp @@ -348,6 +348,9 @@ void SPFilter::set_filter_area( double xmargin = 2.4 * (rx) / width; double ymargin = 2.4 * (ry) / height; + g_warning("SetFilterArea:\n * radius:%f [%f,%f]\n * expans:%f [%f,%f]\n * size:%f,%f\n * magin:[%f,%f][%f,%f]", + radius, rx, ry, expansion, expansionX, expansionY, width, height, -xmargin, -ymargin, 1 + 2 * xmargin, 1 + 2 * ymargin); + // TODO: set it in UserSpaceOnUse instead? auto repr = this->getRepr(); sp_repr_set_svg_double(repr, "x", -xmargin); @@ -357,6 +360,42 @@ void SPFilter::set_filter_area( } } +/* Update the filter region based on the object's bounding box + */ +void SPFilter::update_filter_region(SPItem *item) +{ + Geom::OptRect const inbox = item->desktopGeometricBounds(); + + double width; double height; + if (inbox) { + width = inbox->dimensions()[Geom::X]; + height = inbox->dimensions()[Geom::Y]; + } else { + width = height = 0; + } + + Geom::OptRect outbox = inbox; + for(auto& primitive_obj: this->children) { + if (SP_IS_FILTER_PRIMITIVE(&primitive_obj)) { + SPFilterPrimitive *primitive = SP_FILTER_PRIMITIVE(&primitive_obj); + // Update the region with the primitive's options + outbox = primitive->calculate_region(outbox); + } + } + + double x = outbox->left() / inbox->width(); + double y = outbox->top() / inbox->height(); + double n_width = outbox->width() / inbox->width(); + double n_height = outbox->height() / inbox->height(); + + g_warning("Update_filter_region: \ninbox: [%f,%f][%f,%f]\noutbox: [%f,%f][%f,%f]\nguess: %f,%f [%f,%f]\n", + inbox->left(), inbox->top(), inbox->right(), inbox->bottom(), + outbox->left(), outbox->top(), outbox->right(), outbox->bottom(), + x, y, n_width, n_height); + // Set the filter region into this filter object + // XXX this->set_filter_region(bbox); +} + /** * Gets called when the filter is (re)attached to another filter. diff --git a/src/object/sp-filter.h b/src/object/sp-filter.h index afb72ea13a..821005e596 100644 --- a/src/object/sp-filter.h +++ b/src/object/sp-filter.h @@ -19,6 +19,7 @@ #include "number-opt-number.h" #include "sp-dimensions.h" #include "sp-object.h" +#include "sp-item.h" #include "sp-filter-units.h" #include "svg/svg-length.h" @@ -59,7 +60,7 @@ public: int set_image_name(char const *name); void set_filter_area(gdouble radius, double expansion, double expansionX, double expansionY, double width, double height); - + void update_filter_region(SPItem *object); /** Finds image name based on it's slot number. Returns 0 for unknown slot * numbers. */ diff --git a/src/ui/widget/object-composite-settings.cpp b/src/ui/widget/object-composite-settings.cpp index 9612060544..f6c20441ce 100644 --- a/src/ui/widget/object-composite-settings.cpp +++ b/src/ui/widget/object-composite-settings.cpp @@ -129,6 +129,7 @@ ObjectCompositeSettings::_blendBlurValueChanged() remove_filter(item, false); } else if (radius != 0) { SPFilter *filter = modify_filter_gaussian_blur_from_item(document, item, radius); + filter->update_filter_region(item); sp_style_set_property_url(item, "filter", filter, false); } if (change_blend) { //we do blend so we need update display style -- GitLab From 771c524147658e632cfc550662c0e0ac83caa9a0 Mon Sep 17 00:00:00 2001 From: Martin Owens Date: Wed, 21 Oct 2020 06:05:37 -0400 Subject: [PATCH 3/9] Remove more old code and get closer to correct calculation --- src/filter-chemistry.cpp | 32 +--------- src/filter-chemistry.h | 2 +- src/object/filters/gaussian-blur.cpp | 12 +--- src/object/sp-filter.cpp | 92 ++++++++++++---------------- src/object/sp-filter.h | 2 +- src/ui/dialog/clonetiler.cpp | 10 ++- 6 files changed, 50 insertions(+), 100 deletions(-) diff --git a/src/filter-chemistry.cpp b/src/filter-chemistry.cpp index 0d08e36a87..53ad523482 100644 --- a/src/filter-chemistry.cpp +++ b/src/filter-chemistry.cpp @@ -162,7 +162,7 @@ filter_add_primitive(SPFilter *filter, const Inkscape::Filters::FilterPrimitiveT * Creates a filter with blur primitive of specified radius for an item with the given matrix expansion, width and height */ SPFilter * -new_filter_gaussian_blur (SPDocument *document, gdouble radius, double expansion, double expansionX, double expansionY, double width, double height) +new_filter_gaussian_blur (SPDocument *document, gdouble radius, double expansion) { g_return_val_if_fail(document != nullptr, NULL); @@ -214,8 +214,6 @@ new_filter_gaussian_blur (SPDocument *document, gdouble radius, double expansion g_assert(b != nullptr); g_assert(SP_IS_GAUSSIANBLUR(b)); - f->set_filter_area(radius, expansion, expansionX, expansionY, width, height); - return f; } @@ -225,8 +223,7 @@ new_filter_gaussian_blur (SPDocument *document, gdouble radius, double expansion * an item with the given matrix expansion, width and height */ static SPFilter * -new_filter_blend_gaussian_blur (SPDocument *document, const char *blendmode, gdouble radius, double expansion, - double expansionX, double expansionY, double width, double height) +new_filter_blend_gaussian_blur (SPDocument *document, const char *blendmode, gdouble radius, double expansion) { g_return_val_if_fail(document != nullptr, NULL); @@ -256,7 +253,6 @@ new_filter_blend_gaussian_blur (SPDocument *document, const char *blendmode, gdo SPFilter *f = SP_FILTER( document->getObjectByRepr(repr) ); // Gaussian blur primitive if(radius != 0) { - f->set_filter_area(radius, expansion, expansionX, expansionY, width, height); //create feGaussianBlur node Inkscape::XML::Node *b_repr; b_repr = xml_doc->createElement("svg:feGaussianBlur"); @@ -313,20 +309,7 @@ new_filter_blend_gaussian_blur (SPDocument *document, const char *blendmode, gdo SPFilter * new_filter_simple_from_item (SPDocument *document, SPItem *item, const char *mode, gdouble radius) { - Geom::OptRect const r = item->desktopGeometricBounds(); - - double width; - double height; - if (r) { - width = r->dimensions()[Geom::X]; - height= r->dimensions()[Geom::Y]; - } else { - width = height = 0; - } - - Geom::Affine i2dt (item->i2dt_affine () ); - - return (new_filter_blend_gaussian_blur (document, mode, radius, i2dt.descrim(), i2dt.expansionX(), i2dt.expansionY(), width, height)); + return new_filter_blend_gaussian_blur(document, mode, radius, item->i2dt_affine().descrim()); } /** @@ -372,18 +355,9 @@ SPFilter *modify_filter_gaussian_blur_from_item(SPDocument *document, SPItem *it // Get the object size Geom::OptRect const r = item->desktopGeometricBounds(); - double width; - double height; - if (r) { - width = r->dimensions()[Geom::X]; - height= r->dimensions()[Geom::Y]; - } else { - width = height = 0; - } // Set the filter effects area SPFilter *f = item->style->getFilter(); - f->set_filter_area(radius, expansion, i2d.expansionX(), i2d.expansionY(), width, height); Inkscape::XML::Node *repr = f->getRepr(); // Search for gaussian blur primitives. If found, set the stdDeviation diff --git a/src/filter-chemistry.h b/src/filter-chemistry.h index de25cb6400..7e70c0ddcf 100644 --- a/src/filter-chemistry.h +++ b/src/filter-chemistry.h @@ -26,7 +26,7 @@ class SPObject; SPFilterPrimitive *filter_add_primitive(SPFilter *filter, Inkscape::Filters::FilterPrimitiveType); SPFilter *new_filter (SPDocument *document); -SPFilter *new_filter_gaussian_blur (SPDocument *document, double stdDeviation, double expansion, double expansionX, double expansionY, double width, double height); +SPFilter *new_filter_gaussian_blur (SPDocument *document, double stdDeviation, double expansion); SPFilter *new_filter_simple_from_item (SPDocument *document, SPItem *item, const char *mode, double stdDeviation); SPFilter *modify_filter_gaussian_blur_from_item (SPDocument *document, SPItem *item, double stdDeviation); void remove_filter (SPObject *item, bool recursive); diff --git a/src/object/filters/gaussian-blur.cpp b/src/object/filters/gaussian-blur.cpp index b1f5b6142d..aa66ee3468 100644 --- a/src/object/filters/gaussian-blur.cpp +++ b/src/object/filters/gaussian-blur.cpp @@ -129,15 +129,9 @@ Geom::OptRect SPGaussianBlur::calculate_region(Geom::OptRect region) // If not within the default 10% margin (see // http://www.w3.org/TR/SVG11/filters.html#FilterEffectsRegion), specify margins // The 2.4 is an empirical coefficient: at that distance the cutoff is practically invisible - // (the opacity at 2.4*radius is about 3e-3) - gfloat num = this->stdDeviation.getNumber(); - double marginx = 2.4 * (num / region->width()); - double marginy = 2.4 * (num / region->height()); - double left = region->left() - marginx; - double top = region->top() - marginy; - double right = region->right() + marginx; - double bottom = region->bottom() + marginx; - return *(new Geom::OptRect(left, top, right, bottom)); + // (the opacity at 2.4 * radius is about 3e-3) + region->expandBy(2.4 * this->stdDeviation.getNumber()); + return region; } /* diff --git a/src/object/sp-filter.cpp b/src/object/sp-filter.cpp index 73cf7f2fd5..e04c0a185a 100644 --- a/src/object/sp-filter.cpp +++ b/src/object/sp-filter.cpp @@ -324,78 +324,62 @@ Inkscape::XML::Node* SPFilter::write(Inkscape::XML::Document *doc, Inkscape::XML return repr; } -/** - * Sets a suitable filter effects area according to given radius, expansion and object size. - * - * @param radius - Blur radius to account for - * @param expansion - Amount of expansion - * @param expansionX - Amount of X expansion - * @param expansionY - Amount of Y expansion - * @param width - Width of the object being filtered - * @param height - Height of the object being filtered - */ -void SPFilter::set_filter_area( - gdouble radius, double expansion, double expansionX, double expansionY, double width, double height) -{ - double rx = radius * (expansionY != 0 ? (expansion / expansionY) : 1); - double ry = radius * (expansionX != 0 ? (expansion / expansionX) : 1); - - if (width != 0 && height != 0) { - // If not within the default 10% margin (see - // http://www.w3.org/TR/SVG11/filters.html#FilterEffectsRegion), specify margins - // The 2.4 is an empirical coefficient: at that distance the cutoff is practically invisible - // (the opacity at 2.4*radius is about 3e-3) - double xmargin = 2.4 * (rx) / width; - double ymargin = 2.4 * (ry) / height; - - g_warning("SetFilterArea:\n * radius:%f [%f,%f]\n * expans:%f [%f,%f]\n * size:%f,%f\n * magin:[%f,%f][%f,%f]", - radius, rx, ry, expansion, expansionX, expansionY, width, height, -xmargin, -ymargin, 1 + 2 * xmargin, 1 + 2 * ymargin); - - // TODO: set it in UserSpaceOnUse instead? - auto repr = this->getRepr(); - sp_repr_set_svg_double(repr, "x", -xmargin); - sp_repr_set_svg_double(repr, "y", -ymargin); - sp_repr_set_svg_double(repr, "width", 1 + 2 * xmargin); - sp_repr_set_svg_double(repr, "height", 1 + 2 * ymargin); - } -} - /* Update the filter region based on the object's bounding box */ void SPFilter::update_filter_region(SPItem *item) { - Geom::OptRect const inbox = item->desktopGeometricBounds(); - - double width; double height; - if (inbox) { - width = inbox->dimensions()[Geom::X]; - height = inbox->dimensions()[Geom::Y]; - } else { - width = height = 0; - } - + Geom::OptRect inbox = item->desktopGeometricBounds(); + if (!inbox) + return; // No adjustment for dead box + + double expansion = item->i2dt_affine().descrim(); + // Shrink bounding box by item to desktop affine (surely there's a better way?) + inbox->setLeft(inbox->left() / expansion); + inbox->setTop(inbox->top() / expansion); + inbox->setRight(inbox->right() / expansion); + inbox->setBottom(inbox->bottom() / expansion); Geom::OptRect outbox = inbox; + for(auto& primitive_obj: this->children) { - if (SP_IS_FILTER_PRIMITIVE(&primitive_obj)) { - SPFilterPrimitive *primitive = SP_FILTER_PRIMITIVE(&primitive_obj); + auto primitive = dynamic_cast(&primitive_obj); + if (primitive) { // Update the region with the primitive's options outbox = primitive->calculate_region(outbox); } } - double x = outbox->left() / inbox->width(); - double y = outbox->top() / inbox->height(); - double n_width = outbox->width() / inbox->width(); - double n_height = outbox->height() / inbox->height(); + // TODO: Combine the rect with the original rect for offset filters. + double x1 = (outbox->left() - inbox->left()) / inbox->width(); + double y1 = (outbox->top() - inbox->top()) / inbox->height(); + double x2 = (outbox->right() - inbox->right()) / inbox->width(); + double y2 = (outbox->bottom() - inbox->bottom()) / inbox->height(); + double n_width = x2 - x1 + 1; + double n_height = y2 - x1 + 1; g_warning("Update_filter_region: \ninbox: [%f,%f][%f,%f]\noutbox: [%f,%f][%f,%f]\nguess: %f,%f [%f,%f]\n", inbox->left(), inbox->top(), inbox->right(), inbox->bottom(), outbox->left(), outbox->top(), outbox->right(), outbox->bottom(), - x, y, n_width, n_height); + x1, y1, n_width, n_height); + // Set the filter region into this filter object - // XXX this->set_filter_region(bbox); + this->set_filter_region(x1, y1, n_width, n_height); } +/* Set the filter region attributes from a bounding box + * + */ +void SPFilter::set_filter_region(double x, double y, double width, double height) +{ + if (width != 0 && height != 0) + { + // TODO: set it in UserSpaceOnUse instead? + auto repr = this->getRepr(); + sp_repr_set_svg_double(repr, "x", x); + sp_repr_set_svg_double(repr, "y", y); + sp_repr_set_svg_double(repr, "width", width); + sp_repr_set_svg_double(repr, "height", height); + } +} /** * Gets called when the filter is (re)attached to another filter. diff --git a/src/object/sp-filter.h b/src/object/sp-filter.h index 821005e596..d188ed77fe 100644 --- a/src/object/sp-filter.h +++ b/src/object/sp-filter.h @@ -59,8 +59,8 @@ public: /// Returns slot number for given image name, even if it's unknown. int set_image_name(char const *name); - void set_filter_area(gdouble radius, double expansion, double expansionX, double expansionY, double width, double height); void update_filter_region(SPItem *object); + void set_filter_region(double x, double y, double width, double height); /** Finds image name based on it's slot number. Returns 0 for unknown slot * numbers. */ diff --git a/src/ui/dialog/clonetiler.cpp b/src/ui/dialog/clonetiler.cpp index d22f88978f..7802c37449 100644 --- a/src/ui/dialog/clonetiler.cpp +++ b/src/ui/dialog/clonetiler.cpp @@ -2477,15 +2477,13 @@ void CloneTiler::apply() if (blur > 0.0) { SPObject *clone_object = desktop->getDocument()->getObjectByRepr(clone); - double perimeter = perimeter_original * t.descrim(); - double radius = blur * perimeter; + SPItem *item = dynamic_cast(clone_object); + double radius = blur * perimeter_original * t.descrim(); // this is necessary for all newly added clones to have correct bboxes, // otherwise filters won't work: desktop->getDocument()->ensureUpToDate(); - // it's hard to figure out exact width/height of the tile without having an object - // that we can take bbox of; however here we only need a lower bound so that blur - // margins are not too small, and the perimeter should work - SPFilter *constructed = new_filter_gaussian_blur(desktop->getDocument(), radius, t.descrim(), t.expansionX(), t.expansionY(), perimeter, perimeter); + SPFilter *constructed = new_filter_gaussian_blur(desktop->getDocument(), radius, t.descrim()); + constructed->update_filter_region(item); sp_style_set_property_url (clone_object, "filter", constructed, false); } -- GitLab From 79f650f9e5465cdddf4df6aa39317fa444e5b1ba Mon Sep 17 00:00:00 2001 From: Martin Owens Date: Wed, 21 Oct 2020 07:58:16 -0400 Subject: [PATCH 4/9] Fix blur and use Rect instead of OptRect for transformations --- src/object/filters/gaussian-blur.cpp | 4 +-- src/object/filters/gaussian-blur.h | 2 +- src/object/filters/sp-filter-primitive.cpp | 2 +- src/object/filters/sp-filter-primitive.h | 2 +- src/object/sp-filter.cpp | 35 ++++++++-------------- 5 files changed, 18 insertions(+), 27 deletions(-) diff --git a/src/object/filters/gaussian-blur.cpp b/src/object/filters/gaussian-blur.cpp index aa66ee3468..79c66d4020 100644 --- a/src/object/filters/gaussian-blur.cpp +++ b/src/object/filters/gaussian-blur.cpp @@ -124,13 +124,13 @@ void SPGaussianBlur::build_renderer(Inkscape::Filters::Filter* filter) { * * @param region The original shape's region or previous primitive's region output. */ -Geom::OptRect SPGaussianBlur::calculate_region(Geom::OptRect region) +Geom::Rect SPGaussianBlur::calculate_region(Geom::Rect region) { // If not within the default 10% margin (see // http://www.w3.org/TR/SVG11/filters.html#FilterEffectsRegion), specify margins // The 2.4 is an empirical coefficient: at that distance the cutoff is practically invisible // (the opacity at 2.4 * radius is about 3e-3) - region->expandBy(2.4 * this->stdDeviation.getNumber()); + region.expandBy(2.4 * this->stdDeviation.getNumber()); return region; } diff --git a/src/object/filters/gaussian-blur.h b/src/object/filters/gaussian-blur.h index 2e7f7c2c81..04ad552084 100644 --- a/src/object/filters/gaussian-blur.h +++ b/src/object/filters/gaussian-blur.h @@ -27,7 +27,7 @@ public: /** stdDeviation attribute */ NumberOptNumber stdDeviation; - Geom::OptRect calculate_region(Geom::OptRect region) override; + Geom::Rect calculate_region(Geom::Rect region) override; protected: void build(SPDocument* doc, Inkscape::XML::Node* repr) override; diff --git a/src/object/filters/sp-filter-primitive.cpp b/src/object/filters/sp-filter-primitive.cpp index 12092172c0..d750cce912 100644 --- a/src/object/filters/sp-filter-primitive.cpp +++ b/src/object/filters/sp-filter-primitive.cpp @@ -264,7 +264,7 @@ void SPFilterPrimitive::renderer_common(Inkscape::Filters::FilterPrimitive *nr_p * @param current_region The original shape's region or previous primitive's * calcualte_region output. */ -Geom::OptRect SPFilterPrimitive::calculate_region(Geom::OptRect region) +Geom::Rect SPFilterPrimitive::calculate_region(Geom::Rect region) { return region; // No change. } diff --git a/src/object/filters/sp-filter-primitive.h b/src/object/filters/sp-filter-primitive.h index 5d147e7e9b..9bc375154f 100644 --- a/src/object/filters/sp-filter-primitive.h +++ b/src/object/filters/sp-filter-primitive.h @@ -49,7 +49,7 @@ public: virtual void build_renderer(Inkscape::Filters::Filter* filter) = 0; /* Calculate the filter's effect on the region */ - virtual Geom::OptRect calculate_region(Geom::OptRect region); + virtual Geom::Rect calculate_region(Geom::Rect region); /* Common initialization for filter primitives */ void renderer_common(Inkscape::Filters::FilterPrimitive *nr_prim); diff --git a/src/object/sp-filter.cpp b/src/object/sp-filter.cpp index e04c0a185a..c26e51c7fc 100644 --- a/src/object/sp-filter.cpp +++ b/src/object/sp-filter.cpp @@ -22,6 +22,7 @@ #include #include +#include <2geom/transforms.h> #include "bad-uri-exception.h" #include "attributes.h" @@ -328,18 +329,15 @@ Inkscape::XML::Node* SPFilter::write(Inkscape::XML::Document *doc, Inkscape::XML */ void SPFilter::update_filter_region(SPItem *item) { - Geom::OptRect inbox = item->desktopGeometricBounds(); - if (!inbox) + Geom::OptRect bbox = item->desktopGeometricBounds(); + if (!bbox) return; // No adjustment for dead box - double expansion = item->i2dt_affine().descrim(); - // Shrink bounding box by item to desktop affine (surely there's a better way?) - inbox->setLeft(inbox->left() / expansion); - inbox->setTop(inbox->top() / expansion); - inbox->setRight(inbox->right() / expansion); - inbox->setBottom(inbox->bottom() / expansion); - Geom::OptRect outbox = inbox; + // Turn bbox into real box and shrink it to units used in filters. + Geom::Rect inbox = *(new Geom::Rect(bbox->corner(0), bbox->corner(2))); + inbox *= item->i2dt_affine().inverse(); + Geom::Rect outbox = inbox; for(auto& primitive_obj: this->children) { auto primitive = dynamic_cast(&primitive_obj); if (primitive) { @@ -348,21 +346,14 @@ void SPFilter::update_filter_region(SPItem *item) } } - // TODO: Combine the rect with the original rect for offset filters. - double x1 = (outbox->left() - inbox->left()) / inbox->width(); - double y1 = (outbox->top() - inbox->top()) / inbox->height(); - double x2 = (outbox->right() - inbox->right()) / inbox->width(); - double y2 = (outbox->bottom() - inbox->bottom()) / inbox->height(); - double n_width = x2 - x1 + 1; - double n_height = y2 - x1 + 1; - - g_warning("Update_filter_region: \ninbox: [%f,%f][%f,%f]\noutbox: [%f,%f][%f,%f]\nguess: %f,%f [%f,%f]\n", - inbox->left(), inbox->top(), inbox->right(), inbox->bottom(), - outbox->left(), outbox->top(), outbox->right(), outbox->bottom(), - x1, y1, n_width, n_height); + // Include the original bounding-box in the result + outbox.unionWith(inbox); + // Scale outbox to width/height scale of input. + outbox *= Geom::Translate(-inbox.left(), -inbox.top()); + outbox *= Geom::Scale(1/inbox.width(), 1/inbox.height()); // Set the filter region into this filter object - this->set_filter_region(x1, y1, n_width, n_height); + this->set_filter_region(outbox.left(), outbox.top(), outbox.width(), outbox.height()); } /* Set the filter region attributes from a bounding box -- GitLab From cec1e22f7917ceb81256e7f2ad8f3e7a58df397b Mon Sep 17 00:00:00 2001 From: Martin Owens Date: Wed, 21 Oct 2020 11:31:11 -0400 Subject: [PATCH 5/9] Add offset and allow checkbox to ignore auto updating --- src/attributes.cpp | 1 + src/attributes.h | 1 + src/object/filters/gaussian-blur.cpp | 6 +++++- src/object/filters/offset.cpp | 12 ++++++++++++ src/object/filters/offset.h | 4 +++- src/object/sp-filter.cpp | 25 ++++++++++++++++++++++++- src/object/sp-filter.h | 4 ++++ src/ui/dialog/filter-effects-dialog.cpp | 1 + 8 files changed, 51 insertions(+), 3 deletions(-) diff --git a/src/attributes.cpp b/src/attributes.cpp index 531bc16edb..021205c232 100644 --- a/src/attributes.cpp +++ b/src/attributes.cpp @@ -532,6 +532,7 @@ static SPStyleProp const props[] = { {SPAttr::FLOOD_COLOR, "flood-color"}, {SPAttr::FLOOD_OPACITY, "flood-opacity"}, {SPAttr::LIGHTING_COLOR, "lighting-color"}, + {SPAttr::AUTO_REGION, "inkscape:auto-region"}, /* Gradient */ {SPAttr::STOP_COLOR, "stop-color"}, diff --git a/src/attributes.h b/src/attributes.h index 95553b4ae5..2dc50d4c30 100644 --- a/src/attributes.h +++ b/src/attributes.h @@ -534,6 +534,7 @@ enum class SPAttr { FLOOD_COLOR, FLOOD_OPACITY, LIGHTING_COLOR, + AUTO_REGION, /* Gradient */ STOP_COLOR, diff --git a/src/object/filters/gaussian-blur.cpp b/src/object/filters/gaussian-blur.cpp index 79c66d4020..c8460d0144 100644 --- a/src/object/filters/gaussian-blur.cpp +++ b/src/object/filters/gaussian-blur.cpp @@ -126,11 +126,15 @@ void SPGaussianBlur::build_renderer(Inkscape::Filters::Filter* filter) { */ Geom::Rect SPGaussianBlur::calculate_region(Geom::Rect region) { + double x = this->stdDeviation.getNumber(); + double y = this->stdDeviation.getOptNumber(); + if (y == -1.0) + y = x; // If not within the default 10% margin (see // http://www.w3.org/TR/SVG11/filters.html#FilterEffectsRegion), specify margins // The 2.4 is an empirical coefficient: at that distance the cutoff is practically invisible // (the opacity at 2.4 * radius is about 3e-3) - region.expandBy(2.4 * this->stdDeviation.getNumber()); + region.expandBy(2.4 * x, 2.4 * y); return region; } diff --git a/src/object/filters/offset.cpp b/src/object/filters/offset.cpp index 51e81d8dbf..5610c4b669 100644 --- a/src/object/filters/offset.cpp +++ b/src/object/filters/offset.cpp @@ -14,6 +14,8 @@ * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ +#include <2geom/transforms.h> + #include "offset.h" #include "attributes.h" @@ -124,6 +126,16 @@ void SPFeOffset::build_renderer(Inkscape::Filters::Filter* filter) { nr_offset->set_dy(this->dy); } +/* Calculate the region taken up by an offset + * + * @param region The original shape's region or previous primitive's region output. + */ +Geom::Rect SPFeOffset::calculate_region(Geom::Rect region) +{ + region *= Geom::Translate(this->dx, this->dy); + return region; +} + /* Local Variables: diff --git a/src/object/filters/offset.h b/src/object/filters/offset.h index f847da16a9..a717b452f8 100644 --- a/src/object/filters/offset.h +++ b/src/object/filters/offset.h @@ -23,7 +23,9 @@ public: SPFeOffset(); ~SPFeOffset() override; - double dx, dy; + double dx, dy; + + Geom::Rect calculate_region(Geom::Rect region) override; protected: void build(SPDocument* doc, Inkscape::XML::Node* repr) override; diff --git a/src/object/sp-filter.cpp b/src/object/sp-filter.cpp index c26e51c7fc..9973cc0601 100644 --- a/src/object/sp-filter.cpp +++ b/src/object/sp-filter.cpp @@ -50,6 +50,7 @@ SPFilter::SPFilter() this->y = 0; this->width = 0; this->height = 0; + this->auto_region = true; this->_image_name->clear(); } @@ -71,6 +72,7 @@ void SPFilter::build(SPDocument *document, Inkscape::XML::Node *repr) { this->readAttr(SPAttr::Y); this->readAttr(SPAttr::WIDTH); this->readAttr(SPAttr::HEIGHT); + this->readAttr(SPAttr::AUTO_REGION); this->readAttr(SPAttr::FILTERRES); this->readAttr(SPAttr::XLINK_HREF); this->_refcount = 0; @@ -162,6 +164,10 @@ void SPFilter::set(SPAttr key, gchar const *value) { this->height.readOrUnset(value); this->requestModified(SP_OBJECT_MODIFIED_FLAG); break; + case SPAttr::AUTO_REGION: + this->auto_region = (!value || strcmp(value, "false")); + this->requestModified(SP_OBJECT_MODIFIED_FLAG); + break; case SPAttr::FILTERRES: this->filterRes.set(value); this->requestModified(SP_OBJECT_MODIFIED_FLAG); @@ -194,6 +200,10 @@ guint SPFilter::getRefCount() { return _refcount; } +void SPFilter::modified(guint flags) { + update_filter_all_regions(); +} + /** * Receives update notifications. */ @@ -325,12 +335,25 @@ Inkscape::XML::Node* SPFilter::write(Inkscape::XML::Document *doc, Inkscape::XML return repr; } +/* Update the filter's region based on it's detectable href links + * + */ +void SPFilter::update_filter_all_regions() +{ + for (auto & obj : this->hrefList) { + SPItem *item = dynamic_cast(obj); + update_filter_region(item); + } +} + /* Update the filter region based on the object's bounding box + * + * @param item - The item who's coords are used as the basis for the area. */ void SPFilter::update_filter_region(SPItem *item) { Geom::OptRect bbox = item->desktopGeometricBounds(); - if (!bbox) + if (!bbox || !this->auto_region) return; // No adjustment for dead box // Turn bbox into real box and shrink it to units used in filters. diff --git a/src/object/sp-filter.h b/src/object/sp-filter.h index d188ed77fe..e4e5f76a99 100644 --- a/src/object/sp-filter.h +++ b/src/object/sp-filter.h @@ -59,6 +59,7 @@ public: /// Returns slot number for given image name, even if it's unknown. int set_image_name(char const *name); + void update_filter_all_regions(); void update_filter_region(SPItem *object); void set_filter_region(double x, double y, double width, double height); @@ -75,6 +76,8 @@ public: unsigned int primitiveUnits_set : 1; NumberOptNumber filterRes; SPFilterReference *href; + bool auto_region; + sigc::connection modified_connection; guint getRefCount(); @@ -94,6 +97,7 @@ protected: void set(SPAttr key, const char* value) override; + void modified(unsigned int flags) override; void update(SPCtx* ctx, unsigned int flags) override; Inkscape::XML::Node* write(Inkscape::XML::Document* doc, Inkscape::XML::Node* repr, unsigned int flags) override; diff --git a/src/ui/dialog/filter-effects-dialog.cpp b/src/ui/dialog/filter-effects-dialog.cpp index d6592bc6a3..5ad4520100 100644 --- a/src/ui/dialog/filter-effects-dialog.cpp +++ b/src/ui/dialog/filter-effects-dialog.cpp @@ -2772,6 +2772,7 @@ void FilterEffectsDialog::init_settings_widgets() _settings_initialized = true; _filter_general_settings->type(0); + _filter_general_settings->add_checkbutton(true, SPAttr::AUTO_REGION, _("Automatic Region"), "true", "false", _("If unset the coordinates and dimensions won't be automatically updated.")); _filter_general_settings->add_multispinbutton(/*default x:*/ (double) -0.1, /*default y:*/ (double) -0.1, SPAttr::X, SPAttr::Y, _("Coordinates:"), -100, 100, 0.01, 0.1, 2, _("X coordinate of the left corners of filter effects region"), _("Y coordinate of the upper corners of filter effects region")); _filter_general_settings->add_multispinbutton(/*default width:*/ (double) 1.2, /*default height:*/ (double) 1.2, SPAttr::WIDTH, SPAttr::HEIGHT, _("Dimensions:"), 0, 1000, 0.01, 0.1, 2, _("Width of filter effects region"), _("Height of filter effects region")); -- GitLab From 6df33a577e9ab85398a2e1d4ec8d9c15978cc44e Mon Sep 17 00:00:00 2001 From: Martin Owens Date: Wed, 21 Oct 2020 17:30:50 -0400 Subject: [PATCH 6/9] Simplify OptRect to Rect conversion. --- src/object/sp-filter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/object/sp-filter.cpp b/src/object/sp-filter.cpp index 9973cc0601..8b398cc198 100644 --- a/src/object/sp-filter.cpp +++ b/src/object/sp-filter.cpp @@ -357,7 +357,7 @@ void SPFilter::update_filter_region(SPItem *item) return; // No adjustment for dead box // Turn bbox into real box and shrink it to units used in filters. - Geom::Rect inbox = *(new Geom::Rect(bbox->corner(0), bbox->corner(2))); + Geom::Rect inbox = *bbox; inbox *= item->i2dt_affine().inverse(); Geom::Rect outbox = inbox; -- GitLab From 50ac6ba21d659f7aa960f523d043b8a6eb058b5c Mon Sep 17 00:00:00 2001 From: Martin Owens Date: Thu, 22 Oct 2020 10:48:02 -0400 Subject: [PATCH 7/9] Fix tests --- testfiles/src/attributes-test.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/testfiles/src/attributes-test.cpp b/testfiles/src/attributes-test.cpp index 759c057923..f3e44aa3d7 100644 --- a/testfiles/src/attributes-test.cpp +++ b/testfiles/src/attributes-test.cpp @@ -194,6 +194,7 @@ std::vector getKnownAttrs() AttributeInfo("lengthAdjust", true), AttributeInfo("letter-spacing", true), AttributeInfo("lighting-color", true), + AttributeInfo("inkscape:auto-region", true), AttributeInfo("limitingConeAngle", true), AttributeInfo("local", true), AttributeInfo("marker-end", true), -- GitLab From a31682b2e7ec34a2ace98f579204f5c5f7ab7971 Mon Sep 17 00:00:00 2001 From: Martin Owens Date: Wed, 28 Oct 2020 07:58:00 -0400 Subject: [PATCH 8/9] Fix pdf test and doxegen comments --- src/object/filters/offset.cpp | 3 ++- src/object/sp-filter.cpp | 13 +++++++------ .../testcases/export-dpi_expected.pdf | Bin 2177 -> 2193 bytes 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/object/filters/offset.cpp b/src/object/filters/offset.cpp index 5610c4b669..716bea40e4 100644 --- a/src/object/filters/offset.cpp +++ b/src/object/filters/offset.cpp @@ -126,7 +126,8 @@ void SPFeOffset::build_renderer(Inkscape::Filters::Filter* filter) { nr_offset->set_dy(this->dy); } -/* Calculate the region taken up by an offset +/** + * Calculate the region taken up by an offset * * @param region The original shape's region or previous primitive's region output. */ diff --git a/src/object/sp-filter.cpp b/src/object/sp-filter.cpp index 8b398cc198..2930bee4f2 100644 --- a/src/object/sp-filter.cpp +++ b/src/object/sp-filter.cpp @@ -335,8 +335,8 @@ Inkscape::XML::Node* SPFilter::write(Inkscape::XML::Document *doc, Inkscape::XML return repr; } -/* Update the filter's region based on it's detectable href links - * +/** + * Update the filter's region based on it's detectable href links */ void SPFilter::update_filter_all_regions() { @@ -346,8 +346,9 @@ void SPFilter::update_filter_all_regions() } } -/* Update the filter region based on the object's bounding box - * +/** + * Update the filter region based on the object's bounding box + * * @param item - The item who's coords are used as the basis for the area. */ void SPFilter::update_filter_region(SPItem *item) @@ -379,8 +380,8 @@ void SPFilter::update_filter_region(SPItem *item) this->set_filter_region(outbox.left(), outbox.top(), outbox.width(), outbox.height()); } -/* Set the filter region attributes from a bounding box - * +/** + * Set the filter region attributes from a bounding box */ void SPFilter::set_filter_region(double x, double y, double width, double height) { diff --git a/testfiles/cli_tests/testcases/export-dpi_expected.pdf b/testfiles/cli_tests/testcases/export-dpi_expected.pdf index 9a2bb9fe0003a8fd9df76582146af4e20e16e54f..82c6045d3f52503f0ed5044bbedbb0b68d762a16 100644 GIT binary patch delta 782 zcmZn^oG3WKuil!k*?{M6Em!;k#l92y?^$P9DTlmp(tdI2@%1t$mx-Uw{FXT0GVOft zk5gjHMa>cpRd`!+?9{)}D>hw!!=37wqhgyp@5u(RcrM=D_Dkh!W>8_$3aOBgIHTqI zx7F4!y#DjDnmn1WqGS_9%>revDEQO&ExBG zx%;i|o1Wug&YMs&F{MQQx3K(=XT4ertJ9|Gre1#PdgCwCJ$r)>Kbeip{QM^Vm1D9n z+bqj?f|1c|av_WAWNv2L`s9R!goKCF!zKtnO-N#3c9Wa?<6Y(D({q+Y9E@RnztlXg zzT*3nj8-d;hSRyPckloI|NsB*pR4xT2s2Oqbmsd1f6WKiZ&tM7ne$G(?tgoe`Q#MI z)=hH%nm^XRxs)KHTg~g$6%SOh8ca^=9pEJudnoVYB$ppK+uzqgP zMXtjNJbTu^J#4pN>D=y@8nrR?l|@3GD^lNZ2tCwOOJ`tb6??GZNs7@S<{J!*N|`Tf zjAu5kRE#$b)@?~&q53N0qU%ns**YA&3qv)f{eA@K*)E%|a_F+@X8x|}byxOSy*zAd zKP$HW%Kwe^rS;-$b!C4Wd+Q(9$MN_7>3k%UrkVPAYi#G2JKYP-lBeZAUVFL9{yN`h z?t+&wp_40FV#Wprle^i~>n+e_3=K>T(ZvkRFvScFj4aXB8JZZIVu+a< bVc2eHVQ2zTH`$QGozu*mOI6j?-;E0Z7VI}5 delta 814 zcmbOz*eE!`uijFq*?{NGSFT!xW1k+ZKF_{lg{0a-B}tFT&)wSbDxq@l-%D+sz^AG)&W{b0*`^obJS885LaY<2XVlIf4 zpOnRAs$if1q7@Vr49zV!t1zBmWHg=pmMN(|w*Me+vjLCmTsKLEVkW^(K1Yf60v#V_ z?hC34+6RPAGOawK>h7^HAmr=)c{;cMGcqKecrPQ_Xlo);`a37Pv1Ivy|+GZl1Ie4Irfi2&TTL|-Pyfz!gl@ty}1>)&WfLl{bm05?p6Mjq#b(SC6zUf ze|{gq;=q!j!oGLwrCPze-==A-T9&;^XMueCSq;z0hhOlnKTz2O@&T$}ERg(SW;j`x zC6m!~axF_?eQyvqSE~Wf=}g;0%=d$%kIUUV8f+D$HTSc5(-o=Y9Ilqjj9h;j8pBhU z+iG86mYk85vTxHhpQnpiII^2J1b^VHN$xUUy3+mHGvkk8>kKDdzPiag;Q#lZtid&Q z?5o35g6p;jPQ5>Y{k(?#O@aAsXD#Q>yl?&VvbV)%g2g2?&#rG55eMAfPwr%uXEdI?k~NddRL{^rK_jE2q+qfP+dWTa| h#1KQTp{aoxhB|Xg3y9ccM-F#RQ$sFQRabvEE&w}EG)Vve -- GitLab From df09493e72efb9ea6b6d51972386055e6f0c8593 Mon Sep 17 00:00:00 2001 From: Thomas Holder Date: Wed, 2 Dec 2020 18:26:48 +0100 Subject: [PATCH 9/9] New PS+EPS test output --- .../testcases/export-dpi_expected.eps | 355 +++++++++--------- .../testcases/export-dpi_expected.ps | 351 +++++++++-------- 2 files changed, 369 insertions(+), 337 deletions(-) diff --git a/testfiles/cli_tests/testcases/export-dpi_expected.eps b/testfiles/cli_tests/testcases/export-dpi_expected.eps index b66223759f..c86b4edc94 100644 --- a/testfiles/cli_tests/testcases/export-dpi_expected.eps +++ b/testfiles/cli_tests/testcases/export-dpi_expected.eps @@ -4,7 +4,7 @@ %%Pages: 1 %%DocumentData: Clean7Bit %%LanguageLevel: 3 -%%BoundingBox: 0 0 173 90 +%%BoundingBox: 0 1 173 92 %%EndComments %%BeginProlog 50 dict begin @@ -67,192 +67,203 @@ %%EndSetup %%Page: 1 1 %%BeginPageSetup -%%PageBoundingBox: 0 0 173 90 +%%PageBoundingBox: 0 1 173 92 %%EndPageSetup -q 0 0 173 90 rectclip -1 0 0 -1 0 90 cm q +q 0 1 173 91 rectclip +1 0 0 -1 0 93 cm q 0.9 0.950196 0.9 rg -90 45 m 90 69.852 69.852 90 45 90 c 20.148 90 0 69.852 0 45 c 0 20.148 -20.148 0 45 0 c 69.852 0 90 20.148 90 45 c f +90 46.5 m 90 71.352 69.852 91.5 45 91.5 c 20.148 91.5 0 71.352 0 46.5 c + 0 21.648 20.148 1.5 45 1.5 c 69.852 1.5 90 21.648 90 46.5 c h +90 46.5 m f 0 0.501961 0 rg -82.5 45 m 82.5 65.711 65.711 82.5 45 82.5 c 24.289 82.5 7.5 65.711 7.5 -45 c 7.5 24.289 24.289 7.5 45 7.5 c 65.711 7.5 82.5 24.289 82.5 45 c f +82.5 46.5 m 82.5 67.211 65.711 84 45 84 c 24.289 84 7.5 67.211 7.5 46.5 + c 7.5 25.789 24.289 9 45 9 c 65.711 9 82.5 25.789 82.5 46.5 c h +82.5 46.5 m f Q q -82 0 91 90 re W n +81 1 92 91 re W n q -82 0 91 90 re W n -% Fallback Image: x=82 y=0 w=91 h=90 res=300ppi size=427500 -[ 91.2 0 0 -90 82 90 ] concat +81 1 92 91 re W n +% Fallback Image: x=81 y=1 w=92 h=91 res=300ppi size=437760 +[ 92.16 0 0 -91.2 81 92.2 ] concat /cairo_ascii85_file currentfile /ASCII85Decode filter def /DeviceRGB setcolorspace << /ImageType 1 - /Width 380 - /Height 375 + /Width 384 + /Height 380 /Interpolate false /BitsPerComponent 8 /Decode [ 0 1 0 1 0 1 ] /DataSource cairo_ascii85_file /FlateDecode filter - /ImageMatrix [ 380 0 0 -375 0 375 ] + /ImageMatrix [ 384 0 0 -380 0 380 ] >> cairo_image - Gb"0WH'"9PS\T5"b>sR!5geEA>#*o-O@iO4X\%*-qcMm@A1J8n(%L39mSUBAm&DJ3@:ip8) - &VLH0Le;2,\jHDCQ=u(:dd_5R$mA&Hg^cQIr2eK@?;Q,q=WJOchCMul8"XcoqhP25Q/l$&' - ncZ;-;Q[:r[X5ERX3R;3b[Eh$MJE<8#+sH - ql'fM<%cb>QXprUk*6_gmNoJd.*6c5=-K$;T%EuX.b[CR5inD0fbeY8Gi$b`;beY:C@02JK - g;"]P_&Of[.'=$'_&U[-!fl9o^`:S7*%'SlXprUk*6_gmNoJd.*6c5=-K$;T%EuX.b[CR5i - nD0fbeY8Gi$b`;beY:C@02JKg;"]P_&Of[.'=$'_&U[-!fl9o^`:S7*%'SlXprUk*6_gmNo - Jd.*6c5=-K$;T%EuX.b[CR5inD0fbeY8Gi$bafT&-'15+NTQ)LQn)Zn(3Op67M%]:7T1a[& - Z[SCC0sPiP]C:jJe2Y+@8J=&:NYLZL5o^]q68;7GSd - NphA!N>+I^Oq2&*.=&cS.WB=7!h/-=K@W,VWf")4_-,TGS\%bupH$Akg.GUH:5tWV"$7cHh - 'e[B24b`3[f-_7Z,e=W5qmTl2p=qaUrU-66RJ4Y.-Q_1=\4TD;/l=VT.CL?Wad&[m>jX'!= - &`=5rpn1+]PI<1SMYNdlgF)i>4(:_E%g-H1oIM$a+\fHXgLS47JWPQ!`+AUq0/RUmQ8t"DH1]KCbkh/A3iB@&pDCS.3j'T#jgj)D[R]13b[3ZXB246-2ru - _@&gd\)OaQ<$Fj*E08sW+8/g^,M:-^GU76;9Q2gF-s9 - Bk$S\]@#gG'3LGFZR,d78%d%./ju>AgK/oAgK/oH-I\&PfRCS'A - ]m"2\^4SADHk08?bp2Bs*S):l]d$&B3Bmr8F5M)nuX.o;XR6OR>ptkQ8G*hVtGm3$KDHU.C - /QLqUp3(1&Gh[BFetf - &g-+pk.3@>ShJ;okU`imr)P2RqlBEp=e%(?+'7'X&eJ\F\:))CNZ3sBkkrC=h1jI?U=^MQr - KT?I4.2Kj%T')f,p><2-I*EFnKo\Fj$RoNndUDS(o3B@Ie#prDE:biq47J2jfh1OsUFEJ+N - W&Z6i;fR:9.(mnp6_d8?[Z7#`oQHHaI\,CPpS8qi#%(j>$3.ok"Y,t[EZP/ho*mhL">?brq - (.u[+cR%AIDc+IP/a"!N-4A?mg`#Pmp0\i0,C37]T#KLIPPL#a.,sF-o'7#&sg+Odc:R4Fb - T"O7/,qq6'XEc$lNP'SfeLHD9>%sOj,[@91;:AgK/oHAMV"+j^k=;EaV^*'+S!6m:,`eSJITG#r"2SE1 - q:iL2M@PI_BIZBW,^MX7)uGbhM:)4d>9I].b\o@"oZ^^5Vr,:VH_)l[N[%*N=5k?SVKL="a - OPZL=:WSX7'3kt@@LqP$;D?6"!*Vt)D7]6p$lb9u,9I.PZBr60Oc8auuSfcOd<*69h[h;n: - sBP_hmqKGg*Q.mU]$(X*6ljRd%\)"*p&`0'mGM\R+dk/\NR$McN]R=/\b%6pQXH7t-F\,p1 - _Bg"BML9jHjmRZeQ1(]bJj2j)afR#$RP-.)?/@X$,-L>R%DkAGHQ%d%./k$ - S\]@*Soik$0b*q#=L_Yl0rA3LGFZB:Di^1j(]b%d$0.QugZB;pejtg!cpCfRD&\*6c6hZd% - as<4>p&p%N/@)07J:F"h7bS8OA:f[6X?AB_k0be]g#F+u]H-BH_Z=I=_$R"IC$B:q98/_,8 - t$=@4M%d%./jp[c2k"*afc/qO9*6c5=bS`BQ2+RY>]=6PaaI%T?R"IC$kH*f:2(^\?8&cOm - _&UZ:38\>.eCrH13LCIo@Eq;X:1\LCahJ+&0F)YQkIf$$9KoV33LGFZQug"(83q_])07J:F - "esIF1eV]be]gj$$._FF/4ia%a;bZ#RPQg0W2LY)KRS[6Ga]]b`Q'i>;-;'+sG18`15!4b4 - GRI/l_hSrk2pXfa.&^7[*`hiSD3;mTWS,Ol^!gk$ZJAR$2d4D(,gM8r>-Tb<1lS?BT3UfD@ - )opR8WXbrOZg_WpIQPOkkmX#7)XX[\nq,ZdmL/(p__U(fW/5Ge>-NCc(OXc?gK-/E[^Z7FR - Ck(5K]ENbcP-iR4Z;+XX^U=L-_:N<3J - LV?+.SQZBhHAZPGVqeSX_E[f^`HSGLSP+<6ucZh_H)3^LauGr^>(%'g?. - "[]^LX!SAF+JHt,p^WB#!s,j%4rq'"]`cnJ."Mr?1@@h,K9poU6:2Vo[&od["Jb]-Xmbfu9 - eAWGhgo3r3lcl$7ekff1h5t?,(k$S[i*p/.$S3c#s?k-QXq'TUp=)So;KRKguKS2@Z2FhLqDnqmI06N/60ZO38gl63q$2[Vu#RPPl0LomGC - rRHh9/+C8F8Xd#@1En33+IsoB7`-3R,aPoeLf==6\II[R3S9q=!98QX9s$=1oJ($*coEn0Z - Qb+cMBnLbTX%(*%0!n=uokALMRHA*&a^l[0C]njtOhaEsDW@3TcE'@?/7fPRdK]/'K0;_Aq - a`nMGMFLTCr+Ji;d3Hd)\pXfLi2F,DK+rFd&00$&+s4d.$?"nN)/W.D[:S5#cMk._=iAKi&V5qRb04E]Tk'ZUqr6q*1O453WU8-S?o - c<_>g3jq>VNmsr(WIg:-CC%]8rP9Vqbd9P`^Z"0+#DcXEREg$^,5K1(GAc;m-C/c-`of`NK - +pZZIG^NHWR$XVoV;;SpU^kf\-&E"`@.BHek['4T4C^XZ\/r"C5S3V13^Lg:#_bU==>]lU2 - 0!j<,+F-ccZ=lkhW-Cjhc_aopb$W%*P*^['i0M2f2*-kn,!1Fd.h\T+ksl(0Ua4n?==>s(?U:t*3$NO)KX!"0IONqaQ[+C= - V^(Pf:4M,?XO@H($:2$S0PF3C>*DVL[5=g_JYCGfo8"&:RDf=F,ncm_]5s0bR(mA''>9=@8 - >J"3F$Vbfp.LVA4`.%$0Kt[kSj8@a%s),c4&Qc0Ig9@VY?A0+.iJgr"@cXPO,?GiG?h![ERf - E;gkF[+a3Q%0L>U"A(SgKNRm?lN*eR+^8ERi)^\'*9kMW-sHiNkVsO2(M#Lp;3f@ZqSp=>&.LD=0fS9P\":ATU-XMo - k2o4%6Suk<8&ZJDB?VM;9UMSacI4kH`Z1_hKK1OER/H.TF3(BJ0rM[`4ET^:fY.?,L^'&+k - 3P:B4*9W/IsXBA&.Kt5bXjEZ%\5U,5;'trZ7:4Fq9Sg81N_cHr2(k(OB'tDcKm-h6YDGJ2a - )?hq4!tC6WRcZ3Hq@V),bOIfb*:`H=@@ePs+8gN>W[5F+XCI%d&jQfji=SV*j'5ma.@8oLF - !WLTJW>F"Vb.'!WX&jokVp*R#\-ZHA]>8%+/tS=omO$d9cpZHA\["m\Kf:=('j`#Q'4X3CD - .&,nY4jokXf5g1CPZHA\+3u:>S3K&)5`Z2:a#W,$YOZBbWbZ^cNVjhbJqE)Wcr3C\jF10?jj_MUnh&Oh[J,\W]S?6k8I2-hEQGac.V(TlZ^6fSP7SNGTMEHte41hR3Y&-^-5F7tme_]5C)At(f154N:0+UV_fRIf;*pFq@NN7hggp7B#bN`3q%Fpqn` - @\Tm'Cp+%MKZ>./Fs8&AfMDTB"bTX$E5]c!A%$8u3_lnj@S1!3A - &p&j1AsG@[jTV>6B>Id[0S_&ap6bGQ9E.W`A*D-^lKtK'LF?R(QnpdLF@C7>kfe>KmAD[o- - t)b9oCK>]LX_i,hL5ZML]V1L3WJZjIgN)763lWa"jA$/ro;i7#cTi#JV,sn>P=GJi(=9G3C - /-;&9Gq$lnR[U'-DnQS=i(XLcK!Mp6\e4=;j5AK)T^!k)aH5@uc'Q3cfoPE";#lD./H^3C/ - -S#BRtp0Ls)%q.Inp)GHNn@SY;.?k1j*k*VWJnq1u,+ksjRF)us4>:_I`/.?UDbq]$f($:T - p@?.lYcYO-_[lZ="/.?UDbq]$f($:Tp=_NiH?8I%O*oH\$#rmjqCPFB5EN^5pF8VA5kfc%d - I7W<\a&D?q\E#dNh`tMRF,7_a;(Gd4P_Pf>e`^Ge@/UGXfV* - g$N0LXtV0C4U\+t&Wjj_n,JW,XQP`^Z"H/S=A?&!nXX,fJfbR9/*E>2NZTD=a4Jhu=MTI.$ - r<`%^OKm?,:DLY(lsZY]BAkl&nK"`ZgE?P"mWr%RPoUSC:/]F*%r`>l"f&b^?BOcuM"USC> - ??TVTH0lQ1AQ-"a%_N&n^J'u]k:tK#dhAPIsI?(HjEj&q_:1:dT6.[+EF%AIMq]$>lA@;]L[ - 5=g_JW+oF#iBE4^h+.KLfbfZk+$*EJN77FQkMr1j<0gW[Y6a#1.T'_P%.r2fbW_'b8g>p$?O; - *0IL,f<45d&%pRdhRp)%krqGSHEf^U4'3*0n'?+96'U!R_;#s0jn_oA$_B_Kciq)3,d$`qO - CW?BIRciF&mnpX5nb)Q=3,'[QEgMqcQs`gd.oFsp;>cL9#ok$ChQV"Ij+b:d?8JCLU,c_Xa - '?+I3lCk'IeB;_o%#J\Qri-D0'7P&JK$Gm*7:jpTes8o>ALis\tncNm`qD!=+tEsq7cghqb - >4SkNskUqqu6#:;>KM="aC,/)N/LZBm3`o&>b806N/6^AIJF<>LjA:\m - 6'ZDL@@RVLK>bUL0,S[-VKR:!]4[O2G$-Q^?6MGt=;iVc%dbo64Q]I5>qV:=#MLhL!<"/$::d - GCPh;)O0=Jmcj+!Fb-$(L,hQWkI;JeV]Ss`.BY5IGNT>q-4)bSX7Pg&'[2[BJ"J_nb6)hL< - 4_59aIZfMNnc2!HgG%K%/jiV(4kVt\(>+"=W?K_Db`fH#Nh,U@?/7f<4>i!EELF.X25j3c" - ZT^`Z29Eb]2($o#C6OLW(7=.a2n\S@Z-ibTY0I*%0"qkiol&Z96?Nbab]!Wj<`^F3(@8a"_ - X=3a4:n#]lLja4',F[_lPa=V^'Y@norm'BTj3JiSjYj)/o7It\k`LbQjMF3aOa/'K0+_jjP - m;q=drg!bd<@:/:6+^n-Ec1/5fc[Ap>+fiJM:n)OWcN.Jb=W?LVe=>7Dg44st*3q'<$)ooc - DfROeC^Y;NB?jn&E\N8b$ZeXFNr6A!r-a*_6Z`XsKZos:5H[&r*07'ZP*kjfCCa.bS8>_.L - 6s3)%9p!Af*pfgkpc$XF)Uog1NI)`(e/rP-EC]QJ\*%Hk+#qbRDIeAprt7e/FdWoA!3n0,r - \K3If7mXp<9%p-I(7$94#aQ.n3dXm.0Tuqn@>C?GIQW-4rUV;+^?PH@PA]ZZ9ZG[V==ia5< - 6,d,f88(HQV"9qH:(Z(nRRd*pUukMJSX]UH9tN/o`WMj%(s;bTe88X4q`\adt7P&Iolq.>h - .8\%82Y?5t%gbS]_Rgcq$V+]pQ8YrXuIA'F%M3@cq-Ru=XNt[$0?PV]lmuQ=%]HO[2eYlqa - APHb/*VJ94YBdg0]n=)tcTQn;VkU0MI/g#m],UX=Yr6Og-b10,DG:'Yh'@PE%WYAA4h%fcdQp.RT%a7-!r02%d$/GQt(b/mG7.q*#WfO#BMRH#]j`bCP+](k$S\]?qq9qFS0`a%d$/ - SQud8:e'Qq_oT:j5F"di?cHD_#IQicPR"IDd'=9bO>qIBn`Z34=*6];53Ro_3L.B6'beY:C - )*$Mc:1mA#iK5i"F"di?#0pAr3NlelqR43[beY:C))q`CnU9]4s1CR=4E)tZF"di?#0oJT3 - k@CPNf5ucK/o;TE6^.-T[5_Tk$S\]@*ZPJh&Q6j9(BuiK/oOZG_&U\8S: - Pb(S;*%.H.4lk5NZ>`k$ZKfk*.n)?hIn=k!XZcF4$BHNBDN>3LGFZQnr\8#BMN!0F)[5D(l - 390F)Y'c37WOcCpa;#<&@u82EiiOb;GsnZJ@FDn\*jWML3V'OE?+o@D8\ - 9f_ei+B4K%QoA%T.X3C,&2.QW@osT-)J$M*5@!^tUR7;Uh.0l9.3Ur.&GPl9pcOB^8qO`dM - 3C7L#[I:d#SP*>&<$fLV1,@=)U85t>\bq?H=/`c`@`?[BIVum&UdS9ncGqqJ+(Z<:E;B6sC - $9@e06H>):/>5X:Am>mF_P?"=]:_sV+me,\!'d]P['_pQlKX;m];7J'.7?c1@Mf^f8Ye/4X - PK6Guq2\qPmOsW?aEC>)CR/B/M_RkD>Aic+J6mp6<\PPKf$-O%Cou2;Im[\]D7"jO)\Vjc/ - A5NFh6polg$$pB_YuKanI/l0[riWk,=DZF/,fQHG)NMJord4.'oKIs\m7s7"srm&AGm9fHl - lcEr('pCH:n`i4()l>)%nQ%lI^5&A1lQ*a:X)B1JUr7^Zjc]qTe1O%u?g_o6#(+%,*KmtI3 - _bN%YW+r;pj_A+GDg24_rPFS?iT=ZIUM*D&CR&jIT%n)C*6L#9G-mKW+ROa3*ftS;&]6^cB - 5I3BQ0W,EWf^Id[h^sgk%OQAGF67$q7DBGi(EKgnS[4RqW!jcQ2]Zj1^nFZ#BMR(F$_gQK/ - oAgK/oU2Vi(dkp[U$fa#QAo'T6d6(]U< - 5>ccSEdOt&OU-5RE_@?cM/g*I!\%;a=gp#jilMJZZ9]HoZs$,=WSs/9t5Z>ol@NOhV\>3Dm - +T^g1sb?jL`Es2Pnh_-0`"Kf;Ul0P(7WRk546d0JBsr3W%k'XH0^3A$;:k)#qm-k&pE@f,+(\V^eA<46T6gC6Vmem-QlKZ1)m^W3j,lk - 2W6g.*(9!ad4KdPQ3]\qJr/$TWpQ*)(Vf^8QdbX'-/[R(2lIK_A])'"F(48Y3c+LQan#JXS - >Gd.8#2b`bmDa:uQW07bH4!pm0#Q`mWPthCY(T2EU1"@^$30k"IpK^.nWKG9b^40\A+R.RS - $uB@X](D6jf_U.U?$TBrihD;So4m#qICo,p1`5oY0"k_Jeq+G,KmNi]ig4Ar79t829! - Zn9h>%5G!lO(A.E*`U"`a'dn`E!LlhNt2(ah\t@_&U[=E\I`>0F)Zh*6c5=beY8H - #BMR(F"kYV_&U\8*6`DC0F)YQ#BQCTbeY:C_&Og-F"di?0ZV;O*6c5=b].YW#BMR(F.=5o_ - &U\8*%**8c1/5fIHGcrbXjEZN\-&%G0Kj>Y2:hdYu'(-:TTqiFj?L/Apo(1SQ"8,H]6()&M - @%S/"\g:R)>G*'[:@UBFloB(PWpJ\(=<-c<`:G8i@s+&sJ39%$Q>Aqk*jnfTEcdoB*eJs+r - \,;!GN7Sl7Wq.BY@"Lc0ac,aH&TjA&8fIHLIbAhcU7?WoVk4d^:FNRh.1;Fbq$7upEqXjno - u2G2$TO*sn"`L$7))C>\L;L:e^X](D'MWTerGbV":Sh@-H:iTcpq\!>%7r`BfpNC>Kpml8GZ - b*lL>br)C-a_;_--=p0eU"C0HFpOj6,ds]mRr41*lYO<3LGFZ9YaM1)jl7UR"IDd_ZUsu[=L?*K/lW@;-Y'-K/o=:"<>CgJN9*M3D@4c>T9&_3LI\e*6#=:3LG - FZ9YaM1)jl7UR"IDd_ZUsu[=L?*K/lW@;-Y'-K/o=:"<>CgJN9* - M3D@4c>T9&_3LI\e*6#=:3LGFZ9YaM1)jl7UR"IDd_ZUsu[=L>j - YH4u[!mTurl,4Pge>'N3HMVrp~> + Gb"0WH$n-7S]>_85[9ZQO@)YU%>]^W[&XK"U[)Z!CXq7KgMM)8?pR2Z=ug-('?D1,EY-)"m + Mg0g&-<.`+TV^4mZ9^%0Y*C8*&Ee2mO+"[]"='Jr'%k;hjNB7hiHC,5C]EO4QefMK\p/:MI + @?V@2O`-hl``\UV4")?ZTGXcCe[EeSD3hW[aJgR;&*HA_OG1>regc.qm,pl0_Pb/!sp\3-AN#^8Rj + $KNW;CEJ:Qi\ttiuqf0N!Hen-Ar#U'?,*&,a73%K=-bpk+DAr..*%#88ui"+T5>->N#Trk + KULeDLl8JNoO#_&rFSIkX`P5O\pW@%mjTi",b%7*TcMd2KCacO*rH72%U(W49*7L)PqB)bZ + T]5FNon9kt1o,,i&S=WW&Ri"'E3laYj"f$VKukbrF%VZR>T\jDgfXAiEHXJE,^DZ*jih7)> + $=0=-Ajh>=fUQ4t[D/3^;o8bKl5'qA`@lp7#S!o>]S6Ck'o^'Z%b0I!/iKRQ;)j1kT1umf) + b[])E3-I:[nA^-@lf6U#^EC3!nJg&L?aDS8l*?[fSELdj3u(FR1V`#0!+S6 + nKbE[=mejilK*prj\l9F!?*#o0P)G?]6Xqk9QfeI!jXes.)YD^-=IF8#t8, + $p%-O:dJs7/^:D>9faX_6$;"5k@W)tRsQGOa/@`PjLG68frk5@HkcG)r36!e]'dfMb>:5C"(rPekO/L + !DN901^G,"`L(OTS7hWK/ZPC+_%$_$h5K>2fo^`1Lf&tOEQpir]_9?o'^eN$(k7JrXR_B77Q5P + 9=?r4,gQ.m,aQl?3l>ZNXuC`if6qaksd]qL)u)"/aiKfhM10b_`Jf:Xr<[UR2WZoW74GH45 + !8lkYVGQUT(_G1Q#S"aB'u$HGPBloGJD::h1]uha_jM.jIDF(km0\'ck2'>P$Dm!DK/# + uc9J07rkKWN"+N]JrkKWN"+N]JrkKWN"+N]JrkKWN"+N]JrkKWN"+N]JrkKWN"+N]JrkKWN + "+N]JrkKWN"+N]JrkKWN"+N]JrkKWN"+N]JrkKWN"+N]JrkKWN"+N]JrkKWN"+N]JrkKWN" + +N]JrkKWN"+N]JrkKWN"+N]JrkKWN"+N]JrkKWN"+N]JrkKWN"+N]JrkKWN"+N^5/q98`FS + TGEet;1(BMRX7WcP69rd5:TJc-(*q"(_]np!bZJ*--da7j4pr-GM1M9]EVJ+KRmk>*u50m0 + esds_6:3Wak5BL5nmb_=Y5#I>s7V4HJ)Jh)rcu?S"f=M+"[5,9k"kaILPZO + !c?%d"J/NG&bal(U%]eE,em;JERd9u2%R28!-#n.hR((F:.S4YQFF*-647=2>Nt\(3VJ`"u7ujjGA!me[4D99*J + ,p.m*!c,je5)D,DX8lHZM[>YT8tup`p\a9:HZ2LaX`&*#f3p/L6*9!tLck\4&K>":79Sa:Kk'4dW18]/<`/,;Pm0f*ng8Ghg65+IEO3^U + Tis0Z2SqA#p;NkG&O[V#h,&*Db\n$,TJamsiJZa#Q@las,L1V-,,L]2\[WTU]Q2q`D\SZeU + FkbLfuhK9XcD2dA^0gU,$c`d#Vl#!LXNZti=Gj;5O_1!5F"s.ApkF!J)Ck8JNn2N6T+-Ji=Gj;5O_1!5A#< + g&cTQfp]H$,or#C3g;JI]r..*%#QDg1O6JkF#(PPAr..)h8qDNb1I3Gq;D0bd@kYEEfrB04`:1'Rjl6#d#j(S2 + 7j#k>$)Xf]cM(MNqF5We_R)o&\VN]k,PnNiWLEM=u]^!klRZMTngpUiP"pDhLVDTd20Jm + %_pg(.Be6Zm9AmrSGs!VNJ#+3K8%u7B_Go2T^#a4Cg&:GO/,g.Y8KiBf2D];!V]TdU&K + S_UE^NT6S`FlLj(M&!$9^re&5G:"::NNQU!<',IqlOnPW"h^kCq4mSc;:"-_*AYqM\a)L:I + _&hC"jh;"R**8#En>,&=1D5,sVF37/K)j@k*J$Co(LHGK0^O1lX')gX"2'W5H/a&:)YJTlDC%Ti + a6$(uqP*Tq`m'%1RNb9@[J+RT(ebW3OQr;Z"c]*f$P5XXZrskTL(54F$K_#`6A3&hFC"oPJ + 59W&BqsRYXq5=,`XIMni-!=5Na"rdKa?2bZ]5oUm:7QtMM\Fp1h9#R:#QC+>bI_)Pi&TaNL + W9TY%,`thEM-KR3Js&]*]`lh29('g(_>kjF;t&(p6SgN>IFCqE__+Erq!jnql6*bb-g?$kT + ,Q+THc;]ZhL"Th7%%Y7"SuLAfOQ1HmJXr7IQqqjI_Q2Hss\:-)W2@;l4K*c=ob`N3OF6qek + cD]o/CJJ%CI*7l%Bik`I[k)3fub;an_rlM>/]/2PX!Q(hGTieA + I>-'qY1QGA2Z;+!'DgOfJJod[XMgd>5#M=*lpYFm@]`>kj,V"-8&O+En\IP+q2u1WQ?d\t0 + e)H2&]=urufi6)cVkgT9P0ZAKr_R57cG1N4bJ>9Ore0Hg_]NF4_&n7-8:(^KIunDs56CeGp + (e)9+OpUE`;1.0(BK#Ibm&[9qK"^p2#KWt)uL<6c`rF=n-@W_n40QE<7L:`pk*B=p`nRdd% + !r-K5<@=,M\oTR4$r(ZH_O+?.!;GHZ$7"5\X*)b:YOIQmWK5<.7A"l0 + q,8McoccI_:^BFaiOD9A3-Mm>Hn+2G5A,G\<19V]5EU5Rn-@WGnAfX# + [Noa!qNG'IGpMFk_u)7Z#Jo=/;GEJ>cgL,2/_6P#93sGU+CQBNc, + @!a[im/Dq?c[3c\Lteq7u@-3O?XDk-KNb=9($lImF9Q`QUDY!,e"$@T'RNn9jk0l]t&>ibd + -bFj+$:a6Vbu(4A3G/o\YB"Q6K2RK4iQn6%^-^Fo>6R"!%V1jo3b68)AR`n=,qS47J]V7>I + UlU:cpW:-$ibFffelqa8bX+1ethkSKO_R^2&'(8jf#&'O^)YQ%W&Sg0tr6n]FiFmKXIrIE? + bT8>p?L;4lV0RE4NmeAcJ!=f3$\W6O*_-aj(i,qdZh>>[ona#GA$KmdBAQNLpjH!B"(XjJ( + 6mmpk*B.p`mVIcXT.?c6'(LO5St-;GHXNJj8]FHWO'oHN"0krZK_oIjFeFHfo36rTX-U + :uMp_:%1Z)5@Zjr.dE(=C/7'J$@e.PJj0nGl0$F^o:YIur=@O#2?Xo6Dsp.atK_(RA0d\2Jg'\dRhMbpCKNrNc;&gNI_oK(&r=4[tM,)UIY + ^n#$E&%#-u;D#E&Vp=n)8R*2_E6$)5UT#58Ci2fpuX7"KpafSk/Xf:8%qh(H<&0X"HZq;Lp8HZK(Qi&(Q7+Q:H%cVWeu"NW+*?ff5\/'k?)$@f"tV3dJ=B1pYgpk*B+p` + n%UR"?1M"+LkT>ToKi=GiPbi"O=J+h + ^PVjou3QQ4rpZA%4t1k0[Th[+ej%q5`mV"s&#eR(=d*%j7e+(Pbq+)1_9"7QQp+D1+5RQp3 + hmdLJ\+D1+5RQmAmT(!!a+D1+5RQl7&rSS4a6-S8J1k,S1#:er`7JMLO,5h5Rb5$\)rs.4Jn + 4/p3M'\#J#Q>iXn4/'pI3&0]C!m^f-P?>#RgB!an7RR#&@:j\f,sqho>jCQ58*niJbK\Nq6 + T5#)H6QR:?qBK47EKS-2:u55QCTkJ*[:)V;;/no;0],M,(bHHe$aJRfL1l8+ + PtEUX2>XKNW<5OH4bs!Y?RD]F/bon0GhRMRf!IJirY"rZiY>2)rUW'6t4!n7Pq?1e%ArQF[P)+Bo-N8FYeDLOZ/!5CQ]4i"*,Ii/ + \"j(.KiSe=Rerr!\(:RY!a:r,GMU=3dG0^O-<:OICbc')[m]iOe@Ro_df')rg<#tgIL.%e=W>Iqu_G1RY!cdqQp+ + dE^frs^$5uTOGsBmbk3@QHD%#3o_dZ0r'>')rg<"I@WA,_rq-'G(]6R6$N?FScU@5YddT!= + T3ab[Jj2e#_&p#]q3McpU8U;iIeX(rOICbcB)1J3d8%3T + $$BF`,-1H%h]iZ1#3pt[UrNN.HaLQ\%VRN5Ig_';Fg5s<.?h"H#r7?W3Kp!s(/C(?>4Hb0 + S=QZjn(7H0\HRH8R&df:W&m_C29dN%abmp7,c=:+o:&Wu\&f!S-7_4Ri2U$qn1T7=aW_?X:M$VNIFL(cWR30QC-_iHWE;JOg)+PcN6sdgbK!W.V:m=4`f(f6gg + +a!7+L0&PcW@AYI<"BW:.q=Im('CA1Ar*X5+:=q.I,6IJhum$fi7I]7"=&TrZ2<:?MumUES + M7Ui"&^anAdqHr3#lFlHe_]e*mUq>4BR#Ir=,TWMi&?H/\5tfimmc7":(4MA5_2qK=^n6+. + R,YloQ:AK3K<+8CK<'`>3=InjRGJj7<\K0P[[:KVKOr'<4Xr36BS9`N!Ppk.'1r"8ACM9HX + 1iradmRY!c@r<^RPJNq46K0R6OM9Lm25KGa85@XROQ?hgj^.CQ_"b2X1Jj3`C1$b$Ipk)N] + r"87U=eE@E+/H4#O'$nUqW&[r+I*+["T!0pl_M)d^`TALi/^-Q6?>KP&$k!(8pU"3Iup+N5GJ(+GJ_Wb%nW + N5,^okjOP6PHf7N]%:;Vm*=cr1W[soC(2bOV[a+Sek-,R)P2G3A$4s3]C`T%0!cS+'VDkIq + Ao1,_A"<5AqWhS":>13Ph4\A=]AP-EY9g-9aK(cBA.:;/nrY54Ae[8FXYlrQ2%aJNrYEg4M + W@kg,OB.i`rp&$h;TO#?cTleX5g"+O8]r38_@nuD<>&,]9jnAjhlTaGNBi",`_61Dq[r2FV + 6LHk8u*Pii`njfJ(d!lu3JNrE*r`GLO3eIqA^`V)0J#p^5HLo\7f`?T\O(s826%sm4YT!>I?M0 + p$2.s>7nKa/&eQn^5u[V3SgjK?.ma#JMfSdR=5+dV2sK*LY_p5o6r(OdG!I"aj=oRDCCMsL + cp7^4K$-W`(^Q-hDk#E>DM+qoZkj.k^c-J%!N`;N1Lr8I+7GB/GN%`n/1@KO<4KYeUQkBr\ + $l]ZL?il4(cilE:]pNS2M^1%/_HV6O*ASO`#Z9)*/0p%7O + q#c^_UgNmktAM]:Y7ciN:\LO[S52b/>ggpZD(O0/46C"jmgA;]7Yak?LDO[#Xsj!@3NM[kp + )OK]nd>!TX,S,hM90C/8UL$5aIIl'lgpr-@r6BFWZ:/cpO\&,^uC$Mi0DLKF43 + i=Gj;5Odj`qa3@8HkH,UJ)Ck8;8SroQC;,5!e9,=r..()q"(`9gu/eAJNrWo;1EoebT6\u+ + 8>qfPk.D?dr]\T^`W=RJ)JYJpSZV-G(r6N#QDL\-2a`Vmer3;i=Gj;5O_0dIsV+&1]2egn- + Aq@k>.;HafLE9+8>qfi/]Gg<&[mJqeq$Mn-As.+'JLrjU4qfi"*l#An/1.MngG0"+T6iKQZ4Mg9(f'-aCbMGDYm_9h?=&/3)I + rD@ee7Z6R43;+J-b@/cpQ6]U7aO@[Ob\R[#P01.q0-GH7_+Ksk:pkE-`aKnA#BK(qhJJWn< + 7-BiUl0$5@,DCI,i2*o\>egg'_]*,]:/4mm``mN>#S@Y9qTBDU>lW=gdR,^Mg*Jm65l=&S$ + U$6/Opt1/:Q`VMrZVW4XN>8o]a#'=U#?ocHs-iPXRaREZ#-6*H,Dr23*SDm-0N4Nai8IgE;3J%V@uHL'%M->]5,.rZ7A1, + il!1HN=5+g/qiP&j3E_^1O@#,RX3IEK:?2od_@6FZ+8>qfi/^3W5O\pW^`QF+5O\ + pW^`QF+5O\pW^`QF+5O\pW^`QF+5O\pW^`QF+5O\pW^`QF+5O\pW^`QF+5O\pW^`QF+5O\p + W^`QF+5O\pW^`QF+5O\pW^`QF+5O\pW^`QF+5O\pW^`QF+5O\pW^`QF+5O\pW^`QF+5O\pW + ^`QF+5O\pW^`QF+5O\pW^`QF+5O\pW^`QF[3.LM[nE6;Mr_V&GVSD'd!n/7&!["$eIlo:-+ + !s2Oj3M&75HjL#Q1'cTOu*#]`08.^6bC0Tr&L6r[bIGK:VAEln,$VXCF?[]r1VRIe";cj,N + ZLZQONl'b2^fmcg`dnB9GI^#W#W]nOiHuIlR5=T1;+DM#Kn!lb)-.A8%uWY$kA_;O=OX:G, + 5Q>]Q628nlGNlaqOBI=/6n9$Zm_T6H1edcdN#U6]cp/Gc\K,!R5MIt-bXbhbS_r^S+RrA") + .S3@A\n;EYe2T0Olh!_HDQ@Je[p4_X`#3Q+FbGr^r;g4">hlb]2]CgAPgKO:;H=lf0i3+oL + \7;C0QN,LTG%7k`\_4cU*^B;^P?jGJ04_/aF&-A<3gb*[E;F#-h)`4Kk5NnNX1s\lRa^QnX + -^jZs&7RB(5=L'6s!J1\Thi<;J&QGQ`Rs+hCHRgoU`GQP_-70=hV: + cm#?RL$a_=c1WkY)&bC\(E+,>9gCf0sOdY$64LrF/UYO)OIPA?=s85;]B;8S%($d@g'8#+P + ge(s%P"OXiQG2@X?]*/lHN3o_'+8>qfi/ahQ+8>qfi/ahQ+8>qfi/ahQ+8>qfi/ahQ+8>qf + i/ahQ+8>qfi/ahQ+8>qfi/ahQ+8>qfi/ahQ+8>qfi/ahQ+8>qfi/ahQ+8>qfi/ahQ+8>qfi + /ahQ+8>qfi/ahQ+8>qfi/ahQ+8>qfi/ahQ+8>qfi/ahQ+8>qfi/ahQ+8>qfi/ahQ+8>qfi/ + ahQ+8>qfi/ah9eXq7KpG)UQ\e.@0XGC/u,Y/]#kR%^8(TQ&Y-MV)GZS[.e.PiWl+0*MV3#V + Yio$!F._A3\*be!cK1u_G/jNj:dCHRbCi",`_bt+PRqjksp,32jAHY[C92gR;9+4!o?Xs1> + 52lA!X=a!M_RO4$O/^:C+9a:F;.:&./=-H_8:fLP(aGF4PC0G?joo4q8DT6j0HO&.3\jH-a + +ofLqZe:I-1>G4aIKuF@F9WaAX[0"%f%b&P8,qX<=")Ubngi1TQ#"R2Pr4j>c?673[4Sf1> + 2!/=*Cb@s@"r;WAg-L(m,/tZ.FP!WV3tD;^7ee5cB$bRo*.F*T2O*6_N0_#e]lXgWg]rVN( + o$:"94\-h+(iO6pGoNWuq7j5O\mk2>@9'i"%/Ln-As.+&(2B&,^uCR&IhCp]H"";1h+&#QA + ,*#(PPAr4tc1rkKWNK8CNEJNrW/'/fOeJ)ChaC@DK-^`Q8"i",a:5FACc+8>qf0N!Hen-Ar + #U'?,*&,a73%K=-bpk+DAr..*%#88ui"+T5>->N#TrkKULeDLl8JNoO#_&oNP.BY=Y58a+0 + WRDq0r..'LB(fb\?Dr"@Ird:P__;M:0Wb$I(CafkR/~> Q Q Q showpage diff --git a/testfiles/cli_tests/testcases/export-dpi_expected.ps b/testfiles/cli_tests/testcases/export-dpi_expected.ps index a2b5a458a9..a92caa7ef3 100644 --- a/testfiles/cli_tests/testcases/export-dpi_expected.ps +++ b/testfiles/cli_tests/testcases/export-dpi_expected.ps @@ -111,186 +111,207 @@ q 0 0 173 90 rectclip 1 0 0 -1 0 90 cm q 0.9 0.950196 0.9 rg 90 45 m 90 69.852 69.852 90 45 90 c 20.148 90 0 69.852 0 45 c 0 20.148 -20.148 0 45 0 c 69.852 0 90 20.148 90 45 c f +20.148 0 45 0 c 69.852 0 90 20.148 90 45 c h +90 45 m f 0 0.501961 0 rg 82.5 45 m 82.5 65.711 65.711 82.5 45 82.5 c 24.289 82.5 7.5 65.711 7.5 -45 c 7.5 24.289 24.289 7.5 45 7.5 c 65.711 7.5 82.5 24.289 82.5 45 c f +45 c 7.5 24.289 24.289 7.5 45 7.5 c 65.711 7.5 82.5 24.289 82.5 45 c h +82.5 45 m f Q q -82 0 91 90 re W n +81 0 92 90 re W n q -82 0 91 90 re W n -% Fallback Image: x=82 y=0 w=91 h=90 res=300ppi size=427500 -[ 91.2 0 0 -90 82 90 ] concat +81 0 92 90 re W n +% Fallback Image: x=81 y=0 w=92 h=90 res=300ppi size=432000 +[ 92.16 0 0 -90 81 90 ] concat /cairo_ascii85_file currentfile /ASCII85Decode filter def /DeviceRGB setcolorspace << /ImageType 1 - /Width 380 + /Width 384 /Height 375 /Interpolate false /BitsPerComponent 8 /Decode [ 0 1 0 1 0 1 ] /DataSource cairo_ascii85_file /FlateDecode filter - /ImageMatrix [ 380 0 0 -375 0 375 ] + /ImageMatrix [ 384 0 0 -375 0 375 ] >> cairo_image - Gb"0WH'"9PS\T5"b>sR!5geEA>#*o-O@iO4X\%*-qcMm@A1J8n(%L39mSUBAm&DJ3@:ip8) - &VLH0Le;2,\jHDCQ=u(:dd_5R$mA&Hg^cQIr2eK@?;Q,q=WJOchCMul8"XcoqhP25Q/l$&' - ncZ;-;Q[:r[X5ERX3R;3b[Eh$MJE<8#+sH - ql'fM<%cb>QXprUk*6_gmNoJd.*6c5=-K$;T%EuX.b[CR5inD0fbeY8Gi$b`;beY:C@02JK - g;"]P_&Of[.'=$'_&U[-!fl9o^`:S7*%'SlXprUk*6_gmNoJd.*6c5=-K$;T%EuX.b[CR5i - nD0fbeY8Gi$b`;beY:C@02JKg;"]P_&Of[.'=$'_&U[-!fl9o^`:S7*%'SlXprUk*6_gmNo - Jd.*6c5=-K$;T%EuX.b[CR5inD0fbeY8Gi$bafT&-'15+NTQ)LQn)Zn(3Op67M%]:7T1a[& - Z[SCC0sPiP]C:jJe2Y+@8J=&:NYLZL5o^]q68;7GSd - NphA!N>+I^Oq2&*.=&cS.WB=7!h/-=K@W,VWf")4_-,TGS\%bupH$Akg.GUH:5tWV"$7cHh - 'e[B24b`3[f-_7Z,e=W5qmTl2p=qaUrU-66RJ4Y.-Q_1=\4TD;/l=VT.CL?Wad&[m>jX'!= - &`=5rpn1+]PI<1SMYNdlgF)i>4(:_E%g-H1oIM$a+\fHXgLS47JWPQ!`+AUq0/RUmQ8t"DH1]KCbkh/A3iB@&pDCS.3j'T#jgj)D[R]13b[3ZXB246-2ru - _@&gd\)OaQ<$Fj*E08sW+8/g^,M:-^GU76;9Q2gF-s9 - Bk$S\]@#gG'3LGFZR,d78%d%./ju>AgK/oAgK/oH-I\&PfRCS'A - ]m"2\^4SADHk08?bp2Bs*S):l]d$&B3Bmr8F5M)nuX.o;XR6OR>ptkQ8G*hVtGm3$KDHU.C - /QLqUp3(1&Gh[BFetf - &g-+pk.3@>ShJ;okU`imr)P2RqlBEp=e%(?+'7'X&eJ\F\:))CNZ3sBkkrC=h1jI?U=^MQr - KT?I4.2Kj%T')f,p><2-I*EFnKo\Fj$RoNndUDS(o3B@Ie#prDE:biq47J2jfh1OsUFEJ+N - W&Z6i;fR:9.(mnp6_d8?[Z7#`oQHHaI\,CPpS8qi#%(j>$3.ok"Y,t[EZP/ho*mhL">?brq - (.u[+cR%AIDc+IP/a"!N-4A?mg`#Pmp0\i0,C37]T#KLIPPL#a.,sF-o'7#&sg+Odc:R4Fb - T"O7/,qq6'XEc$lNP'SfeLHD9>%sOj,[@91;:AgK/oHAMV"+j^k=;EaV^*'+S!6m:,`eSJITG#r"2SE1 - q:iL2M@PI_BIZBW,^MX7)uGbhM:)4d>9I].b\o@"oZ^^5Vr,:VH_)l[N[%*N=5k?SVKL="a - OPZL=:WSX7'3kt@@LqP$;D?6"!*Vt)D7]6p$lb9u,9I.PZBr60Oc8auuSfcOd<*69h[h;n: - sBP_hmqKGg*Q.mU]$(X*6ljRd%\)"*p&`0'mGM\R+dk/\NR$McN]R=/\b%6pQXH7t-F\,p1 - _Bg"BML9jHjmRZeQ1(]bJj2j)afR#$RP-.)?/@X$,-L>R%DkAGHQ%d%./k$ - S\]@*Soik$0b*q#=L_Yl0rA3LGFZB:Di^1j(]b%d$0.QugZB;pejtg!cpCfRD&\*6c6hZd% - as<4>p&p%N/@)07J:F"h7bS8OA:f[6X?AB_k0be]g#F+u]H-BH_Z=I=_$R"IC$B:q98/_,8 - t$=@4M%d%./jp[c2k"*afc/qO9*6c5=bS`BQ2+RY>]=6PaaI%T?R"IC$kH*f:2(^\?8&cOm - _&UZ:38\>.eCrH13LCIo@Eq;X:1\LCahJ+&0F)YQkIf$$9KoV33LGFZQug"(83q_])07J:F - "esIF1eV]be]gj$$._FF/4ia%a;bZ#RPQg0W2LY)KRS[6Ga]]b`Q'i>;-;'+sG18`15!4b4 - GRI/l_hSrk2pXfa.&^7[*`hiSD3;mTWS,Ol^!gk$ZJAR$2d4D(,gM8r>-Tb<1lS?BT3UfD@ - )opR8WXbrOZg_WpIQPOkkmX#7)XX[\nq,ZdmL/(p__U(fW/5Ge>-NCc(OXc?gK-/E[^Z7FR - Ck(5K]ENbcP-iR4Z;+XX^U=L-_:N<3J - LV?+.SQZBhHAZPGVqeSX_E[f^`HSGLSP+<6ucZh_H)3^LauGr^>(%'g?. - "[]^LX!SAF+JHt,p^WB#!s,j%4rq'"]`cnJ."Mr?1@@h,K9poU6:2Vo[&od["Jb]-Xmbfu9 - eAWGhgo3r3lcl$7ekff1h5t?,(k$S[i*p/.$S3c#s?k-QXq'TUp=)So;KRKguKS2@Z2FhLqDnqmI06N/60ZO38gl63q$2[Vu#RPPl0LomGC - rRHh9/+C8F8Xd#@1En33+IsoB7`-3R,aPoeLf==6\II[R3S9q=!98QX9s$=1oJ($*coEn0Z - Qb+cMBnLbTX%(*%0!n=uokALMRHA*&a^l[0C]njtOhaEsDW@3TcE'@?/7fPRdK]/'K0;_Aq - a`nMGMFLTCr+Ji;d3Hd)\pXfLi2F,DK+rFd&00$&+s4d.$?"nN)/W.D[:S5#cMk._=iAKi&V5qRb04E]Tk'ZUqr6q*1O453WU8-S?o - c<_>g3jq>VNmsr(WIg:-CC%]8rP9Vqbd9P`^Z"0+#DcXEREg$^,5K1(GAc;m-C/c-`of`NK - +pZZIG^NHWR$XVoV;;SpU^kf\-&E"`@.BHek['4T4C^XZ\/r"C5S3V13^Lg:#_bU==>]lU2 - 0!j<,+F-ccZ=lkhW-Cjhc_aopb$W%*P*^['i0M2f2*-kn,!1Fd.h\T+ksl(0Ua4n?==>s(?U:t*3$NO)KX!"0IONqaQ[+C= - V^(Pf:4M,?XO@H($:2$S0PF3C>*DVL[5=g_JYCGfo8"&:RDf=F,ncm_]5s0bR(mA''>9=@8 - >J"3F$Vbfp.LVA4`.%$0Kt[kSj8@a%s),c4&Qc0Ig9@VY?A0+.iJgr"@cXPO,?GiG?h![ERf - E;gkF[+a3Q%0L>U"A(SgKNRm?lN*eR+^8ERi)^\'*9kMW-sHiNkVsO2(M#Lp;3f@ZqSp=>&.LD=0fS9P\":ATU-XMo - k2o4%6Suk<8&ZJDB?VM;9UMSacI4kH`Z1_hKK1OER/H.TF3(BJ0rM[`4ET^:fY.?,L^'&+k - 3P:B4*9W/IsXBA&.Kt5bXjEZ%\5U,5;'trZ7:4Fq9Sg81N_cHr2(k(OB'tDcKm-h6YDGJ2a - )?hq4!tC6WRcZ3Hq@V),bOIfb*:`H=@@ePs+8gN>W[5F+XCI%d&jQfji=SV*j'5ma.@8oLF - !WLTJW>F"Vb.'!WX&jokVp*R#\-ZHA]>8%+/tS=omO$d9cpZHA\["m\Kf:=('j`#Q'4X3CD - .&,nY4jokXf5g1CPZHA\+3u:>S3K&)5`Z2:a#W,$YOZBbWbZ^cNVjhbJqE)Wcr3C\jF10?jj_MUnh&Oh[J,\W]S?6k8I2-hEQGac.V(TlZ^6fSP7SNGTMEHte41hR3Y&-^-5F7tme_]5C)At(f154N:0+UV_fRIf;*pFq@NN7hggp7B#bN`3q%Fpqn` - @\Tm'Cp+%MKZ>./Fs8&AfMDTB"bTX$E5]c!A%$8u3_lnj@S1!3A - &p&j1AsG@[jTV>6B>Id[0S_&ap6bGQ9E.W`A*D-^lKtK'LF?R(QnpdLF@C7>kfe>KmAD[o- - t)b9oCK>]LX_i,hL5ZML]V1L3WJZjIgN)763lWa"jA$/ro;i7#cTi#JV,sn>P=GJi(=9G3C - /-;&9Gq$lnR[U'-DnQS=i(XLcK!Mp6\e4=;j5AK)T^!k)aH5@uc'Q3cfoPE";#lD./H^3C/ - -S#BRtp0Ls)%q.Inp)GHNn@SY;.?k1j*k*VWJnq1u,+ksjRF)us4>:_I`/.?UDbq]$f($:T - p@?.lYcYO-_[lZ="/.?UDbq]$f($:Tp=_NiH?8I%O*oH\$#rmjqCPFB5EN^5pF8VA5kfc%d - I7W<\a&D?q\E#dNh`tMRF,7_a;(Gd4P_Pf>e`^Ge@/UGXfV* - g$N0LXtV0C4U\+t&Wjj_n,JW,XQP`^Z"H/S=A?&!nXX,fJfbR9/*E>2NZTD=a4Jhu=MTI.$ - r<`%^OKm?,:DLY(lsZY]BAkl&nK"`ZgE?P"mWr%RPoUSC:/]F*%r`>l"f&b^?BOcuM"USC> - ??TVTH0lQ1AQ-"a%_N&n^J'u]k:tK#dhAPIsI?(HjEj&q_:1:dT6.[+EF%AIMq]$>lA@;]L[ - 5=g_JW+oF#iBE4^h+.KLfbfZk+$*EJN77FQkMr1j<0gW[Y6a#1.T'_P%.r2fbW_'b8g>p$?O; - *0IL,f<45d&%pRdhRp)%krqGSHEf^U4'3*0n'?+96'U!R_;#s0jn_oA$_B_Kciq)3,d$`qO - CW?BIRciF&mnpX5nb)Q=3,'[QEgMqcQs`gd.oFsp;>cL9#ok$ChQV"Ij+b:d?8JCLU,c_Xa - '?+I3lCk'IeB;_o%#J\Qri-D0'7P&JK$Gm*7:jpTes8o>ALis\tncNm`qD!=+tEsq7cghqb - >4SkNskUqqu6#:;>KM="aC,/)N/LZBm3`o&>b806N/6^AIJF<>LjA:\m - 6'ZDL@@RVLK>bUL0,S[-VKR:!]4[O2G$-Q^?6MGt=;iVc%dbo64Q]I5>qV:=#MLhL!<"/$::d - GCPh;)O0=Jmcj+!Fb-$(L,hQWkI;JeV]Ss`.BY5IGNT>q-4)bSX7Pg&'[2[BJ"J_nb6)hL< - 4_59aIZfMNnc2!HgG%K%/jiV(4kVt\(>+"=W?K_Db`fH#Nh,U@?/7f<4>i!EELF.X25j3c" - ZT^`Z29Eb]2($o#C6OLW(7=.a2n\S@Z-ibTY0I*%0"qkiol&Z96?Nbab]!Wj<`^F3(@8a"_ - X=3a4:n#]lLja4',F[_lPa=V^'Y@norm'BTj3JiSjYj)/o7It\k`LbQjMF3aOa/'K0+_jjP - m;q=drg!bd<@:/:6+^n-Ec1/5fc[Ap>+fiJM:n)OWcN.Jb=W?LVe=>7Dg44st*3q'<$)ooc - DfROeC^Y;NB?jn&E\N8b$ZeXFNr6A!r-a*_6Z`XsKZos:5H[&r*07'ZP*kjfCCa.bS8>_.L - 6s3)%9p!Af*pfgkpc$XF)Uog1NI)`(e/rP-EC]QJ\*%Hk+#qbRDIeAprt7e/FdWoA!3n0,r - \K3If7mXp<9%p-I(7$94#aQ.n3dXm.0Tuqn@>C?GIQW-4rUV;+^?PH@PA]ZZ9ZG[V==ia5< - 6,d,f88(HQV"9qH:(Z(nRRd*pUukMJSX]UH9tN/o`WMj%(s;bTe88X4q`\adt7P&Iolq.>h - .8\%82Y?5t%gbS]_Rgcq$V+]pQ8YrXuIA'F%M3@cq-Ru=XNt[$0?PV]lmuQ=%]HO[2eYlqa - APHb/*VJ94YBdg0]n=)tcTQn;VkU0MI/g#m],UX=Yr6Og-b10,DG:'Yh'@PE%WYAA4h%fcdQp.RT%a7-!r02%d$/GQt(b/mG7.q*#WfO#BMRH#]j`bCP+](k$S\]?qq9qFS0`a%d$/ - SQud8:e'Qq_oT:j5F"di?cHD_#IQicPR"IDd'=9bO>qIBn`Z34=*6];53Ro_3L.B6'beY:C - )*$Mc:1mA#iK5i"F"di?#0pAr3NlelqR43[beY:C))q`CnU9]4s1CR=4E)tZF"di?#0oJT3 - k@CPNf5ucK/o;TE6^.-T[5_Tk$S\]@*ZPJh&Q6j9(BuiK/oOZG_&U\8S: - Pb(S;*%.H.4lk5NZ>`k$ZKfk*.n)?hIn=k!XZcF4$BHNBDN>3LGFZQnr\8#BMN!0F)[5D(l - 390F)Y'c37WOcCpa;#<&@u82EiiOb;GsnZJ@FDn\*jWML3V'OE?+o@D8\ - 9f_ei+B4K%QoA%T.X3C,&2.QW@osT-)J$M*5@!^tUR7;Uh.0l9.3Ur.&GPl9pcOB^8qO`dM - 3C7L#[I:d#SP*>&<$fLV1,@=)U85t>\bq?H=/`c`@`?[BIVum&UdS9ncGqqJ+(Z<:E;B6sC - $9@e06H>):/>5X:Am>mF_P?"=]:_sV+me,\!'d]P['_pQlKX;m];7J'.7?c1@Mf^f8Ye/4X - PK6Guq2\qPmOsW?aEC>)CR/B/M_RkD>Aic+J6mp6<\PPKf$-O%Cou2;Im[\]D7"jO)\Vjc/ - A5NFh6polg$$pB_YuKanI/l0[riWk,=DZF/,fQHG)NMJord4.'oKIs\m7s7"srm&AGm9fHl - lcEr('pCH:n`i4()l>)%nQ%lI^5&A1lQ*a:X)B1JUr7^Zjc]qTe1O%u?g_o6#(+%,*KmtI3 - _bN%YW+r;pj_A+GDg24_rPFS?iT=ZIUM*D&CR&jIT%n)C*6L#9G-mKW+ROa3*ftS;&]6^cB - 5I3BQ0W,EWf^Id[h^sgk%OQAGF67$q7DBGi(EKgnS[4RqW!jcQ2]Zj1^nFZ#BMR(F$_gQK/ - oAgK/oU2Vi(dkp[U$fa#QAo'T6d6(]U< - 5>ccSEdOt&OU-5RE_@?cM/g*I!\%;a=gp#jilMJZZ9]HoZs$,=WSs/9t5Z>ol@NOhV\>3Dm - +T^g1sb?jL`Es2Pnh_-0`"Kf;Ul0P(7WRk546d0JBsr3W%k'XH0^3A$;:k)#qm-k&pE@f,+(\V^eA<46T6gC6Vmem-QlKZ1)m^W3j,lk - 2W6g.*(9!ad4KdPQ3]\qJr/$TWpQ*)(Vf^8QdbX'-/[R(2lIK_A])'"F(48Y3c+LQan#JXS - >Gd.8#2b`bmDa:uQW07bH4!pm0#Q`mWPthCY(T2EU1"@^$30k"IpK^.nWKG9b^40\A+R.RS - $uB@X](D6jf_U.U?$TBrihD;So4m#qICo,p1`5oY0"k_Jeq+G,KmNi]ig4Ar79t829! - Zn9h>%5G!lO(A.E*`U"`a'dn`E!LlhNt2(ah\t@_&U[=E\I`>0F)Zh*6c5=beY8H - #BMR(F"kYV_&U\8*6`DC0F)YQ#BQCTbeY:C_&Og-F"di?0ZV;O*6c5=b].YW#BMR(F.=5o_ - &U\8*%**8c1/5fIHGcrbXjEZN\-&%G0Kj>Y2:hdYu'(-:TTqiFj?L/Apo(1SQ"8,H]6()&M - @%S/"\g:R)>G*'[:@UBFloB(PWpJ\(=<-c<`:G8i@s+&sJ39%$Q>Aqk*jnfTEcdoB*eJs+r - \,;!GN7Sl7Wq.BY@"Lc0ac,aH&TjA&8fIHLIbAhcU7?WoVk4d^:FNRh.1;Fbq$7upEqXjno - u2G2$TO*sn"`L$7))C>\L;L:e^X](D'MWTerGbV":Sh@-H:iTcpq\!>%7r`BfpNC>Kpml8GZ - b*lL>br)C-a_;_--=p0eU"C0HFpOj6,ds]mRr41*lYO<3LGFZ9YaM1)jl7UR"IDd_ZUsu[=L?*K/lW@;-Y'-K/o=:"<>CgJN9*M3D@4c>T9&_3LI\e*6#=:3LG - FZ9YaM1)jl7UR"IDd_ZUsu[=L?*K/lW@;-Y'-K/o=:"<>CgJN9* - M3D@4c>T9&_3LI\e*6#=:3LGFZ9YaM1)jl7UR"IDd_ZUsu[=L>j - YH4u[!mTurl,4Pge>'N3HMVrp~> + Gb"0WGB=Pn\c6Z%%44*ud^u(.KMYC`,R3VR5&gnFp9BgFm_na4)sko\Vu^)(,`76nLgk[36 + P9Z$5sk2j#t_(uHIo;7Valj8d58Y[Ur..*%#:"!A&,^uC(f:Bbi",b%L`6$jJ)Ci,eA*1$"+N#!#6)C[pk'/9 + n-As.+,m.L5O\pW?pkJ%JNrW/,AQ-Ir..'L;$2=+&,a8")YaDai"&<,^`W=RIk[T$rkKWNK + +U!1#QDL\N2V:ln-Ar#6k0'F5O\n&C>]X#JNjuK"+T5>r5"1-p]H$(%n>'U4DH$&RNFCCUJ + %AZ^8AogM?k[i'UuSi@73U.K$8?#o!iE*$?QHqo!S9>L6B]jb)4m[rHF^qM'-E9hue + JqHlgBaNQmIF;jpcTF$ir5(&.P[(I))PMnUD&CZP%VDpgH?1"I;M.^SQ0ga@]@*DJT+pW + d(H6ur1nmLQrN89Fe,r4i8[:3q#8AWb/"$2S15cn&56Zf["d>o:/7g&-s-KIZ5[8l0N]'#3 + OMl=n],;U-T1?-L#Rj]:QQ)Bu^Y>p9$md*r&FCi6>sTBrTEUs^Mkr7Q;GCl%/d^A%;l4*t# + "?V,ce=rF_gH/ruIA9RHYB!d@t'XihNK$X\e0!01jFEb@V`lU*Soh='WVE_1s[$otmQ9VM; + +8B?("+T5>r..),#6)C[p]H"6%K(cAn-ArK)YaDai",_t2=LkM^`W=rC>]X#JNrWoeA*1$" + +T6iW.@2&#QDL\;$2=+&,^uCU'CV4+8>qf6k0'F5O\pWL`6$jJ)Ck8&N0%_rkKWN,AQ-Ir. + .*%8(>r"1flpd> + )Ic%%Vf3LTX!WA8891c*#qB*OhhfX8cI:pDpYVr!QCbh#e54l!#M5S[q")B4l-](# + Kc(o,Y-G.@[TZn5j]#`i8M*TK;/kPrd4JCQ[g4(j)h%gs!i1#r^ur9>/R:Q$pmfg=aCS#i0 + )>:d`ugr9Flq^[Jj,SA4EkZKX4]eF=%'1CFEs'kS22P5=N;Z.7j9`6__]/tAQ38TW\q?;\X + Wp[;klp=bq#8%6sl1e1%MDMRN&X,>$Zc@PG-BC^[utWH<4*S=O*;/(4$2H*d_65K>4%5?:qb + +n2PuX,.roqF:G>P.SPE_*ej4aGe1Tk?%TGpn.P/l>P*4Bl:$@fO8+(L3jGtT%@pBK)(!uo + jQEUe[t^EmehC@p?:bpPFJRbJ+rpqGePla@kafH*ePg7:7/J:g=4qW3PE#ieb,9[X5[,`/o + Qm-k0'5W'0T:;;[F"1F9Gc>_4UF[>jJ"sP77HG/%la^(39--=\e-8c`K=Eo$8]_9M6L?j*\ + n0+X=WiE2"TL;tlJea[(%<,r..),#6)C[p]H"6%K(cA + n-ArK)YaDai",_t2=LkM^`W=rC>]X#JNrWoeA*1$"+T6iW.@2&#QDL\;$2=+&,^uCU'CV4+ + 8>qf6k0'F5O\pWL`6$jJ)Ck8&N0%_rkKWN,AQ-Ir..*%8(>>=%07fbK7Nsj7GR3GKS]c^[]4T=T4973Kf + Dq4Fa:1mK7"iB9N:jf)fe10)h(<159 + sO^L,+DOsBZE@1>e-X&efO'M[>@t1-t&@4)T@tsCg3m;@V-TZj)"!'=YBKl%dk:fP\ba0XQT,eZ==;3N`]B8LF;$M"^0N)!q^(tm.l0a-r + ]Z';]8s;'dE"OCbG1BW``d!H.VWuCHKr&&I=Y=e-B?B&h];XN#=gu9o\n1.l)LX#FC0/tAm + Lt>53qk-Z]BPAjIU-C7gNaZ7@#qVU + oue/.8tjDi>mk#U&6df!I;709*>mn^4&\?VlmGn>9!Bs+?M.O$\.I+ER@5/X[rnMi",a:5=5;4cmKIb&,^uCn4-f7b)Bm7 + $iGQ?n-AsjnGZ6Aoel4DrkKWN,K(\(4Wao\5O\pW^`Q5Ee"/4(^`W=RJ)HA6r1QXcp]H$(& + ,[%!:RRoXm=-eeJNrW/r`I76Vg^96rkKWN"+Mlb:Plf]NVDGk&,^uCnAfSK'Y!m">2^"iJN + rYEUAhOi?G)6ommLbW7V,)0MUfX+`&PDXi9cqfB6&=unG>Rs%#$q>k0G](r*kmLf:N@u'Bg + ]`NcjLN,IZRZ#2^B6o'F+iBE"_C[dHq6?1jEZ0f0`+NCH[?-:2FS`tNo]U\6(Q3m+;bO#R%4Q%2r5"AG5h96q9M-[p=46b4M7:-HkdGf + oPl"kVr^2:NS42)]OM$=s!3Brq6IrmWgTOg.:cVgMd!E*Db4TNUkRk&X^HG0.,,Bg&osi,= + lUkL)/H1>JOV?W3p%d5m-jiq/c4F*-T>8$GSaP:$-#PT29LhE1YlRFF#?+.VD62#6@cJua! + U1@`b]oYUP]07fSW+6-.n%/ph2jNiG)"e8cpTr/qY55j"0Sp,JZ)*&`LKO7ZVrQ3uYdrRb- + oHRPQO?]8Tq2Th%aSS8;61F(7<>[7E&,]:>nAi]MU$,VlrkKUE#(N`3TC4Fai",`_;Xha'r + V?F*#l_U],mAd7_Op;R#_PA27nLN=S+)2JBPN&^n4-p'+'\95]!`OV^`S76Ir*)rU7.gRjs + Kj*J!:ZFYn_be)H.+CJNla6rn*;s`APX,#R5e:O(*]*2WPB[CbW^JpnO57O)=\kAgi9rn4, + 4L+5?@aW2<*eVh75'7iB,bS+EOG?NP`8O"u;O2WmkRFmd/)L[YQJ-m8:pF8k6"jWp*:_&rH + IJDcJ?qK'@.3rFOn$4-X`U-FK[M7^_cW'=ATY&\t!4o@Us + &H&e1J#JU;'UCst+gZf50+;(+Bkd@jl$K;Ni/\iTZ,^DHa)BtuJB;&G%\*DWQ$S>:Z/fDPf + mi2Tr`&&+BG.K/?+F*[1SZ]m=rOMt?L!$3ro`(p\XXFi/t[>/OY5,pf:qR5h,"+TYg=o + @OHdVZYcGZk/Cn=(Q20g9)Z*hGq2Qt%Sc3$#S[kXti>]#Hd7^i6;.eMK*?aQnmVVod?KE2" + R&QR*_F3c[(A.!706h"f1c&rOqnb9ZA/6=Qg[^JBbD:V"Ap?, + +[hEFU3!%fa))m^20,553)2lg5kfhO!mke?)>leKU=6Xt)fB%(J-PT_c=h-aE;bfsV-RLcabN1=N]LUAdPnX']_P>S@M%A-8eW]p)"jCW&&r'>KCr36HU3qP$er'?2Hr`Fe;a-)M"+(U/C + +)CA$MTCU(5KM]55OJ(j[63!6rj;\q5k#"--MZ%bJ+haTCJfRV5<'+%OM(gIg?HquK)T=YI + uYluLHil:Jj8Ypk9jJ`@SqX/s!])cK+o!sBr=2A3'J"H)f8H%_U??[&$i7o:](*1N-/\-P? + aE$39fprr+Y=1_.%HC%oPr=q@g^nqelESqGVDt>^HnR)ZG$#k=a,RjYT24I_a3Y&q*?C2NO%Jp>;R + qWh6k0C3ci?d`^)Qo+O('D-"WV*h-AcS)6rTV\XM=E,!8=6(W^`lkmV(V&_5^;"Yr4Z#;'( + 'qIJ54$VWIV&QiGMDS)-'rb2UF2"p + 4nqdo^qdO]WZ\Zh!f$i?+4u/5.c'4kJ3^-s!aihgP9lpm1+W-^fp\F7fN%$lhA\UGMYOQ2Q + ,1$^:kb:"$NYQg5CYcf2iIO=p$,TQKBsu\dij31G`T)-59>Y&n-@WBn>AYQFEZqs^$rVpr,GAKK0RG + +Jj1:HLY?rrTB6!EG$a&4Iup3(Ilcl@g.Q35lAsOS0DL1d,l#hcJ,X]jNu%D$qpk*'#MD]< + Vka6Mo,hUBIlnB-i=E5Xi(llAl/pDGV(BT+r]Kj*0_g87'E%*n5?)0*UuhMO-`Gu7ZBO4-" + Fmjeh/56&<:MI5KBslY,Jg"KiCbcccVr_QLHe5>B8>9rj;V9+S^R''E"!N%i(ZJDE3 + 4#rWj)56&H!J8FpKGm9Q%LVsTNF_&oJ?i/]^E2F_VlS3Llj((U+l'GLD,k0MhE\X)ibm>iC + ,+/G:^O#2@cE;U64o>``F(&f>&#l[BdkKWsD>0`bK56=RY/.#YbIuj_`5LTIln.EV-p>=OC + \,!"K?1T7l*h.HTST&lJhgn!_CbkkYc@L2rqYDd=ea=R+=P`]5M9\h + l)e_RELRc$-@:R+GD*$qT"'_Tb?o//Efklr'>Kfsu$iEa<#ptdejE]'``aA%q5`mV"q@g`_O_QitUYZIulerJ#)m-%_s1]K:'IsC&Y%EeaNhC+ + D1+5RQmC#T(!!a+D1+5RQpTC&,\`ki/]^Er,;C8d%=G8KBsfW,L)joGPC+qGPHfX_&mCj*5 + $dJErP"gccAk55LTHirnod6JNnr.r/p+P^Ae[m&3oq7c(FLLE6J'0huA>q,krV/TD6'Dp]H + !W1\`32T?b`Qn!2b9TKi5MccA;%58*nYWTr.[HogO'p:ik9P$J"=ec$@P[L\MY&,-#ikWb5 + MrM/HP^]*s\L'Yl?f_6-Nm3a0,,cs6/**QLi-ShXsJ8[/.-( + X:4D:pk+5)J#)lr-Y/8!r)(G6i"*,Ki/]^EejlpQS3LltDX[na6h!U75CPQi/E`'T5n/=*4 + Tp81q%K2$3#T@%'uHgDXhRlp^mEI,mAcZl;(LEY">H]QLi-Ad0'@j3skn.e=Y, + PUF"jIIXg)Nn.5F_8dbQ>e=Ve^GPIZr*V-,q&-'EeUnlpE8,ZR*[E>6CG[9om5KHTO599DE + n4oWFrFt:NO22p6Yi&<4r1TH-YoqG7rj;Wn2rG3E=R`4UrX@U;<#RsfoV_(p^;D%gYi&<4r + ;"fu5?ZL.X]N!K+(RUPT+:\'-%:b@rRlXB48_D<'E%*V5NafJ1&]c>[n6-L:KoXD^E\Lu6a + dNimb/]\+G%YucT;Bg@JXKaqsQL8r3\chj%oK[l^t]3Wd*'(F%^KD]BT(>!cJ;rNEA6_EtA + Ah$0rZY]N0Nn\THk"rq':dT5eNK/V,mpZ2Y8g(l)@5DL,kTVppT'9<3TUgjuS&Pqdlc6YenPkV-H^>h4JoURtgI:-^.cMS,"4#qg6,UGMMHE2AdEU,uA + 9!3qbNtL4nQChC\=i=sC(5=ErI=$1;Tep,CcRDYVVm"^qma"t^?SVLN/k\BuFSJDTOiJ6%) + FVnq:G!nP`q?Y'B^a=^Hm`m%j>Ue7q]PZd"@f:2iPVapWoqHo,JQA8jg)s=Sm5`3l-E^4rmdbB*)AcT*dllV;H!"^Sc8EAoXCb`V]VF`5KM]55OJ(jW?fT[re.)&^`R* + *_4PoIPH2s9W'B5O_r2-3\)<<)piA:VBKPa85=eA^+)CB/e:U]h#MEPT`u=kirssV;%oQ

@2a:;W.re.,'_&m3[_&or]*)/I4RF/1@I + DI#$]\niQIld5I48_AO$iYtTVaKpc_]P\Yi/^ui3qU]Zr':YtrWr/Z7.Q1piWF[lP(Gn"r< + Z4,L-NbNK0KG9OmqiDIuohF5GJ(mD$Qiq-s'VgIn#-%?9pKq.^VnumNM + FbS%sC5R&3EtU;9=7WNL.(k[>R3=/A`(&GP"c*iC#%Y=6Ha43 + +*h.qhitVjRK,t-S[9:8hNi`WlgJ?ko-!33[]0tU9fb;OnR8u9^h6"PBloekLT*IQfjul5B`>Drqp + HYO!a]tS/a5.P5cfLNUn1b_3#6pdR2+_jAO+NuJ#MA$Ypj5EURoX$?%pI\/5"=or\)SoXa4_6pk/3[R%t"L;WhmM+Q@ + N">hP8;1I"DdbkZ6Z1[%9/c'N?ru)+apk,Xnpg]6]gb62KJ)H@hJj6^-:O7:Ir..'># + l`JH5K,M!R?a30#Q?+opg\CEBPY$F#Q?+rpg[P-PT!0YY6KL7O"u;O[cJ%uLVY)-UR9u#%J + `qu.r'@Z!e9-h@Y-g&jH$H=pZU!Lp]H!['E#*YIi=dpi=Gi`;Xh_-r2H]uC2%C55O_07_&l + nOVnTDWrkKV0"b1@*T*f'sbI7S'i/a%Q5;qI2rH7$/r)$eia85``1R,ip_=XXDli&u+nAi8+_&m=]r]:ek-BKAar?b-SXRFg9O1ZR)((>\a[.nA.L]/FtP$OX'W + M$+2ls\QEd.p>[0&a/gEnTc*?LP#]h-]!1dfB+CF;Bu[SXhet'7NZLp=Iik/)>DO\TIf?VB + 26T,Z?;>VQVsLGMEaq,3':RVG + rN/_BG^l<\+8Jji=Nde[WEu@MSBF$=+o;>=3),o1[):W'hO]";-7=-3*kGip&(_l9<5"[8T + -(ijHLTnj6iCK701L5J)X0C_El8bWHo]2-]#;-=a#Qe1A+W7*Rsmd`7JXNB:7>olV6Q3IBO + QreJ$^QiP7Adt5L&l3g>'c.aJc0g:9h@7oKVcGYM]3TU#0p;n*k[G.tg.\k0MEA6V$NSq*R + dto#DonJIHOUFctP"R#Sg=Hja]sBaM_7DX3Rq[d?kITqo5q)?.Dnp]H"pG^oAYqDJfbJ)Ck + 8JNjq@Vp&9:kaT$[^$l:+^`W>]+b/bqle4N.r..*%#Q@iB+8RX?l9X>=r..*%`tJA.[QOL% + 8+6c_^`W=r]0G#\-beaRJ)Ck8JNoIlVpnO8YeV@U"+T7T&+@hCJ*[Sni",a:5F1f:4[4+E5 + O\pW^`V8.0n7pQd1\b3%"I1Gr..(uo>fKkm*J&V&,^uCn4+7D.V^IpH4]iRJ)Ck8Oi8CK^L + @KJ^`W=RJ,'#MrB4*S"+T5>r3;>J&tZbo&,^uCn;$I.M8#dbV$[.:+8>qfIX$?5!6GIs5O\ + pW^`Nm3MGJA#2uVh>!!!*\l5ilkl?LOrM)UoOS!DF:rK*]QmH2-HQj,!Oi.*qs + P(^U.F!c.$:(W3*#"X5p'6Bl=dD4IL4F-oHfZBNjU9O^J%VfH:-6i\/O>S>j()gWgb62[ + Q6$h30S35&B%6+EeUKaXf8i^W/A5!EkgH+]3Sdd`2fe*&iSMKLHh?dihN&Nd6ruOdSuSC[T + 8ZVLg^!aflVCjZI$)kc*\U1sR/Hg;f3d;0RcL@7R8,c\3k/O:-U8rT8l0B>[QV;J4gQUoUG + >2W%kAaal11:3Rtr2!D/>f?N02;UlFL^SLm,P=J$7oSX^7Cl37F)LWq39)p%"/Bs569PRJl + Yfq.&7n=Gd%(&kF'n4C=K:5O#R=s3D6O)ko9iYGfPSagib.DS*>/XWf?lb%umpdOWd(=q1C + DcbLXt"+T6iW.@2&#QDL\;$2=+&,^uCU'CV4+8>qf6k0'F5O\pWL`6$jJ)Ck8&N0%_rkKWN + ,AQ-Ir..*%8(>r + "1gU#QDL\p`jP3&,^uCn42!D+8>qfi/agf5O\pW^`QDUJ)Ck8JNok5rkKWN"+N^u/H4-Xe] + [&M86X*foWt(5AXltMIc[2_s7u+2s3?H,)Ws%sR+F"n7!`1Wkm4D;0XIeXG6kJQlM`Gc]7C + -`_&e4VqG/A"VX9ekBklSU%5AgW'&6aP>pZs+I`pKIrs54GI;,M2c^bMI_=?a!6T9d2h + H1EnhbaL\VDi]>NJP;AhMRM@YpEkpb`5,2KYH!,/tI)jSG#'g[skV8/4*FXK=JDP1Wq=San + e(:]T1HGfhn^=k4Qjq_,jHmK-q<%Z8hq7s`jF4H(2j@7BP@pVfq\==sdIPEF\ek(tZ+2$3> + 1f<$dR!.:la1ent + k;b)ZdO4g&]3OHl_*4OTIeqK;dN7628UQLt,i>LUuke_8o1S%IY/tWpNF?a + >+FGRXZm;(cc7l<.@l4(`;,H'7Mg0AK48m+c$Gq--6.49qEKD,jrg372eRYtr^?!+Ka8p[N + kXT%eGQZGOVc4/nN13Z%JJgFhuQn8YN[/SL"cRYUT$1=>=in]48K&\\-0tB$/U3!&cN`&5Y + M7uB(L6=<%=]7slo:opoP7uM!5OcbR&)DR(-9VsZa]NK*Y0'Kl9/c75bNi9YZ2Jn@'1%dCE + ::U!4*q9qPu0%l&RVo$RN45S(*:]^LWj*F[[>abd"Ph4r6"&WLH7`G8JSEiPBqr`\Wh/l)6 + I5dOCEVm-PDB(/kgXr"VZ;Qg=O+^p]H$(O/RUon-As.+&N,ii",a:5GAA]^`W=RImabDJNr + W/reMKf"+T5>r"1gU#QDL\p`jP3&,^uCn42!D+8>qfi/agf5O\pW^`QDUJ)Ck8JNok5rkKW + N"+N]Jr..*%#QBMtp]H$(&,["sn-As.+8@'qi",a:5O_1m^`W=RJ)HBdJNrW/rkKUQ"+T5> + r..),#6)C[p]H"6NOASG<15(b5KUt>h:]:;c@3kI6$mM<+[&joX]TRLH:6-NaQ9a[Ae@>1J + nNC;RC=Sd&[MWa$eATgkC1C_JH[FQbH>L+\GfaW^.=[j0sLR)W[WNa2EGmS_o.>1D5b^Yc6 + 5=lPWdrh>+U$qjlD!)#<84A*4g?M'O#]ph3n^e33Bm(Q3a4]bC\(EbSHgVr)rf-ba+i+\hS + Nk[]A.FYBY).:N2Y3ruERbRqLT-a&!tjf(BOq+8B?X_fq&oDIKj;,$.cG?LF.sn8^&fuH + 5BI[KhLh3C=^=s5;e':51[0c+`C9VI:3;.UI3Ca_jQZ6FC%LPgB9oaG0@?+celFPG-Jj^k7 + _2aP2ficj6C0p2:L9")&)G<;=[hZBoqVgK]"\n!r!I0GEj`IUb_U!)psIOt/a\pj)9N51d_ + jRF@R`9spk,i+JNrW/ra8,'r..*%#:"!A&,^uC(f:Bbi",b%L`6$jJ)Ci,eA*1$"+N#!#6) + C[pk'/9n-As.+,m.L5O\pW?pkJ%JNrW/,AQ-Ir..'L;$2=+&,a8")YaDai"&<,^`W=RIk[T + $rkKWNK+U!1#QDL\N2V:ln-Ar#6k0'F5O\n&C>]X#JNjuK"+T5>r5"1-Abr12,(DH")B&M4 + iracbehbn;:JJNJruL\P3X0IiO@H/,Q`&$MfB"e5JkGY]k0;:u&OFE+,M#(;2ua=Q<)sJ~> Q Q Q showpage -- GitLab