diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile | 4 | ||||
-rw-r--r-- | src/pencil_build.c | 272 | ||||
-rw-r--r-- | src/pencil_internal.h | 73 | ||||
-rw-r--r-- | src/pencil_save.c | 533 |
4 files changed, 882 insertions, 0 deletions
diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..99d4a1a --- /dev/null +++ b/src/Makefile @@ -0,0 +1,4 @@ +# Sources +DIR_SOURCES := pencil_build.c pencil_save.c + +include build/makefiles/Makefile.subdir diff --git a/src/pencil_build.c b/src/pencil_build.c new file mode 100644 index 0000000..460b0af --- /dev/null +++ b/src/pencil_build.c @@ -0,0 +1,272 @@ +/* + * This file is part of Pencil + * Licensed under the MIT License, + * http://www.opensource.org/licenses/mit-license + * Copyright 2005 James Bursa <james@semichrome.net> + */ + +#define _GNU_SOURCE /* for strndup */ +#include <assert.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> +#include "pencil_internal.h" + + +static struct pencil_item *pencil_new_item(pencil_item_type type); +static void pencil_append_child(struct pencil_item *group, + struct pencil_item *child); +static void pencil_free_item(struct pencil_item *item); +static void pencil_dump_item(struct pencil_item *item, unsigned int depth); + + +struct pencil_diagram *pencil_create(void) +{ + struct pencil_diagram *diagram; + struct pencil_item *root_group; + + diagram = malloc(sizeof *diagram); + root_group = pencil_new_item(pencil_GROUP); + if (!diagram || !root_group) { + free(root_group); + free(diagram); + return 0; + } + + root_group->group_name = strdup("root group"); + if (!root_group->group_name) { + free(root_group); + free(diagram); + return 0; + } + + diagram->root = root_group; + diagram->current_group = root_group; + + return diagram; +} + + +pencil_code pencil_text(struct pencil_diagram *diagram, + int x, int y, + const char *font_family, rufl_style font_style, + unsigned int font_size, + const char *string, size_t length, + pencil_colour colour) +{ + struct pencil_item *item; + + item = pencil_new_item(pencil_TEXT); + if (!item) + return pencil_OUT_OF_MEMORY; + + item->x = x; + item->y = y; + item->fill_colour = colour; + item->font_family = font_family; + item->font_style = font_style; + item->font_size = font_size; + item->text = strndup(string, length); + if (!item->text) { + free(item); + return pencil_OUT_OF_MEMORY; + } + + pencil_append_child(diagram->current_group, item); + + return pencil_OK; +} + + +pencil_code pencil_path(struct pencil_diagram *diagram, + const int *path, unsigned int n, + pencil_colour fill_colour, pencil_colour outline_colour, + int thickness, pencil_join join, + pencil_cap start_cap, pencil_cap end_cap, + int cap_width, int cap_length, + bool even_odd, pencil_pattern pattern) + +{ + struct pencil_item *item; + + item = pencil_new_item(pencil_PATH); + if (!item) + return pencil_OUT_OF_MEMORY; + + item->fill_colour = fill_colour; + item->outline_colour = outline_colour; + item->path = malloc(sizeof path[0] * n); + if (!item->path) { + free(item); + return pencil_OUT_OF_MEMORY; + } + memcpy(item->path, path, sizeof path[0] * n); + item->path_size = n; + item->thickness = thickness; + item->join = join; + item->start_cap = start_cap; + item->end_cap = end_cap; + item->cap_width = cap_width; + item->cap_length = cap_length; + item->even_odd = even_odd; + item->pattern = pattern; + + pencil_append_child(diagram->current_group, item); + + return pencil_OK; +} + + +pencil_code pencil_sprite(struct pencil_diagram *diagram, + int x, int y, int width, int height, + const char *sprite) +{ + struct pencil_item *item; + + item = pencil_new_item(pencil_SPRITE); + if (!item) + return pencil_OUT_OF_MEMORY; + + item->x = x; + item->y = y; + item->width = width; + item->height = height; + item->sprite = sprite; + + pencil_append_child(diagram->current_group, item); + + return pencil_OK; +} + + +pencil_code pencil_group_start(struct pencil_diagram *diagram, + const char *name) +{ + struct pencil_item *item; + + item = pencil_new_item(pencil_GROUP); + if (!item) + return pencil_OUT_OF_MEMORY; + + item->group_name = strdup(name); + if (!item->group_name) { + free(item); + return pencil_OUT_OF_MEMORY; + } + + pencil_append_child(diagram->current_group, item); + + diagram->current_group = item; + + return pencil_OK; +} + + +pencil_code pencil_group_end(struct pencil_diagram *diagram) +{ + diagram->current_group = diagram->current_group->parent; + + return pencil_OK; +} + + +struct pencil_item *pencil_new_item(pencil_item_type type) +{ + struct pencil_item *item; + + item = malloc(sizeof *item); + if (!item) + return 0; + + item->type = type; + item->group_name = 0; + item->text = 0; + item->path = 0; + item->parent = item->next = item->children = item->last = 0; + + return item; +} + + +void pencil_append_child(struct pencil_item *group, + struct pencil_item *child) +{ + child->parent = group; + if (group->children) { + assert(group->last); + group->last->next = child; + } else { + group->children = child; + } + group->last = child; +} + + +void pencil_free(struct pencil_diagram *diagram) +{ + pencil_free_item(diagram->root); + free(diagram); +} + + +void pencil_free_item(struct pencil_item *item) +{ + for (struct pencil_item *child = item->children; child; + child = child->next) + pencil_free_item(child); + free(item->group_name); + free(item->text); + free(item->path); + free(item); +} + + +void pencil_dump(struct pencil_diagram *diagram) +{ + printf("diagram %p: current group %p\n", + (void *) diagram, (void *) diagram->current_group); + pencil_dump_item(diagram->root, 0); +} + + +void pencil_dump_item(struct pencil_item *item, unsigned int depth) +{ + for (unsigned int i = 0; i != depth; i++) + printf(" "); + + printf("%p ", (void *) item); + switch (item->type) { + case pencil_GROUP: + printf("GROUP"); + break; + case pencil_TEXT: + printf("TEXT (%i %i) font %s %i %i, text \"%s\"", + item->x, item->y, + item->font_family, item->font_style, + item->font_size, item->text); + break; + case pencil_PATH: + printf("PATH ("); + for (unsigned int i = 0; i != item->path_size; i++) + printf("%i ", item->path[i]); + printf(") thickness %i, join %i, caps %i %i %i %i, ", + item->thickness, item->join, + item->start_cap, item->end_cap, + item->cap_width, item->cap_length); + if (item->even_odd) + printf("even-odd, "); + printf("pattern %i", item->pattern); + break; + case pencil_SPRITE: + printf("SPRITE (%i %i) (%i x %i)\n", item->x, item->y, + item->width, item->height); + break; + default: + printf("UNKNOWN"); + } + printf("\n"); + + for (struct pencil_item *child = item->children; child; + child = child->next) + pencil_dump_item(child, depth + 1); +} diff --git a/src/pencil_internal.h b/src/pencil_internal.h new file mode 100644 index 0000000..49aabf0 --- /dev/null +++ b/src/pencil_internal.h @@ -0,0 +1,73 @@ +/* + * This file is part of Pencil + * Licensed under the MIT License, + * http://www.opensource.org/licenses/mit-license + * Copyright 2005 James Bursa <james@semichrome.net> + */ + +#ifndef PENCIL_INTERNAL_H +#define PENCIL_INTERNAL_H + +#include <stdbool.h> +#include "pencil.h" + + +struct pencil_item; + + +struct pencil_diagram { + struct pencil_item *root; + struct pencil_item *current_group; +}; + +typedef enum { + pencil_GROUP, + pencil_TEXT, + pencil_PATH, + pencil_SPRITE, +} pencil_item_type; + +struct pencil_item { + pencil_item_type type; + + pencil_colour fill_colour; + pencil_colour outline_colour; + + char *group_name; + + int x, y; + + const char *font_family; + rufl_style font_style; + unsigned int font_size; + char *text; + + int *path; + unsigned int path_size; + int thickness; + pencil_join join; + pencil_cap start_cap; + pencil_cap end_cap; + int cap_width; + int cap_length; + bool even_odd; + pencil_pattern pattern; + + int width, height; + const void *sprite; + + struct { + int x0; + int y0; + int x1; + int y1; + } bbox; + + struct pencil_item *parent; + struct pencil_item *next; + struct pencil_item *children; + struct pencil_item *last; +}; + + +#endif diff --git a/src/pencil_save.c b/src/pencil_save.c new file mode 100644 index 0000000..f5c23fb --- /dev/null +++ b/src/pencil_save.c @@ -0,0 +1,533 @@ +/* + * This file is part of Pencil + * Licensed under the MIT License, + * http://www.opensource.org/licenses/mit-license + * Copyright 2005 James Bursa <james@semichrome.net> + */ + +/** \file + * Saving as a DrawFile (implementation). + * + * Two passes over the diagram tree are made. The first pass computes the size + * that will be required and enumerates the fonts. The second pass creates the + * DrawFile in a buffer. + */ + +#define _GNU_SOURCE /* for strndup */ +#include <assert.h> +#include <limits.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdio.h> +#include <string.h> +#include <oslib/drawfile.h> +#include <oslib/osspriteop.h> +#include <rufl.h> +#include "pencil_internal.h" + +/* Maximum grouping depth (too deep crashes Draw). */ +#define MAX_DEPTH 10 + + +struct pencil_save_context { + pencil_code code; + struct pencil_diagram *diagram; + size_t size; + char **font_list; + unsigned int font_count; + struct pencil_item *item; + void *buffer; + void *b; + os_box bbox; +}; + + +static void pencil_save_pass1(struct pencil_save_context *context, + struct pencil_item *item, unsigned int depth); +static void pencil_save_pass1_text_callback(void *c, + const char *font_name, unsigned int font_size, + const char *s8, unsigned short *s16, unsigned int n, + int x, int y); +static void pencil_save_pass2(struct pencil_save_context *context, + struct pencil_item *item, unsigned int depth); +static void pencil_save_pass2_text_callback(void *c, + const char *font_name, unsigned int font_size, + const char *s8, unsigned short *s16, unsigned int n, + int x, int y); + + +pencil_code pencil_save_drawfile(struct pencil_diagram *diagram, + const char *source, + char **drawfile_buffer, size_t *drawfile_size) +{ + struct pencil_save_context context = + { pencil_OK, diagram, 0, 0, 0, 0, 0, 0, + { INT_MAX, INT_MAX, INT_MIN, INT_MIN } }; + unsigned int i; + size_t size, font_table_size; + void *buffer, *b; + drawfile_diagram *header; + drawfile_object *font_table; + char *f; + + *drawfile_buffer = 0; + *drawfile_size = 0; + + /* pass 1 */ + pencil_save_pass1(&context, diagram->root, 0); + if (context.code != pencil_OK) { + for (i = 0; i != context.font_count; i++) + free(context.font_list[i]); + free(context.font_list); + return context.code; + } + + /* find font table size */ + font_table_size = 8; + for (i = 0; i != context.font_count; i++) + font_table_size += 1 + strlen(context.font_list[i]) + 1; + font_table_size = (font_table_size + 3) & ~3; + + size = 40 + font_table_size + context.size; + + /* use calloc to prevent information leakage */ + buffer = calloc(size, 1); + if (!buffer) { + for (i = 0; i != context.font_count; i++) + free(context.font_list[i]); + free(context.font_list); + return pencil_OUT_OF_MEMORY; + } + + /* file headers */ + header = (drawfile_diagram *) buffer; + header->tag[0] = 'D'; + header->tag[1] = 'r'; + header->tag[2] = 'a'; + header->tag[3] = 'w'; + header->major_version = 201; + header->minor_version = 0; + strncpy(header->source, source, 12); + for (i = strlen(source); i < 12; i++) + header->source[i] = ' '; + header->bbox = context.bbox; + b = (char *) buffer + sizeof(drawfile_diagram_base); + + /* font table */ + font_table = (drawfile_object *) b; + font_table->type = drawfile_TYPE_FONT_TABLE; + font_table->size = font_table_size; + f = (char *) b + 8; + for (i = 0; i != context.font_count; i++) { + *f++ = i + 1; + strcpy(f, context.font_list[i]); + f += strlen(context.font_list[i]) + 1; + } + b = (char *) b + font_table_size; + + /* pass 2 */ + context.buffer = buffer; + context.b = b; + pencil_save_pass2(&context, diagram->root, 0); + + /* free font list */ + for (i = 0; i != context.font_count; i++) + free(context.font_list[i]); + free(context.font_list); + + if (context.code != pencil_OK) { + free(buffer); + return context.code; + } + + assert(context.b == buffer + size); + + *drawfile_buffer = buffer; + *drawfile_size = size; + return pencil_OK; +} + + +void pencil_save_pass1(struct pencil_save_context *context, + struct pencil_item *item, unsigned int depth) +{ + rufl_code code; + struct pencil_item *child; + + assert(item); + + /* Initialise item bounding box */ + item->bbox.x0 = INT_MAX; + item->bbox.y0 = INT_MAX; + item->bbox.x1 = INT_MIN; + item->bbox.y1 = INT_MIN; + + for (child = item->children; child; child = child->next) { + pencil_save_pass1(context, child, depth + 1); + if (context->code != pencil_OK) + return; + + /* Update item bounding box to include child */ + if (child->bbox.x0 < item->bbox.x0) + item->bbox.x0 = child->bbox.x0; + if (child->bbox.y0 < item->bbox.y0) + item->bbox.y0 = child->bbox.y0; + if (child->bbox.x1 > item->bbox.x1) + item->bbox.x1 = child->bbox.x1; + if (child->bbox.y1 > item->bbox.y1) + item->bbox.y1 = child->bbox.y1; + } + + switch (item->type) { + case pencil_GROUP: + if (!item->children || MAX_DEPTH <= depth || + (item->bbox.x0 == INT_MAX && + item->bbox.y0 == INT_MAX && + item->bbox.x1 == INT_MIN && + item->bbox.y1 == INT_MIN)) + break; + + context->size += 36; + + break; + case pencil_TEXT: + { + int bbox[4]; + int width; + + code = rufl_paint_callback(item->font_family, item->font_style, + item->font_size, item->text, strlen(item->text), + item->x, item->y, + pencil_save_pass1_text_callback, context); + if (code != rufl_OK) + context->code = code; + if (context->code != pencil_OK) + return; + + code = rufl_font_bbox(item->font_family, item->font_style, + item->font_size, bbox); + if (code != rufl_OK) + context->code = code; + if (context->code != pencil_OK) + return; + + code = rufl_width(item->font_family, item->font_style, + item->font_size, item->text, strlen(item->text), + &width); + if (code != rufl_OK) + context->code = code; + if (context->code != pencil_OK) + return; + + item->bbox.x0 = item->x * 256; + item->bbox.y0 = item->y * 256; + item->bbox.x1 = (item->x + width) * 256; + item->bbox.y1 = (item->y + (bbox[3] - bbox[1])) * 256; + } + break; + case pencil_PATH: + context->size += 24 + 16 + item->path_size * 4; + if (item->pattern != pencil_SOLID) + context->size += 12; + + /* Calculate bounding box */ + for (unsigned int i = 0; i != item->path_size; ) { + switch (item->path[i]) { + case 0: + case 5: + i++; + break; + case 2: + case 6: + case 8: + { + int points = item->path[i++] == 6 ? 3 : 1; + + for (; points > 0; points--) { + int x = item->path[i++] * 256; + int y = item->path[i++] * 256; + + if (x < item->bbox.x0) + item->bbox.x0 = x; + if (y < item->bbox.y0) + item->bbox.y0 = y; + if (x > item->bbox.x1) + item->bbox.x1 = x; + if (y > item->bbox.y1) + item->bbox.y1 = y; + } + } + break; + default: + assert(0); + } + } + break; + case pencil_SPRITE: + context->size += 24 + ((const osspriteop_header *) + item->sprite)->size; + + item->bbox.x0 = item->x * 256; + item->bbox.y0 = item->y * 256; + item->bbox.x1 = (item->x + item->width) * 256; + item->bbox.y1 = (item->y + item->height) * 256; + break; + default: + assert(0); + } + + /* Update global bounding box */ + if (item->bbox.x0 < context->bbox.x0) + context->bbox.x0 = item->bbox.x0; + if (item->bbox.y0 < context->bbox.y0) + context->bbox.y0 = item->bbox.y0; + if (item->bbox.x1 > context->bbox.x1) + context->bbox.x1 = item->bbox.x1; + if (item->bbox.y1 > context->bbox.y1) + context->bbox.y1 = item->bbox.y1; +} + + +void pencil_save_pass1_text_callback(void *c, + const char *font_name, unsigned int font_size, + const char *s8, unsigned short *s16, unsigned int n, + int x, int y) +{ + struct pencil_save_context *context = c; + unsigned int i; + char **font_list; + + (void) font_size; /* unused */ + (void) x; /* unused */ + (void) y; /* unused */ + + assert(s8 || s16); + + /* check if the font name is new */ + for (i = 0; i != context->font_count && + strcmp(context->font_list[i], font_name) != 0; i++) + ; + if (i == context->font_count) { + /* add to list of fonts */ + font_list = realloc(context->font_list, + sizeof context->font_list[0] * + (context->font_count + 1)); + if (!font_list) { + context->code = pencil_OUT_OF_MEMORY; + return; + } + font_list[context->font_count] = strdup(font_name); + if (!font_list[context->font_count]) { + context->code = pencil_OUT_OF_MEMORY; + return; + } + context->font_list = font_list; + context->font_count++; + } + + /* compute size of transformed text object */ + if (s8) { + context->size += 24 + 56 + ((n + 4) & ~3); + } else { + unsigned int utf8_length = 0; + for (i = 0; i != n; i++) { + if (s16[i] < 0x80) + utf8_length += 1; + else if (s16[i] < 0x800) + utf8_length += 2; + else + utf8_length += 3; + } + context->size += 24 + 56 + ((utf8_length + 4) & ~3); + } +} + + +void pencil_save_pass2(struct pencil_save_context *context, + struct pencil_item *item, unsigned int depth) +{ + drawfile_object *object = (drawfile_object *) context->b; + bool group = false; + rufl_code code; + int *path; + unsigned int i; + struct pencil_item *child; + + assert(item); + + switch (item->type) { + case pencil_GROUP: + if (!item->children || MAX_DEPTH <= depth || + (item->bbox.x0 == INT_MAX && + item->bbox.y0 == INT_MAX && + item->bbox.x1 == INT_MIN && + item->bbox.y1 == INT_MIN)) + break; + + group = true; + object->type = drawfile_TYPE_GROUP; + object->size = 36; + strncpy(object->data.group.name, item->group_name, 12); + for (i = strlen(item->group_name); i < 12; i++) + object->data.group.name[i] = ' '; + object->data.group.bbox.x0 = item->bbox.x0; + object->data.group.bbox.y0 = item->bbox.y0; + object->data.group.bbox.x1 = item->bbox.x1; + object->data.group.bbox.y1 = item->bbox.y1; + context->b = (char *) context->b + object->size; + break; + case pencil_TEXT: + context->item = item; + code = rufl_paint_callback(item->font_family, item->font_style, + item->font_size, item->text, strlen(item->text), + item->x, item->y, + pencil_save_pass2_text_callback, context); + if (code != rufl_OK) + context->code = code; + if (context->code != pencil_OK) + return; + break; + case pencil_PATH: + object->type = drawfile_TYPE_PATH; + object->size = 24 + 16 + item->path_size * 4; + object->data.path.bbox.x0 = item->bbox.x0; + object->data.path.bbox.y0 = item->bbox.y0; + object->data.path.bbox.x1 = item->bbox.x1; + object->data.path.bbox.y1 = item->bbox.y1; + object->data.path.fill = item->fill_colour; + object->data.path.outline = item->outline_colour; + object->data.path.width = item->thickness * 256; + object->data.path.style.flags = 0; + object->data.path.style.cap_width = item->cap_width; + object->data.path.style.cap_length = item->cap_length; + if (item->pattern != pencil_SOLID) { + object->size += 12; + object->data.path_with_pattern.pattern.start = 0; + object->data.path_with_pattern.pattern. + element_count = 1; + if (item->pattern != pencil_DOTTED) + object->data.path_with_pattern.pattern. + elements[0] = 512 * item->thickness; + else if (item->pattern != pencil_DASHED) + object->data.path_with_pattern.pattern. + elements[0] = 1536 * item->thickness; + } + path = (int *) (void *) ((char *) context->b + object->size - + item->path_size * 4); + for (i = 0; i != item->path_size; ) { + switch (item->path[i]) { + case 0: + case 5: + path[i] = item->path[i]; i++; + break; + case 2: + case 8: + path[i] = item->path[i]; i++; + path[i] = item->path[i] * 256; i++; + path[i] = item->path[i] * 256; i++; + break; + case 6: + path[i] = item->path[i]; i++; + path[i] = item->path[i] * 256; i++; + path[i] = item->path[i] * 256; i++; + path[i] = item->path[i] * 256; i++; + path[i] = item->path[i] * 256; i++; + path[i] = item->path[i] * 256; i++; + path[i] = item->path[i] * 256; i++; + break; + default: + assert(0); + } + } + context->b = (char *) context->b + object->size; + break; + case pencil_SPRITE: + object->type = drawfile_TYPE_SPRITE; + object->size = 24 + ((const osspriteop_header *) + item->sprite)->size; + object->data.sprite.bbox.x0 = item->bbox.x0; + object->data.sprite.bbox.y0 = item->bbox.y0; + object->data.sprite.bbox.x1 = item->bbox.x1; + object->data.sprite.bbox.y1 = item->bbox.y1; + memcpy(&object->data.sprite.header, item->sprite, + object->size - 24); + context->b = (char *) context->b + object->size; + break; + default: + assert(0); + } + + for (child = item->children; child; child = child->next) { + pencil_save_pass2(context, child, depth + 1); + if (context->code != pencil_OK) + return; + } + + if (group) + object->size = (char *) context->b - (char *) object; +} + + +void pencil_save_pass2_text_callback(void *c, + const char *font_name, unsigned int font_size, + const char *s8, unsigned short *s16, unsigned int n, + int x, int y) +{ + struct pencil_save_context *context = c; + drawfile_object *object = (drawfile_object *) context->b; + unsigned int i; + + assert(s8 || s16); + + /* find font index */ + for (i = 0; i != context->font_count && + strcmp(context->font_list[i], font_name) != 0; i++) + ; + assert(i != context->font_count); + + object->type = drawfile_TYPE_TRFM_TEXT; + object->data.trfm_text.bbox.x0 = context->item->bbox.x0; + object->data.trfm_text.bbox.y0 = context->item->bbox.y0; + object->data.trfm_text.bbox.x1 = context->item->bbox.x1; + object->data.trfm_text.bbox.y1 = context->item->bbox.y1; + object->data.trfm_text.trfm.entries[0][0] = 0x10000; + object->data.trfm_text.trfm.entries[0][1] = 0; + object->data.trfm_text.trfm.entries[1][0] = 0; + object->data.trfm_text.trfm.entries[1][1] = 0x10000; + object->data.trfm_text.trfm.entries[2][0] = 0; + object->data.trfm_text.trfm.entries[2][1] = 0; + object->data.trfm_text.flags = drawfile_TEXT_KERN; + object->data.trfm_text.fill = context->item->fill_colour; + object->data.trfm_text.bg_hint = os_COLOUR_WHITE; + object->data.trfm_text.style.font_index = i + 1; + object->data.trfm_text.xsize = font_size * 40; + object->data.trfm_text.ysize = font_size * 40; + object->data.trfm_text.base.x = x * 256; + object->data.trfm_text.base.y = y * 256; + + if (s8) { + strncpy(object->data.trfm_text.text, s8, n); + object->size = 24 + 56 + ((n + 4) & ~3); + } else { + char *z = object->data.trfm_text.text; + unsigned int utf8_length = 0; + for (i = 0; i != n; i++) { + if (s16[i] < 0x80) { + *z++ = s16[i]; + utf8_length += 1; + } else if (s16[i] < 0x800) { + *z++ = 0xc0 | ((s16[i] >> 6) & 0x1f); + *z++ = 0x80 | (s16[i] & 0x3f); + utf8_length += 2; + } else { + *z++ = 0xe0 | (s16[i] >> 12); + *z++ = 0x80 | ((s16[i] >> 6) & 0x3f); + *z++ = 0x80 | (s16[i] & 0x3f); + utf8_length += 3; + } + } + object->size = 24 + 56 + ((utf8_length + 4) & ~3); + } + + context->b = (char *) context->b + object->size; +} |