~ubuntu-branches/ubuntu/feisty/gnumeric/feisty-updates

« back to all changes in this revision

Viewing changes to plugins/html/latex.c

  • Committer: Bazaar Package Importer
  • Author(s): Gauvain Pocentek
  • Date: 2006-11-14 14:02:03 UTC
  • mfrom: (1.1.5 upstream)
  • Revision ID: james.westby@ubuntu.com-20061114140203-iv3j2aii3vch6isl
Tags: 1.7.2-1ubuntu1
* Merge with debian experimental:
  - debian/control, debian/*-gtk-*, debian/rules,
    debian/shlibs.local: Xubuntu changes for
    gtk/gnome multibuild.
  - run intltool-update in po*
  - Build Depend on intltool

Show diffs side-by-side

added added

removed removed

Lines of Context:
7
7
 * Copyright (C) 2001 Adrian Custer, Berkeley
8
8
 * email: acuster@nature.berkeley.edu
9
9
 *
10
 
 * Copyright (C) 2001 Andreas J. Guelzow, Edmonton
 
10
 * Copyright (C) 2001-2006 Andreas J. Guelzow, Edmonton
11
11
 * email: aguelzow@taliesin.ca
12
12
 *
13
13
 * This program is free software; you can redistribute it and/or modify
132
132
};
133
133
 
134
134
/**
 
135
 * latex_raw_str :
 
136
 * @p :     a pointer to a char, start of the string to be processed
 
137
 * @output : output stream where the processed characters are written.
 
138
 * @utf8:   is this a utf8 string?
 
139
 *
 
140
 * @return:
 
141
 * If @p is in form of \L{foo}, return the char pointer pointing to '}' of \L{foo}
 
142
 * else return @p untouched; 
 
143
 * 
 
144
 * Check if @p is in form of \L{foo}.
 
145
 * If it is, the exact "foo" will be put into @output, without any esacaping.
 
146
 *
 
147
 */
 
148
static char const *
 
149
latex_raw_str(char const *p, GsfOutput *output, gboolean utf8)
 
150
{
 
151
        char const *p_begin, *p_orig = p;
 
152
        int depth = 1;
 
153
        if(strncasecmp(p, "\\L{", 3) == 0){
 
154
                p += 3;
 
155
                p_begin = p;
 
156
                /* find the matching close bracket */
 
157
                for(; *p; p = utf8 ? g_utf8_next_char(p) : p + 1){
 
158
                        switch(*p){ /* FIXME: how to put in unmatched brackets? */
 
159
                                case '{':
 
160
                                        depth ++;
 
161
                                        break;
 
162
                                case '}':
 
163
                                        depth--;
 
164
                                        if(depth == 0){
 
165
                                                /* put the string beginning from p_begin to p to output */
 
166
                                                gsf_output_write(output, p - p_begin, p_begin);
 
167
                                                return p;
 
168
                                        }
 
169
                        }
 
170
                }
 
171
        }
 
172
        return p_orig;
 
173
}
 
174
 
 
175
 
 
176
/**
135
177
 * latex_fputs_utf :
136
178
 *
137
179
 * @p :      a pointer to a char, start of the string to be processed.
138
180
 * @output : output stream where the processed characters are written.
139
181
 *
140
 
 * This escapes any special LaTeX characters from the LaTeX engine. Re-ordered
141
 
 * from Rasca's code to have most common first.
 
182
 * This escapes any special LaTeX characters from the LaTeX engine,
 
183
 * except the ones enclosed in "\L{" and "}".
 
184
 * Re-ordered from Rasca's code to have most common first.
142
185
 */
143
186
static void
144
187
latex_fputs_utf (char const *p, GsfOutput *output)
145
188
{
 
189
        char const *rlt;
146
190
        for (; *p; p = g_utf8_next_char (p)) {
147
191
                switch (g_utf8_get_char (p)) {
148
 
                        
 
192
 
149
193
                        /* These are the classic TeX symbols $ & % # _ { } (see Lamport, p.15) */
150
194
                case '$': case '&': case '%': case '#':
151
195
                case '_': case '{': case '}':
156
200
                        gsf_output_printf (output, "\\%c{ }", *p);
157
201
                        break;
158
202
                case '\\':
159
 
                        gsf_output_puts (output, "$\\backslash$");
 
203
                        rlt = latex_raw_str(p, output, TRUE);
 
204
                        if(rlt == p)
 
205
                            gsf_output_puts (output, "$\\backslash$");
 
206
                        else
 
207
                            p = rlt;
160
208
                        break;
161
209
                        /* Are these available only in LaTeX through mathmode? */
162
210
                case '>': case '<':
177
225
 * @p :     a pointer to a char, start of the string to be processed.
178
226
 * @output: output stream where the processed characters are written.
179
227
 *
180
 
 * This escapes any special LaTeX characters from the LaTeX engine.
 
228
 * This escapes any special LaTeX characters from the LaTeX engine,
 
229
 * except the ones enclosed in "\L{" and "}".
181
230
 * 
182
231
 * We assume that htis will be set in Mathematics mode.
183
232
 */
184
233
static void
185
234
latex_math_fputs_utf (char const *p, GsfOutput *output)
186
235
{
 
236
        char const *rlt;
187
237
        for (; *p; p = g_utf8_next_char (p)) {
188
238
                switch (g_utf8_get_char (p)) {
189
239
 
196
246
                                gsf_output_printf (output, "\\%c{ }", *p);
197
247
                                break;
198
248
                        case '\\':
199
 
                                gsf_output_puts (output, "\\backslash");
 
249
                                rlt = latex_raw_str(p, output, TRUE);
 
250
                                if(rlt == p)
 
251
                                    gsf_output_puts (output, "$\\backslash$");
 
252
                                else
 
253
                                    p = rlt;
200
254
                                break;
201
 
 
202
255
                        default:
203
256
                                gsf_output_write (output, 
204
257
                                                  (g_utf8_next_char (p)) - p, p);
260
313
 * @p :      a pointer to a char, start of the string to be processed.
261
314
 * @output : output stream where the processed characters are written.
262
315
 *
263
 
 * This escapes any special LaTeX characters from the LaTeX engine. Re-ordered
264
 
 * from Rasca's code to have most common first.
 
316
 * This escapes any special LaTeX characters from the LaTeX engine,
 
317
 * except the ones enclosed in "\L{" and "}".
 
318
 * Re-ordered from Rasca's code to have most common first.
265
319
 */
266
320
static void
267
321
latex_fputs_latin (char const *text, GsfOutput *output)
268
322
{
269
323
        char * encoded_text = NULL;
270
 
        char * p;
 
324
        char const *p;
 
325
        char const *rlt;
271
326
 
272
327
        encoded_text = latex_convert_latin_to_utf (text);
273
328
 
284
339
                        gsf_output_printf (output, "\\%c{ }", *p);
285
340
                        break;
286
341
                case '\\':
287
 
                        gsf_output_puts (output, "$\\backslash$");
 
342
                        rlt = latex_raw_str(p, output, FALSE);
 
343
                        if(rlt == p)
 
344
                            gsf_output_puts (output, "$\\backslash$");
 
345
                        else
 
346
                            p = rlt;
288
347
                        break;
289
348
                        /* Are these available only in LaTeX through mathmode? */
290
349
                case '>': case '<': case '�':
305
364
 * @p :     a pointer to a char, start of the string to be processed.
306
365
 * @output: output stream where the processed characters are written.
307
366
 *
308
 
 * This escapes any special LaTeX characters from the LaTeX engine.
 
367
 * This escapes any special LaTeX characters from the LaTeX engine, 
 
368
 * except the ones enclosed in "\L{" and "}".
309
369
 * 
310
370
 * We assume that htis will be set in Mathematics mode.
311
371
 */
313
373
latex_math_fputs_latin (char const *text, GsfOutput *output)
314
374
{
315
375
        char * encoded_text = NULL;
316
 
        char * p;
 
376
        char const *p;
 
377
        char const *rlt;
317
378
 
318
379
        encoded_text = latex_convert_latin_to_utf (text);
319
380
 
329
390
                                gsf_output_printf (output, "\\%c{ }", *p);
330
391
                                break;
331
392
                        case '\\':
332
 
                                gsf_output_puts (output, "\\backslash");
 
393
                                rlt = latex_raw_str(p, output, FALSE);
 
394
                                if(rlt == p)
 
395
                                    gsf_output_puts (output, "$\\backslash$");
 
396
                                else
 
397
                                    p = rlt;
333
398
                                break;
334
399
 
335
400
                        default:
636
701
}
637
702
 
638
703
/**
639
 
 * latex2e_write_blank_cell:
 
704
 * latex2e_write_blank_multicolumn_cell:
640
705
 *
641
706
 * @output : output stream where the cell contents will be written.
642
 
 * @col :
643
 
 * @row :
644
 
 * @first_column :
 
707
 * @star_col :
 
708
 * @start_row :
 
709
 * @num_merged_cols : an integer value of the number of columns to merge.
 
710
 * @num_merged_rows : an integer value of the number of rows to merge.
645
711
 * @sheet :  the current sheet.
646
712
 *
647
713
 * This function creates all the LaTeX code for the cell of a table (i.e. all
651
717
 *
652
718
 */
653
719
static void
654
 
latex2e_write_blank_cell (GsfOutput *output, gint col, gint row, gint index,
655
 
                          StyleBorderType *borders, Sheet *sheet)
 
720
latex2e_write_blank_multicolumn_cell (GsfOutput *output, int start_col, int start_row,
 
721
                                      int num_merged_cols, int num_merged_rows,
 
722
                                      gint index,
 
723
                                      StyleBorderType *borders, Sheet *sheet)
656
724
{
657
 
        GnmCellPos pos;
 
725
        int merge_width = 0;
658
726
        StyleBorderType left_border = STYLE_BORDER_NONE;
659
727
        StyleBorderType right_border = STYLE_BORDER_NONE;
660
728
 
661
 
        pos.col = col;
662
 
        pos.row = row;
 
729
        if (num_merged_cols > 1 || num_merged_rows > 1) {
 
730
                ColRowInfo const * ci;
 
731
                int i;
 
732
 
 
733
                for (i = 0; i < num_merged_cols; i++) {
 
734
                        ci = sheet_col_get_info (sheet, start_col + i);
 
735
                        merge_width += ci->size_pixels;
 
736
                }
 
737
        }
663
738
 
664
739
        if (index == 0) {
665
740
                left_border = *borders;
666
741
        }
667
 
        right_border = borders[index + 1];
668
 
 
669
 
        if (left_border == STYLE_BORDER_NONE && right_border == STYLE_BORDER_NONE)
670
 
                gsf_output_printf (output, "\n");
671
 
        else {
 
742
        right_border = borders[index + num_merged_cols];
 
743
 
 
744
        /* We only set up a multicolumn command if necessary */
 
745
        if (num_merged_cols > 1) {
 
746
                int i;
 
747
 
 
748
                /* Open the multicolumn statement. */
 
749
                gsf_output_printf (output, "\\multicolumn{%d}{", num_merged_cols);
 
750
 
 
751
                if (left_border != STYLE_BORDER_NONE)
 
752
                        latex2e_print_vert_border (output, left_border);
 
753
 
 
754
                if (num_merged_rows > 1) {
 
755
                        gsf_output_printf (output, "c");
 
756
                } else {
 
757
                        gsf_output_printf (output, "p{");
 
758
                        for (i = 0; i < num_merged_cols; i++) {
 
759
                                gsf_output_printf (output, "\t\\gnumericCol%s+%%\n",
 
760
                                         col_name (start_col + i));
 
761
                        }
 
762
                        gsf_output_printf (output, "\t\\tabcolsep*2*%i}", num_merged_cols - 1);
 
763
                }
 
764
 
 
765
                if (right_border != STYLE_BORDER_NONE)
 
766
                        latex2e_print_vert_border (output, right_border);
 
767
 
 
768
                /*Close the right delimiter, as above. Also open the text delimiter.*/
 
769
                gsf_output_printf (output,"}%%\n\t{");
 
770
        } else if (left_border != STYLE_BORDER_NONE || right_border != STYLE_BORDER_NONE) {
 
771
 
672
772
                /* Open the multicolumn statement. */
673
773
                gsf_output_printf (output, "\\multicolumn{1}{");
674
774
 
676
776
                        latex2e_print_vert_border (output, left_border);
677
777
 
678
778
                /* Drop in the left hand format delimiter. */
679
 
                gsf_output_printf (output, "c");
 
779
                gsf_output_printf (output, "p{\\gnumericCol%s}", col_name(start_col));
680
780
 
681
781
                if (right_border != STYLE_BORDER_NONE)
682
782
                        latex2e_print_vert_border (output, right_border);
683
783
 
684
 
                /*Close the right delimiter, as above. Also add the empty text delimiters.*/
685
 
                gsf_output_printf (output,"}{}%%\n");
686
 
        }
 
784
                /*Close the right delimiter, as above. Also open the text delimiter.*/
 
785
                gsf_output_printf (output,"}%%\n\t{");
 
786
 
 
787
        }
 
788
 
 
789
        if (num_merged_rows > 1) {
 
790
                int i;
 
791
                /* Open the multirow statement. */
 
792
                gsf_output_printf (output, "\\multirow{%d}[%i]*{\\begin{tabular}{p{",
 
793
                         num_merged_rows, num_merged_rows/2);
 
794
                for (i = 0; i < num_merged_cols; i++) {
 
795
                        gsf_output_printf (output, "\t\\gnumericCol%s+%%\n", col_name (start_col + i));
 
796
                }
 
797
                if (num_merged_cols > 2)
 
798
                        gsf_output_printf (output, "\t\\tabcolsep*2*%i}}", num_merged_cols - 2);
 
799
                else
 
800
                        gsf_output_printf (output, "\t0pt}}");
 
801
                /* Close the multirowtext. */
 
802
                gsf_output_printf (output, "\\end{tabular}}");
 
803
        }
 
804
 
 
805
        /* Close the multicolumn text bracket. */
 
806
        if (num_merged_cols > 1 || left_border != STYLE_BORDER_NONE
 
807
            || right_border != STYLE_BORDER_NONE)
 
808
                gsf_output_printf (output, "}");
 
809
 
 
810
        /* And we are done. */
 
811
        gsf_output_printf (output, "\n");
 
812
 
687
813
}
688
814
 
689
815
 
692
818
 *
693
819
 * @output : output stream where the cell contents will be written.
694
820
 * @cell :   the cell whose contents are to be written.
 
821
 * @star_col :
695
822
 * @num_merged_cols : an integer value of the number of columns to merge.
696
823
 * @num_merged_rows : an integer value of the number of rows to merge.
697
824
 * @sheet :  the current sheet.
772
899
                        latex2e_print_vert_border (output, left_border);
773
900
 
774
901
                /* Drop in the left hand format delimiter. */
775
 
                gsf_output_printf (output, "p{\\gnumericCol%s}", col_name(cell->pos.col));
 
902
                gsf_output_printf (output, "p{\\gnumericCol%s}", col_name(start_col));
776
903
 
777
904
                if (right_border != STYLE_BORDER_NONE)
778
905
                        latex2e_print_vert_border (output, right_border);
1080
1207
        gsf_output_puts (output, ""
1081
1208
"\\setlength\\gnumericTableWidthComplete{\\gnumericTableWidth+%\n"
1082
1209
"         \\tabcolsep*\\gumericNumCols*2+\\arrayrulewidth*\\gumericNumCols}\n"
1083
 
"\\ifthenelse{\\lengthtest{\\gnumericTableWidthComplete > \\textwidth}}%\n"
1084
 
"         {\\def\\gnumericScale{\\ratio{\\textwidth-%\n"
 
1210
"\\ifthenelse{\\lengthtest{\\gnumericTableWidthComplete > \\linewidth}}%\n"
 
1211
"         {\\def\\gnumericScale{\\ratio{\\linewidth-%\n"
1085
1212
"                        \\tabcolsep*\\gumericNumCols*2-%\n"
1086
1213
"                        \\arrayrulewidth*\\gumericNumCols}%\n"
1087
1214
"{\\gnumericTableWidth}}}%\n"
1120
1247
                ColRowInfo const * ri;
1121
1248
                ri = sheet_row_get_info (current_sheet, row);
1122
1249
                if (ri->needs_respan)
1123
 
                        row_calc_spans ((ColRowInfo *) ri, current_sheet);
 
1250
                        row_calc_spans ((ColRowInfo *) ri, row, current_sheet);
1124
1251
 
1125
1252
                /* We need to check for horizontal borders at the top of this row */
1126
1253
                length = num_cols;
1167
1294
                g_free (clines);
1168
1295
 
1169
1296
                for (col = total_range.start.col; col <= total_range.end.col; col++) {
1170
 
                        CellSpanInfo const *the_span;
 
1297
                        GnmCellPos pos;
1171
1298
 
 
1299
                        pos.col = col;
 
1300
                        pos.row = row;
 
1301
                        
1172
1302
                        /* Get the cell. */
1173
1303
                        cell = sheet_cell_get (current_sheet, col, row);
1174
1304
 
1178
1308
                        else
1179
1309
                                gsf_output_printf (output, "\t ");
1180
1310
 
1181
 
                        /* Even an empty cell (especially an empty cell!) can be */
1182
 
                        /* covered by a span!                                    */
1183
 
                        the_span = row_span_get (ri, col);
1184
 
                        if (the_span != NULL) {
1185
 
                                latex2e_write_multicolumn_cell(output, (GnmCell *)the_span->cell,
1186
 
                                                               col,
1187
 
                                                               the_span->right -
1188
 
                                                               col + 1, 1,
1189
 
                                                               col - total_range.start.col,
1190
 
                                                               next_vert, current_sheet);
1191
 
                                col += the_span->right - col;
1192
 
                                continue;
1193
 
                        }
1194
 
 
1195
 
 
1196
 
                        /* A blank cell has only a few options*/
1197
 
                        if (cell_is_empty(cell)) {
1198
 
                                latex2e_write_blank_cell(output, col, row,
1199
 
                                                        col - total_range.start.col,
1200
 
                                                        next_vert, current_sheet);
1201
 
                                continue;
1202
 
                        }
1203
 
 
1204
1311
                        /* Check a merge. */
1205
 
                        merge_range = sheet_merge_is_corner (current_sheet, &cell->pos);
 
1312
                        merge_range = sheet_merge_is_corner (current_sheet, &pos);
1206
1313
                        if (merge_range == NULL) {
1207
 
                                latex2e_write_multicolumn_cell(output, cell, col, 1, 1,
 
1314
                                if (cell_is_empty(cell))
 
1315
                                        latex2e_write_blank_multicolumn_cell(output, col, row,
 
1316
                                                                             1, 1,
 
1317
                                                               col - total_range.start.col,
 
1318
                                                               next_vert, current_sheet);
 
1319
                                else
 
1320
                                        latex2e_write_multicolumn_cell(output, cell, col,
 
1321
                                                                       1, 1,
1208
1322
                                                               col - total_range.start.col,
1209
1323
                                                               next_vert, current_sheet);
1210
1324
                                continue;
1214
1328
                        num_merged_cols = merge_range->end.col - merge_range->start.col + 1;
1215
1329
                        num_merged_rows = merge_range->end.row - merge_range->start.row + 1;
1216
1330
 
1217
 
                        latex2e_write_multicolumn_cell(output, cell, col, num_merged_cols,
1218
 
                                                       num_merged_rows,
1219
 
                                                       col - total_range.start.col,
1220
 
                                                       next_vert, current_sheet);
 
1331
                        if (cell_is_empty(cell))
 
1332
                                latex2e_write_blank_multicolumn_cell(output, col, row, 
 
1333
                                                                     num_merged_cols,
 
1334
                                                                     num_merged_rows,
 
1335
                                                                     col - total_range.start.col,
 
1336
                                                                     next_vert, current_sheet);
 
1337
                        else
 
1338
                                latex2e_write_multicolumn_cell(output, cell, col, num_merged_cols,
 
1339
                                                               num_merged_rows,
 
1340
                                                               col - total_range.start.col,
 
1341
                                                               next_vert, current_sheet);
1221
1342
                        col += (num_merged_cols - 1);
1222
1343
                        continue;
1223
1344
                }
1257
1378
        gsf_output_printf (output, "\\end{longtable}\n\n");
1258
1379
        gsf_output_printf (output, "\\gnumericTableEnd\n");
1259
1380
}
 
1381
 
 
1382
 
 
1383
/**
 
1384
 * latex2e_table_cell:
 
1385
 *
 
1386
 * @output : output stream where the cell contents will be written.
 
1387
 * @cell :   the cell whose contents are to be written.
 
1388
 * @sheet :  the current sheet.
 
1389
 *
 
1390
 * This function creates all the LaTeX code for the cell of a table (i.e. all
 
1391
 * the code that might fall between two ampersands (&)).
 
1392
 *
 
1393
 */
 
1394
static void
 
1395
latex2e_table_write_cell (GsfOutput *output, GnmCell *cell, Sheet *sheet)
 
1396
{
 
1397
        GnmStyle const *style = cell_get_style (cell);
 
1398
 
 
1399
        if (gnm_style_get_contents_hidden (style))
 
1400
                return;
 
1401
 
 
1402
        if (!cell_is_empty (cell)) {
 
1403
                char * rendered_string;
 
1404
 
 
1405
                rendered_string = cell_get_rendered_text (cell);
 
1406
                latex_fputs (rendered_string, output);
 
1407
                g_free (rendered_string);
 
1408
        }
 
1409
}
 
1410
 
 
1411
 
 
1412
/**
 
1413
 * latex2e_table_write_file_header:
 
1414
 *
 
1415
 * @output: Output stream where the cell contents will be written.
 
1416
 *
 
1417
 * This ouputs the LaTeX header. Kept separate for esthetics.
 
1418
 */
 
1419
 
 
1420
static void
 
1421
latex2e_table_write_file_header(GsfOutput *output)
 
1422
{
 
1423
        gsf_output_puts (output,
 
1424
"%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"
 
1425
"%%                                                                  %%\n"
 
1426
"%%  This is a LaTeX2e table fragment exported from Gnumeric.        %%\n"
 
1427
"%%                                                                  %%\n"
 
1428
"%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"
 
1429
        );
 
1430
}
 
1431
 
 
1432
/**
 
1433
 * latex_table_file_save :  The LaTeX2e exporter plugin function.
 
1434
 *
 
1435
 * @FileSaver :        New structure for file plugins. I don't understand.
 
1436
 * @IOcontext :        currently not used but reserved for the future.
 
1437
 * @WorkbookView :     this provides the way to access the sheet being exported.
 
1438
 * @filename :         where we'll write.
 
1439
 *
 
1440
 * This writes the top sheet of a Gnumeric workbook as the content of a latex table environment.
 
1441
 * We try to avoid all formatting.
 
1442
 */
 
1443
void
 
1444
latex_table_file_save (GOFileSaver const *fs, IOContext *io_context,
 
1445
                 WorkbookView const *wb_view, GsfOutput *output)
 
1446
{
 
1447
        GnmCell *cell;
 
1448
        Sheet *current_sheet;
 
1449
        GnmRange total_range;
 
1450
        int row, col;
 
1451
 
 
1452
        /* This is the preamble of the LaTeX2e file. */
 
1453
        latex2e_table_write_file_header(output);
 
1454
 
 
1455
        /* Get the topmost sheet and its range from the plugin function argument. */
 
1456
        current_sheet = wb_view_cur_sheet(wb_view);
 
1457
        total_range = sheet_get_extent (current_sheet, TRUE);
 
1458
 
 
1459
        /* Step through the sheet, writing cells as appropriate. */
 
1460
        for (row = total_range.start.row; row <= total_range.end.row; row++) {
 
1461
                ColRowInfo const * ri;
 
1462
                ri = sheet_row_get_info (current_sheet, row);
 
1463
                if (ri->needs_respan)
 
1464
                        row_calc_spans ((ColRowInfo *) ri, row, current_sheet);
 
1465
 
 
1466
                for (col = total_range.start.col; col <= total_range.end.col; col++) {
 
1467
                        /* Get the cell. */
 
1468
                        cell = sheet_cell_get (current_sheet, col, row);
 
1469
 
 
1470
                        /* Check if we are not the first cell in the row.*/
 
1471
                        if (col != total_range.start.col)
 
1472
                                gsf_output_printf (output, "\t&");
 
1473
 
 
1474
                        if (cell_is_empty (cell))
 
1475
                                continue;
 
1476
 
 
1477
                        latex2e_table_write_cell(output, cell, current_sheet);
 
1478
                }
 
1479
                gsf_output_printf (output, "\\\\\n");
 
1480
        }
 
1481
}