| File: | content/handlers/text/textplain.c |
| Warning: | line 274, column 13 The left operand of '==' is a garbage value |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* | ||||
| 2 | * Copyright 2006 James Bursa <bursa@users.sourceforge.net> | ||||
| 3 | * Copyright 2006 Adrian Lees <adrianl@users.sourceforge.net> | ||||
| 4 | * | ||||
| 5 | * This file is part of NetSurf, http://www.netsurf-browser.org/ | ||||
| 6 | * | ||||
| 7 | * NetSurf is free software; you can redistribute it and/or modify | ||||
| 8 | * it under the terms of the GNU General Public License as published by | ||||
| 9 | * the Free Software Foundation; version 2 of the License. | ||||
| 10 | * | ||||
| 11 | * NetSurf is distributed in the hope that it will be useful, | ||||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
| 14 | * GNU General Public License for more details. | ||||
| 15 | * | ||||
| 16 | * You should have received a copy of the GNU General Public License | ||||
| 17 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
| 18 | */ | ||||
| 19 | |||||
| 20 | /** | ||||
| 21 | * \file | ||||
| 22 | * | ||||
| 23 | * plain text content handling implementation. | ||||
| 24 | */ | ||||
| 25 | |||||
| 26 | #include <string.h> | ||||
| 27 | #include <parserutils/input/inputstream.h> | ||||
| 28 | |||||
| 29 | #include "utils/errors.h" | ||||
| 30 | #include "utils/corestrings.h" | ||||
| 31 | #include "utils/http.h" | ||||
| 32 | #include "utils/log.h" | ||||
| 33 | #include "utils/messages.h" | ||||
| 34 | #include "utils/utils.h" | ||||
| 35 | #include "utils/utf8.h" | ||||
| 36 | #include "utils/nsoption.h" | ||||
| 37 | #include "netsurf/content.h" | ||||
| 38 | #include "netsurf/keypress.h" | ||||
| 39 | #include "netsurf/browser_window.h" | ||||
| 40 | #include "netsurf/plotters.h" | ||||
| 41 | #include "netsurf/layout.h" | ||||
| 42 | #include "content/content_protected.h" | ||||
| 43 | #include "content/content_factory.h" | ||||
| 44 | #include "content/hlcache.h" | ||||
| 45 | #include "content/textsearch.h" | ||||
| 46 | #include "content/handlers/css/utils.h" | ||||
| 47 | #include "desktop/selection.h" | ||||
| 48 | #include "desktop/gui_internal.h" | ||||
| 49 | |||||
| 50 | #include "text/textplain.h" | ||||
| 51 | |||||
| 52 | struct textplain_line { | ||||
| 53 | size_t start; | ||||
| 54 | size_t length; | ||||
| 55 | }; | ||||
| 56 | |||||
| 57 | /** | ||||
| 58 | * plain text content | ||||
| 59 | */ | ||||
| 60 | typedef struct textplain_content { | ||||
| 61 | struct content base; | ||||
| 62 | |||||
| 63 | lwc_string *encoding; | ||||
| 64 | void *inputstream; | ||||
| 65 | char *utf8_data; | ||||
| 66 | size_t utf8_data_size; | ||||
| 67 | size_t utf8_data_allocated; | ||||
| 68 | unsigned long physical_line_count; | ||||
| 69 | struct textplain_line *physical_line; | ||||
| 70 | int formatted_width; | ||||
| 71 | struct browser_window *bw; | ||||
| 72 | |||||
| 73 | struct selection *sel; /** Selection state */ | ||||
| 74 | |||||
| 75 | } textplain_content; | ||||
| 76 | |||||
| 77 | |||||
| 78 | #define CHUNK32768 32768 /* Must be a power of 2 */ | ||||
| 79 | #define MARGIN4 4 | ||||
| 80 | |||||
| 81 | #define TAB_WIDTH8 8 /* must be power of 2 currently */ | ||||
| 82 | #define TEXT_SIZE10 * (1 << (10)) 10 * PLOT_STYLE_SCALE(1 << (10)) /* Unscaled text size in pt */ | ||||
| 83 | |||||
| 84 | static plot_font_style_t textplain_style = { | ||||
| 85 | .family = PLOT_FONT_FAMILY_MONOSPACE, | ||||
| 86 | .size = TEXT_SIZE10 * (1 << (10)), | ||||
| 87 | .weight = 400, | ||||
| 88 | .flags = FONTF_NONE, | ||||
| 89 | .background = 0xffffff, | ||||
| 90 | .foreground = 0x000000, | ||||
| 91 | }; | ||||
| 92 | |||||
| 93 | static int textplain_tab_width = 256; /* try for a sensible default */ | ||||
| 94 | |||||
| 95 | static lwc_string *textplain_default_charset; | ||||
| 96 | |||||
| 97 | |||||
| 98 | /** | ||||
| 99 | * Clean up after the text content handler | ||||
| 100 | */ | ||||
| 101 | static void textplain_fini(void) | ||||
| 102 | { | ||||
| 103 | if (textplain_default_charset != NULL((void*)0)) { | ||||
| 104 | lwc_string_unref(textplain_default_charset){ lwc_string *__lwc_s = (textplain_default_charset); if (__lwc_s != ((void*)0)) { __lwc_s->refcnt--; if ((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1) && (__lwc_s-> insensitive == __lwc_s))) lwc_string_destroy(__lwc_s); } }; | ||||
| 105 | textplain_default_charset = NULL((void*)0); | ||||
| 106 | } | ||||
| 107 | } | ||||
| 108 | |||||
| 109 | |||||
| 110 | /** | ||||
| 111 | * Work around feature in libparserutils | ||||
| 112 | * | ||||
| 113 | * if the client provides an encoding up front, but does not provide a | ||||
| 114 | * charset detection callback, then libparserutils will replace the | ||||
| 115 | * provided encoding with UTF-8. This breaks our input handling. | ||||
| 116 | * | ||||
| 117 | * Avoid this by providing a callback that does precisely nothing, | ||||
| 118 | * thus preserving whatever charset information we decided on in | ||||
| 119 | * textplain_create. | ||||
| 120 | */ | ||||
| 121 | static parserutils_error | ||||
| 122 | textplain_charset_hack(const uint8_t *data, | ||||
| 123 | size_t len, | ||||
| 124 | uint16_t *mibenum, | ||||
| 125 | uint32_t *source) | ||||
| 126 | { | ||||
| 127 | return PARSERUTILS_OK; | ||||
| 128 | } | ||||
| 129 | |||||
| 130 | |||||
| 131 | /** | ||||
| 132 | * setup plain text render. | ||||
| 133 | * | ||||
| 134 | * \param[in] c content object. | ||||
| 135 | * \param[in] encoding the encoding of the content. | ||||
| 136 | * \return NSERROR_OK else appropriate error code. | ||||
| 137 | */ | ||||
| 138 | static nserror | ||||
| 139 | textplain_create_internal(textplain_content *c, lwc_string *encoding) | ||||
| 140 | { | ||||
| 141 | char *utf8_data; | ||||
| 142 | parserutils_inputstream *stream; | ||||
| 143 | parserutils_error error; | ||||
| 144 | |||||
| 145 | textplain_style.size = (nsoption_int(font_size)(nsoptions[NSOPTION_font_size].value.i) * PLOT_STYLE_SCALE(1 << (10))) / 10; | ||||
| 146 | |||||
| 147 | utf8_data = malloc(CHUNK32768); | ||||
| 148 | if (utf8_data == NULL((void*)0)) | ||||
| 149 | goto no_memory; | ||||
| 150 | |||||
| 151 | error = parserutils_inputstream_create(lwc_string_data(encoding)({((encoding != ((void*)0)) ? (void) (0) : __assert_fail ("encoding != NULL" , "content/handlers/text/textplain.c", 151, __extension__ __PRETTY_FUNCTION__ )); (const char *)((encoding)+1);}), 0, | ||||
| 152 | textplain_charset_hack, &stream); | ||||
| 153 | if (error == PARSERUTILS_BADENCODING) { | ||||
| 154 | /* Fall back to Windows-1252 */ | ||||
| 155 | error = parserutils_inputstream_create("Windows-1252", 0, | ||||
| 156 | textplain_charset_hack, &stream); | ||||
| 157 | } | ||||
| 158 | if (error != PARSERUTILS_OK) { | ||||
| 159 | free(utf8_data); | ||||
| 160 | goto no_memory; | ||||
| 161 | } | ||||
| 162 | |||||
| 163 | c->encoding = lwc_string_ref(encoding)({lwc_string *__lwc_s = (encoding); ((__lwc_s != ((void*)0)) ? (void) (0) : __assert_fail ("__lwc_s != NULL", "content/handlers/text/textplain.c" , 163, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt ++; __lwc_s;}); | ||||
| 164 | c->inputstream = stream; | ||||
| 165 | c->utf8_data = utf8_data; | ||||
| 166 | c->utf8_data_size = 0; | ||||
| 167 | c->utf8_data_allocated = CHUNK32768; | ||||
| 168 | c->physical_line = 0; | ||||
| 169 | c->physical_line_count = 0; | ||||
| 170 | c->formatted_width = 0; | ||||
| 171 | c->bw = NULL((void*)0); | ||||
| 172 | c->sel = selection_create((struct content *)c); | ||||
| 173 | |||||
| 174 | return NSERROR_OK; | ||||
| 175 | |||||
| 176 | no_memory: | ||||
| 177 | content_broadcast_error(&c->base, NSERROR_NOMEM, NULL((void*)0)); | ||||
| 178 | |||||
| 179 | return NSERROR_NOMEM; | ||||
| 180 | } | ||||
| 181 | |||||
| 182 | |||||
| 183 | /** | ||||
| 184 | * Create a CONTENT_TEXTPLAIN. | ||||
| 185 | */ | ||||
| 186 | static nserror | ||||
| 187 | textplain_create(const content_handler *handler, | ||||
| 188 | lwc_string *imime_type, | ||||
| 189 | const struct http_parameter *params, | ||||
| 190 | llcache_handle *llcache, | ||||
| 191 | const char *fallback_charset, | ||||
| 192 | bool_Bool quirks, | ||||
| 193 | struct content **c) | ||||
| 194 | { | ||||
| 195 | textplain_content *text; | ||||
| 196 | nserror error; | ||||
| 197 | lwc_string *encoding; | ||||
| 198 | |||||
| 199 | text = calloc(1, sizeof(textplain_content)); | ||||
| 200 | if (text == NULL((void*)0)) { | ||||
| 201 | return NSERROR_NOMEM; | ||||
| 202 | } | ||||
| 203 | |||||
| 204 | error = content__init(&text->base, handler, imime_type, params, | ||||
| 205 | llcache, fallback_charset, quirks); | ||||
| 206 | if (error != NSERROR_OK) { | ||||
| 207 | free(text); | ||||
| 208 | return error; | ||||
| 209 | } | ||||
| 210 | |||||
| 211 | error = http_parameter_list_find_item(params, corestring_lwc_charset, | ||||
| 212 | &encoding); | ||||
| 213 | if (error != NSERROR_OK) { | ||||
| 214 | encoding = lwc_string_ref(textplain_default_charset)({lwc_string *__lwc_s = (textplain_default_charset); ((__lwc_s != ((void*)0)) ? (void) (0) : __assert_fail ("__lwc_s != NULL" , "content/handlers/text/textplain.c", 214, __extension__ __PRETTY_FUNCTION__ )); __lwc_s->refcnt++; __lwc_s;}); | ||||
| 215 | } | ||||
| 216 | |||||
| 217 | error = textplain_create_internal(text, encoding); | ||||
| 218 | if (error != NSERROR_OK) { | ||||
| 219 | lwc_string_unref(encoding){ lwc_string *__lwc_s = (encoding); if (__lwc_s != ((void*)0) ) { __lwc_s->refcnt--; if ((__lwc_s->refcnt == 0) || (( __lwc_s->refcnt == 1) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy(__lwc_s); } }; | ||||
| 220 | free(text); | ||||
| 221 | return error; | ||||
| 222 | } | ||||
| 223 | |||||
| 224 | lwc_string_unref(encoding){ lwc_string *__lwc_s = (encoding); if (__lwc_s != ((void*)0) ) { __lwc_s->refcnt--; if ((__lwc_s->refcnt == 0) || (( __lwc_s->refcnt == 1) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy(__lwc_s); } }; | ||||
| 225 | |||||
| 226 | *c = (struct content *) text; | ||||
| 227 | |||||
| 228 | return NSERROR_OK; | ||||
| 229 | } | ||||
| 230 | |||||
| 231 | |||||
| 232 | /** | ||||
| 233 | * copy utf8 encoded data | ||||
| 234 | */ | ||||
| 235 | static bool_Bool | ||||
| 236 | textplain_copy_utf8_data(textplain_content *c, const uint8_t *buf, size_t len) | ||||
| 237 | { | ||||
| 238 | if (c->utf8_data_size + len >= c->utf8_data_allocated) { | ||||
| 239 | /* Compute next multiple of chunk above the required space */ | ||||
| 240 | size_t allocated; | ||||
| 241 | char *utf8_data; | ||||
| 242 | |||||
| 243 | allocated = (c->utf8_data_size + len + CHUNK32768 - 1) & ~(CHUNK32768 - 1); | ||||
| 244 | utf8_data = realloc(c->utf8_data, allocated); | ||||
| 245 | if (utf8_data == NULL((void*)0)) | ||||
| 246 | return false0; | ||||
| 247 | |||||
| 248 | c->utf8_data = utf8_data; | ||||
| 249 | c->utf8_data_allocated = allocated; | ||||
| 250 | } | ||||
| 251 | |||||
| 252 | memcpy(c->utf8_data + c->utf8_data_size, buf, len); | ||||
| 253 | c->utf8_data_size += len; | ||||
| 254 | |||||
| 255 | return true1; | ||||
| 256 | } | ||||
| 257 | |||||
| 258 | |||||
| 259 | /** | ||||
| 260 | * drain input | ||||
| 261 | */ | ||||
| 262 | static bool_Bool | ||||
| 263 | textplain_drain_input(textplain_content *c, | ||||
| 264 | parserutils_inputstream *stream, | ||||
| 265 | parserutils_error terminator) | ||||
| 266 | { | ||||
| 267 | static const uint8_t *u_fffd = (const uint8_t *) "\xef\xbf\xbd"; | ||||
| 268 | const uint8_t *ch; | ||||
| 269 | size_t chlen, offset = 0; | ||||
| 270 | |||||
| 271 | while (parserutils_inputstream_peek(stream, offset, &ch, &chlen) != | ||||
| 272 | terminator) { | ||||
| 273 | /* Replace all instances of NUL with U+FFFD */ | ||||
| 274 | if (chlen == 1 && *ch == 0) { | ||||
| |||||
| 275 | if (offset > 0) { | ||||
| 276 | /* Obtain pointer to start of input data */ | ||||
| 277 | parserutils_inputstream_peek(stream, 0, | ||||
| 278 | &ch, &chlen); | ||||
| 279 | /* Copy from it up to the start of the NUL */ | ||||
| 280 | if (textplain_copy_utf8_data(c, ch, | ||||
| 281 | offset) == false0) | ||||
| 282 | return false0; | ||||
| 283 | } | ||||
| 284 | |||||
| 285 | /* Emit U+FFFD */ | ||||
| 286 | if (textplain_copy_utf8_data(c, u_fffd, 3) == false0) | ||||
| 287 | return false0; | ||||
| 288 | |||||
| 289 | /* Advance inputstream past the NUL we just read */ | ||||
| 290 | parserutils_inputstream_advance(stream, offset + 1); | ||||
| 291 | /* Reset the read offset */ | ||||
| 292 | offset = 0; | ||||
| 293 | } else { | ||||
| 294 | /* Accumulate input */ | ||||
| 295 | offset += chlen; | ||||
| 296 | |||||
| 297 | if (offset > CHUNK32768) { | ||||
| 298 | /* Obtain pointer to start of input data */ | ||||
| 299 | parserutils_inputstream_peek(stream, 0, | ||||
| 300 | &ch, &chlen); | ||||
| 301 | |||||
| 302 | /* Emit the data we've read */ | ||||
| 303 | if (textplain_copy_utf8_data(c, ch, | ||||
| 304 | offset) == false0) | ||||
| 305 | return false0; | ||||
| 306 | |||||
| 307 | /* Advance the inputstream */ | ||||
| 308 | parserutils_inputstream_advance(stream, offset); | ||||
| 309 | /* Reset the read offset */ | ||||
| 310 | offset = 0; | ||||
| 311 | } | ||||
| 312 | } | ||||
| 313 | } | ||||
| 314 | |||||
| 315 | if (offset > 0) { | ||||
| 316 | /* Obtain pointer to start of input data */ | ||||
| 317 | parserutils_inputstream_peek(stream, 0, &ch, &chlen); | ||||
| 318 | /* Emit any data remaining */ | ||||
| 319 | if (textplain_copy_utf8_data(c, ch, offset) == false0) | ||||
| 320 | return false0; | ||||
| 321 | |||||
| 322 | /* Advance the inputstream past the data we've read */ | ||||
| 323 | parserutils_inputstream_advance(stream, offset); | ||||
| 324 | } | ||||
| 325 | |||||
| 326 | return true1; | ||||
| 327 | } | ||||
| 328 | |||||
| 329 | |||||
| 330 | /** | ||||
| 331 | * Process data for CONTENT_TEXTPLAIN. | ||||
| 332 | */ | ||||
| 333 | static bool_Bool | ||||
| 334 | textplain_process_data(struct content *c, const char *data, unsigned int size) | ||||
| 335 | { | ||||
| 336 | textplain_content *text = (textplain_content *) c; | ||||
| 337 | parserutils_inputstream *stream = text->inputstream; | ||||
| 338 | parserutils_error error; | ||||
| 339 | |||||
| 340 | error = parserutils_inputstream_append(stream, | ||||
| 341 | (const uint8_t *) data, size); | ||||
| 342 | if (error != PARSERUTILS_OK) { | ||||
| 343 | goto no_memory; | ||||
| 344 | } | ||||
| 345 | |||||
| 346 | if (textplain_drain_input(text, stream, PARSERUTILS_NEEDDATA) == false0) | ||||
| 347 | goto no_memory; | ||||
| 348 | |||||
| 349 | return true1; | ||||
| 350 | |||||
| 351 | no_memory: | ||||
| 352 | content_broadcast_error(c, NSERROR_NOMEM, NULL((void*)0)); | ||||
| 353 | return false0; | ||||
| 354 | } | ||||
| 355 | |||||
| 356 | |||||
| 357 | /** | ||||
| 358 | * Convert a CONTENT_TEXTPLAIN for display. | ||||
| 359 | */ | ||||
| 360 | static bool_Bool textplain_convert(struct content *c) | ||||
| 361 | { | ||||
| 362 | textplain_content *text = (textplain_content *) c; | ||||
| 363 | parserutils_inputstream *stream = text->inputstream; | ||||
| 364 | parserutils_error error; | ||||
| 365 | |||||
| 366 | error = parserutils_inputstream_append(stream, NULL((void*)0), 0); | ||||
| 367 | if (error != PARSERUTILS_OK) { | ||||
| 368 | return false0; | ||||
| 369 | } | ||||
| 370 | |||||
| 371 | if (textplain_drain_input(text, stream, PARSERUTILS_EOF) == false0) | ||||
| 372 | return false0; | ||||
| 373 | |||||
| 374 | parserutils_inputstream_destroy(stream); | ||||
| 375 | text->inputstream = NULL((void*)0); | ||||
| 376 | |||||
| 377 | content_set_ready(c); | ||||
| 378 | content_set_done(c); | ||||
| 379 | content_set_status(c, messages_get("Done")); | ||||
| 380 | |||||
| 381 | return true1; | ||||
| 382 | } | ||||
| 383 | |||||
| 384 | |||||
| 385 | /** | ||||
| 386 | * Calculate the line height, in pixels | ||||
| 387 | * | ||||
| 388 | * \return Line height, in pixels | ||||
| 389 | */ | ||||
| 390 | static float textplain_line_height(void) | ||||
| 391 | { | ||||
| 392 | /* Size is in points, so convert to pixels. | ||||
| 393 | * Then use a constant line height of 1.2 x font size. | ||||
| 394 | */ | ||||
| 395 | return FIXTOFLT(FDIV((FMUL(FLTTOFIX(1.2), FMUL(nscss_screen_dpi, INTTOFIX((textplain_style.size / PLOT_STYLE_SCALE))))), F_72))((float) ((css_divide_fixed((((css_multiply_fixed((((css_fixed ) ((1.2) * (float) (1 << 10)))), ((css_multiply_fixed(( nscss_screen_dpi), ((css_int_to_fixed((textplain_style.size / (1 << (10))))))))))))), (0x00012000)))) / (float) (1 << 10)); | ||||
| 396 | } | ||||
| 397 | |||||
| 398 | |||||
| 399 | /** | ||||
| 400 | * Reformat a CONTENT_TEXTPLAIN to a new width. | ||||
| 401 | */ | ||||
| 402 | static void textplain_reformat(struct content *c, int width, int height) | ||||
| 403 | { | ||||
| 404 | textplain_content *text = (textplain_content *) c; | ||||
| 405 | char *utf8_data = text->utf8_data; | ||||
| 406 | size_t utf8_data_size = text->utf8_data_size; | ||||
| 407 | unsigned long line_count = 0; | ||||
| 408 | struct textplain_line *line = text->physical_line; | ||||
| 409 | struct textplain_line *line1; | ||||
| 410 | size_t i, space, col; | ||||
| 411 | size_t columns = 80; | ||||
| 412 | int character_width; | ||||
| 413 | size_t line_start; | ||||
| 414 | nserror res; | ||||
| 415 | |||||
| 416 | NSLOG(netsurf, INFO, "content %p w:%d h:%d", c, width, height)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf , NSLOG_LEVEL_INFO, "content/handlers/text/textplain.c", sizeof ("content/handlers/text/textplain.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 416, }; nslog__log(&_nslog_ctx , "content %p w:%d h:%d", c, width, height); } } while(0); | ||||
| 417 | |||||
| 418 | /* compute available columns (assuming monospaced font) - use 8 | ||||
| 419 | * characters for better accuracy | ||||
| 420 | */ | ||||
| 421 | res = guit->layout->width(&textplain_style, | ||||
| 422 | "ABCDEFGH", 8, | ||||
| 423 | &character_width); | ||||
| 424 | if (res != NSERROR_OK) { | ||||
| 425 | return; | ||||
| 426 | } | ||||
| 427 | |||||
| 428 | columns = (width - MARGIN4 - MARGIN4) * 8 / character_width; | ||||
| 429 | textplain_tab_width = (TAB_WIDTH8 * character_width) / 8; | ||||
| 430 | |||||
| 431 | text->formatted_width = width; | ||||
| 432 | |||||
| 433 | text->physical_line_count = 0; | ||||
| 434 | |||||
| 435 | if (!line) { | ||||
| 436 | text->physical_line = line = | ||||
| 437 | malloc(sizeof(struct textplain_line) * (1024 + 3)); | ||||
| 438 | if (!line) | ||||
| 439 | goto no_memory; | ||||
| 440 | } | ||||
| 441 | |||||
| 442 | line[line_count++].start = line_start = 0; | ||||
| 443 | space = 0; | ||||
| 444 | i = 0; | ||||
| 445 | col = 0; | ||||
| 446 | while (i < utf8_data_size) { | ||||
| 447 | size_t csize; /* number of bytes in character */ | ||||
| 448 | uint32_t chr; | ||||
| 449 | bool_Bool term; | ||||
| 450 | size_t next_col; | ||||
| 451 | parserutils_error perror; | ||||
| 452 | |||||
| 453 | perror = parserutils_charset_utf8_to_ucs4((const uint8_t *)utf8_data + i, utf8_data_size - i, &chr, &csize); | ||||
| 454 | if (perror != PARSERUTILS_OK) { | ||||
| 455 | chr = 0xfffd; | ||||
| 456 | } | ||||
| 457 | |||||
| 458 | term = (chr == '\n' || chr == '\r'); | ||||
| 459 | |||||
| 460 | next_col = col + 1; | ||||
| 461 | |||||
| 462 | if (chr == '\t') { | ||||
| 463 | next_col = (next_col + TAB_WIDTH8 - 1) & ~(TAB_WIDTH8 - 1); | ||||
| 464 | } | ||||
| 465 | |||||
| 466 | if (term || next_col >= columns) { | ||||
| 467 | if (line_count % 1024 == 0) { | ||||
| 468 | line1 = realloc(line, | ||||
| 469 | sizeof(struct textplain_line) * | ||||
| 470 | (line_count + 1024 + 3)); | ||||
| 471 | if (!line1) | ||||
| 472 | goto no_memory; | ||||
| 473 | text->physical_line = line = line1; | ||||
| 474 | } | ||||
| 475 | |||||
| 476 | if (term) { | ||||
| 477 | line[line_count-1].length = i - line_start; | ||||
| 478 | |||||
| 479 | /* skip second char of CR/LF or LF/CR pair */ | ||||
| 480 | if (i + 1 < utf8_data_size && | ||||
| 481 | utf8_data[i+1] != utf8_data[i] && | ||||
| 482 | (utf8_data[i+1] == '\n' || | ||||
| 483 | utf8_data[i+1] == '\r')) { | ||||
| 484 | i++; | ||||
| 485 | } | ||||
| 486 | } else { | ||||
| 487 | if (space) { | ||||
| 488 | /* break at last space in line */ | ||||
| 489 | i = space; | ||||
| 490 | line[line_count-1].length = (i + 1) - line_start; | ||||
| 491 | } else | ||||
| 492 | line[line_count-1].length = i - line_start; | ||||
| 493 | } | ||||
| 494 | |||||
| 495 | line[line_count++].start = line_start = i + 1; | ||||
| 496 | col = 0; | ||||
| 497 | space = 0; | ||||
| 498 | } else { | ||||
| 499 | col++; | ||||
| 500 | if (chr == ' ') | ||||
| 501 | space = i; | ||||
| 502 | } | ||||
| 503 | i += csize; | ||||
| 504 | } | ||||
| 505 | line[line_count-1].length = i - line[line_count-1].start; | ||||
| 506 | line[line_count].start = utf8_data_size; | ||||
| 507 | |||||
| 508 | text->physical_line_count = line_count; | ||||
| 509 | c->width = width; | ||||
| 510 | c->height = line_count * textplain_line_height() + MARGIN4 + MARGIN4; | ||||
| 511 | |||||
| 512 | return; | ||||
| 513 | |||||
| 514 | no_memory: | ||||
| 515 | NSLOG(netsurf, INFO, "out of memory (line_count %lu)", line_count)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf , NSLOG_LEVEL_INFO, "content/handlers/text/textplain.c", sizeof ("content/handlers/text/textplain.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 515, }; nslog__log(&_nslog_ctx , "out of memory (line_count %lu)", line_count); } } while(0); | ||||
| 516 | return; | ||||
| 517 | } | ||||
| 518 | |||||
| 519 | |||||
| 520 | /** | ||||
| 521 | * Destroy a CONTENT_TEXTPLAIN and free all resources it owns. | ||||
| 522 | */ | ||||
| 523 | |||||
| 524 | static void textplain_destroy(struct content *c) | ||||
| 525 | { | ||||
| 526 | textplain_content *text = (textplain_content *) c; | ||||
| 527 | |||||
| 528 | lwc_string_unref(text->encoding){ lwc_string *__lwc_s = (text->encoding); if (__lwc_s != ( (void*)0)) { __lwc_s->refcnt--; if ((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy(__lwc_s); } }; | ||||
| 529 | |||||
| 530 | if (text->inputstream != NULL((void*)0)) { | ||||
| 531 | parserutils_inputstream_destroy(text->inputstream); | ||||
| 532 | } | ||||
| 533 | |||||
| 534 | if (text->physical_line != NULL((void*)0)) { | ||||
| 535 | free(text->physical_line); | ||||
| 536 | } | ||||
| 537 | |||||
| 538 | if (text->utf8_data != NULL((void*)0)) { | ||||
| 539 | free(text->utf8_data); | ||||
| 540 | } | ||||
| 541 | |||||
| 542 | if (text->sel != NULL((void*)0)) { | ||||
| 543 | selection_destroy(text->sel); | ||||
| 544 | } | ||||
| 545 | } | ||||
| 546 | |||||
| 547 | |||||
| 548 | static nserror textplain_clone(const struct content *old, struct content **newc) | ||||
| |||||
| 549 | { | ||||
| 550 | const textplain_content *old_text = (textplain_content *) old; | ||||
| 551 | textplain_content *text; | ||||
| 552 | nserror error; | ||||
| 553 | const uint8_t *data; | ||||
| 554 | size_t size; | ||||
| 555 | |||||
| 556 | text = calloc(1, sizeof(textplain_content)); | ||||
| 557 | if (text == NULL((void*)0)) | ||||
| 558 | return NSERROR_NOMEM; | ||||
| 559 | |||||
| 560 | error = content__clone(old, &text->base); | ||||
| 561 | if (error != NSERROR_OK) { | ||||
| 562 | content_destroy(&text->base); | ||||
| 563 | return error; | ||||
| 564 | } | ||||
| 565 | |||||
| 566 | /* Simply replay create/process/convert */ | ||||
| 567 | error = textplain_create_internal(text, old_text->encoding); | ||||
| 568 | if (error
| ||||
| 569 | content_destroy(&text->base); | ||||
| 570 | return error; | ||||
| 571 | } | ||||
| 572 | |||||
| 573 | data = content__get_source_data(&text->base, &size); | ||||
| 574 | if (size > 0) { | ||||
| 575 | if (textplain_process_data(&text->base, | ||||
| 576 | (const char *)data, | ||||
| 577 | size) == false0) { | ||||
| 578 | content_destroy(&text->base); | ||||
| 579 | return NSERROR_NOMEM; | ||||
| 580 | } | ||||
| 581 | } | ||||
| 582 | |||||
| 583 | if (old->status == CONTENT_STATUS_READY || | ||||
| 584 | old->status == CONTENT_STATUS_DONE) { | ||||
| 585 | if (textplain_convert(&text->base) == false0) { | ||||
| 586 | content_destroy(&text->base); | ||||
| 587 | return NSERROR_CLONE_FAILED; | ||||
| 588 | } | ||||
| 589 | } | ||||
| 590 | |||||
| 591 | return NSERROR_OK; | ||||
| 592 | } | ||||
| 593 | |||||
| 594 | |||||
| 595 | static content_type textplain_content_type(void) | ||||
| 596 | { | ||||
| 597 | return CONTENT_TEXTPLAIN; | ||||
| 598 | } | ||||
| 599 | |||||
| 600 | |||||
| 601 | /** | ||||
| 602 | * Return byte offset within UTF8 textplain content. | ||||
| 603 | * | ||||
| 604 | * given the co-ordinates of a point within a textplain content. 'dir' | ||||
| 605 | * specifies the direction in which to search (-1 = above-left, +1 = | ||||
| 606 | * below-right) if the co-ordinates are not contained within a line. | ||||
| 607 | * | ||||
| 608 | * \param[in] c content of type CONTENT_TEXTPLAIN | ||||
| 609 | * \param[in] x x ordinate of point | ||||
| 610 | * \param[in] y y ordinate of point | ||||
| 611 | * \param[in] dir direction of search if not within line | ||||
| 612 | * \return byte offset of character containing (or nearest to) point | ||||
| 613 | */ | ||||
| 614 | static size_t | ||||
| 615 | textplain_offset_from_coords(struct content *c, int x, int y, int dir) | ||||
| 616 | { | ||||
| 617 | textplain_content *textc = (textplain_content *) c; | ||||
| 618 | float line_height = textplain_line_height(); | ||||
| 619 | struct textplain_line *line; | ||||
| 620 | const char *text; | ||||
| 621 | unsigned nlines; | ||||
| 622 | size_t length; | ||||
| 623 | int idx; | ||||
| 624 | |||||
| 625 | assert(c != NULL)((c != ((void*)0)) ? (void) (0) : __assert_fail ("c != NULL", "content/handlers/text/textplain.c", 625, __extension__ __PRETTY_FUNCTION__ )); | ||||
| 626 | |||||
| 627 | y = (int)((float)(y - MARGIN4) / line_height); | ||||
| 628 | x -= MARGIN4; | ||||
| 629 | |||||
| 630 | nlines = textc->physical_line_count; | ||||
| 631 | if (!nlines) | ||||
| 632 | return 0; | ||||
| 633 | |||||
| 634 | if (y <= 0) y = 0; | ||||
| 635 | else if ((unsigned)y >= nlines) | ||||
| 636 | y = nlines - 1; | ||||
| 637 | |||||
| 638 | line = &textc->physical_line[y]; | ||||
| 639 | text = textc->utf8_data + line->start; | ||||
| 640 | length = line->length; | ||||
| 641 | idx = 0; | ||||
| 642 | |||||
| 643 | while (x > 0) { | ||||
| 644 | size_t next_offset = 0; | ||||
| 645 | int width = INT_MAX2147483647; | ||||
| 646 | |||||
| 647 | while (next_offset < length && text[next_offset] != '\t') { | ||||
| 648 | next_offset = utf8_next(text, length, next_offset); | ||||
| 649 | } | ||||
| 650 | |||||
| 651 | if (next_offset < length) { | ||||
| 652 | guit->layout->width(&textplain_style, | ||||
| 653 | text, | ||||
| 654 | next_offset, | ||||
| 655 | &width); | ||||
| 656 | } | ||||
| 657 | |||||
| 658 | if (x <= width) { | ||||
| 659 | int pixel_offset; | ||||
| 660 | size_t char_offset; | ||||
| 661 | |||||
| 662 | guit->layout->position(&textplain_style, | ||||
| 663 | text, next_offset, x, | ||||
| 664 | &char_offset, &pixel_offset); | ||||
| 665 | |||||
| 666 | idx += char_offset; | ||||
| 667 | break; | ||||
| 668 | } | ||||
| 669 | |||||
| 670 | x -= width; | ||||
| 671 | length -= next_offset; | ||||
| 672 | text += next_offset; | ||||
| 673 | idx += next_offset; | ||||
| 674 | |||||
| 675 | /* check if it's within the tab */ | ||||
| 676 | width = textplain_tab_width - (width % textplain_tab_width); | ||||
| 677 | if (x <= width) break; | ||||
| 678 | |||||
| 679 | x -= width; | ||||
| 680 | length--; | ||||
| 681 | text++; | ||||
| 682 | idx++; | ||||
| 683 | } | ||||
| 684 | |||||
| 685 | return line->start + idx; | ||||
| 686 | } | ||||
| 687 | |||||
| 688 | |||||
| 689 | /** | ||||
| 690 | * Handle mouse clicks and movements in a TEXTPLAIN content window. | ||||
| 691 | * | ||||
| 692 | * \param c content of type textplain | ||||
| 693 | * \param bw browser window | ||||
| 694 | * \param mouse mouse state on action | ||||
| 695 | * \param x coordinate of mouse | ||||
| 696 | * \param y coordinate of mouse | ||||
| 697 | */ | ||||
| 698 | static nserror | ||||
| 699 | textplain_mouse_action(struct content *c, | ||||
| 700 | struct browser_window *bw, | ||||
| 701 | browser_mouse_state mouse, | ||||
| 702 | int x, int y) | ||||
| 703 | { | ||||
| 704 | textplain_content *text = (textplain_content *) c; | ||||
| 705 | browser_pointer_shape pointer = BROWSER_POINTER_DEFAULT; | ||||
| 706 | union content_msg_data msg_data; | ||||
| 707 | const char *status = 0; | ||||
| 708 | size_t idx; | ||||
| 709 | int dir = 0; | ||||
| 710 | |||||
| 711 | browser_window_set_drag_type(bw, DRAGGING_NONE, NULL((void*)0)); | ||||
| 712 | |||||
| 713 | idx = textplain_offset_from_coords(c, x, y, dir); | ||||
| 714 | if (selection_click(text->sel, text->bw, mouse, idx)) { | ||||
| 715 | |||||
| 716 | if (selection_dragging(text->sel)) { | ||||
| 717 | browser_window_set_drag_type(bw, | ||||
| 718 | DRAGGING_SELECTION, NULL((void*)0)); | ||||
| 719 | status = messages_get("Selecting"); | ||||
| 720 | } | ||||
| 721 | |||||
| 722 | } else { | ||||
| 723 | if (mouse & (BROWSER_MOUSE_DRAG_1 | BROWSER_MOUSE_DRAG_2)) { | ||||
| 724 | browser_window_page_drag_start(bw, x, y); | ||||
| 725 | pointer = BROWSER_POINTER_MOVE; | ||||
| 726 | } | ||||
| 727 | } | ||||
| 728 | |||||
| 729 | msg_data.explicit_status_text = status; | ||||
| 730 | content_broadcast(c, CONTENT_MSG_STATUS, &msg_data); | ||||
| 731 | |||||
| 732 | msg_data.pointer = pointer; | ||||
| 733 | content_broadcast(c, CONTENT_MSG_POINTER, &msg_data); | ||||
| 734 | |||||
| 735 | return NSERROR_OK; | ||||
| 736 | } | ||||
| 737 | |||||
| 738 | |||||
| 739 | /** | ||||
| 740 | * Handle mouse tracking (including drags) in a TEXTPLAIN content window. | ||||
| 741 | * | ||||
| 742 | * \param c content of type textplain | ||||
| 743 | * \param bw browser window | ||||
| 744 | * \param mouse state of mouse buttons and modifier keys | ||||
| 745 | * \param x coordinate of mouse | ||||
| 746 | * \param y coordinate of mouse | ||||
| 747 | */ | ||||
| 748 | static nserror | ||||
| 749 | textplain_mouse_track(struct content *c, | ||||
| 750 | struct browser_window *bw, | ||||
| 751 | browser_mouse_state mouse, | ||||
| 752 | int x, int y) | ||||
| 753 | { | ||||
| 754 | textplain_content *text = (textplain_content *) c; | ||||
| 755 | |||||
| 756 | if (browser_window_get_drag_type(bw) == DRAGGING_SELECTION && !mouse) { | ||||
| 757 | int dir = -1; | ||||
| 758 | size_t idx; | ||||
| 759 | |||||
| 760 | if (selection_dragging_start(text->sel)) | ||||
| 761 | dir = 1; | ||||
| 762 | |||||
| 763 | idx = textplain_offset_from_coords(c, x, y, dir); | ||||
| 764 | selection_track(text->sel, mouse, idx); | ||||
| 765 | |||||
| 766 | browser_window_set_drag_type(bw, DRAGGING_NONE, NULL((void*)0)); | ||||
| 767 | } | ||||
| 768 | |||||
| 769 | switch (browser_window_get_drag_type(bw)) { | ||||
| 770 | |||||
| 771 | case DRAGGING_SELECTION: { | ||||
| 772 | int dir = -1; | ||||
| 773 | size_t idx; | ||||
| 774 | |||||
| 775 | if (selection_dragging_start(text->sel)) dir = 1; | ||||
| 776 | |||||
| 777 | idx = textplain_offset_from_coords(c, x, y, dir); | ||||
| 778 | selection_track(text->sel, mouse, idx); | ||||
| 779 | } | ||||
| 780 | break; | ||||
| 781 | |||||
| 782 | default: | ||||
| 783 | textplain_mouse_action(c, bw, mouse, x, y); | ||||
| 784 | break; | ||||
| 785 | } | ||||
| 786 | |||||
| 787 | return NSERROR_OK; | ||||
| 788 | } | ||||
| 789 | |||||
| 790 | |||||
| 791 | /** | ||||
| 792 | * Handle keypresses. | ||||
| 793 | * | ||||
| 794 | * \param c content of type CONTENT_TEXTPLAIN | ||||
| 795 | * \param key The UCS4 character codepoint | ||||
| 796 | * \return true if key handled, false otherwise | ||||
| 797 | */ | ||||
| 798 | static bool_Bool textplain_keypress(struct content *c, uint32_t key) | ||||
| 799 | { | ||||
| 800 | textplain_content *text = (textplain_content *) c; | ||||
| 801 | struct selection *sel = text->sel; | ||||
| 802 | |||||
| 803 | switch (key) { | ||||
| 804 | case NS_KEY_COPY_SELECTION: | ||||
| 805 | selection_copy_to_clipboard(sel); | ||||
| 806 | return true1; | ||||
| 807 | |||||
| 808 | case NS_KEY_CLEAR_SELECTION: | ||||
| 809 | selection_clear(sel, true1); | ||||
| 810 | return true1; | ||||
| 811 | |||||
| 812 | case NS_KEY_SELECT_ALL: | ||||
| 813 | selection_select_all(sel); | ||||
| 814 | return true1; | ||||
| 815 | |||||
| 816 | case NS_KEY_ESCAPE: | ||||
| 817 | /* if there's no selection, leave Escape for the caller */ | ||||
| 818 | return selection_clear(sel, true1); | ||||
| 819 | } | ||||
| 820 | |||||
| 821 | return false0; | ||||
| 822 | } | ||||
| 823 | |||||
| 824 | |||||
| 825 | /** | ||||
| 826 | * Redraw a text string with highlighting | ||||
| 827 | * (for selection/search) | ||||
| 828 | * | ||||
| 829 | * \param utf8_text pointer to UTF-8 text string | ||||
| 830 | * \param utf8_len length of string, in bytes | ||||
| 831 | * \param offset byte offset within textual representation | ||||
| 832 | * \param x x ordinate at which to plot text | ||||
| 833 | * \param y y ordinate at which to plot text | ||||
| 834 | * \param clip pointer to current clip rectangle | ||||
| 835 | * \param height height of text string | ||||
| 836 | * \param scale current display scale (1.0 = 100%) | ||||
| 837 | * \param text Content being redrawn. | ||||
| 838 | * \param sel Selection context | ||||
| 839 | * \param search Search context | ||||
| 840 | * \param ctx current redraw context | ||||
| 841 | * \return true iff successful and redraw should proceed | ||||
| 842 | */ | ||||
| 843 | static bool_Bool | ||||
| 844 | text_draw(const char *utf8_text, | ||||
| 845 | size_t utf8_len, | ||||
| 846 | size_t offset, | ||||
| 847 | int x, | ||||
| 848 | int y, | ||||
| 849 | const struct rect *clip, | ||||
| 850 | int height, | ||||
| 851 | float scale, | ||||
| 852 | textplain_content *text, | ||||
| 853 | const struct selection *sel, | ||||
| 854 | const struct redraw_context *ctx) | ||||
| 855 | { | ||||
| 856 | bool_Bool highlighted = false0; | ||||
| 857 | plot_font_style_t plot_fstyle; | ||||
| 858 | nserror res; | ||||
| 859 | |||||
| 860 | /* Need scaled text size to pass to plotters */ | ||||
| 861 | plot_fstyle = textplain_style; | ||||
| 862 | plot_fstyle.size *= scale; | ||||
| 863 | |||||
| 864 | /* is this box part of a selection? */ | ||||
| 865 | if (ctx->interactive == true1) { | ||||
| 866 | unsigned len = utf8_len; | ||||
| 867 | unsigned start_idx; | ||||
| 868 | unsigned end_idx; | ||||
| 869 | |||||
| 870 | /* first try the browser window's current selection */ | ||||
| 871 | if (selection_highlighted(sel, | ||||
| 872 | offset, | ||||
| 873 | offset + len, | ||||
| 874 | &start_idx, | ||||
| 875 | &end_idx)) { | ||||
| 876 | highlighted = true1; | ||||
| 877 | } | ||||
| 878 | |||||
| 879 | /* what about the current search operation, if any? */ | ||||
| 880 | if (!highlighted && | ||||
| 881 | (text->base.textsearch.context != NULL((void*)0)) && | ||||
| 882 | content_textsearch_ishighlighted(text->base.textsearch.context, | ||||
| 883 | offset, | ||||
| 884 | offset + len, | ||||
| 885 | &start_idx, | ||||
| 886 | &end_idx)) { | ||||
| 887 | highlighted = true1; | ||||
| 888 | } | ||||
| 889 | |||||
| 890 | /* \todo make search terms visible within selected text */ | ||||
| 891 | if (highlighted) { | ||||
| 892 | struct rect r; | ||||
| 893 | unsigned endtxt_idx = end_idx; | ||||
| 894 | bool_Bool clip_changed = false0; | ||||
| 895 | bool_Bool text_visible = true1; | ||||
| 896 | int startx, endx; | ||||
| 897 | plot_style_t pstyle_fill_hback = *plot_style_fill_white; | ||||
| 898 | plot_font_style_t fstyle_hback = plot_fstyle; | ||||
| 899 | |||||
| 900 | if (end_idx > utf8_len) { | ||||
| 901 | /* adjust for trailing space, not present in | ||||
| 902 | * utf8_text | ||||
| 903 | */ | ||||
| 904 | assert(end_idx == utf8_len + 1)((end_idx == utf8_len + 1) ? (void) (0) : __assert_fail ("end_idx == utf8_len + 1" , "content/handlers/text/textplain.c", 904, __extension__ __PRETTY_FUNCTION__ )); | ||||
| 905 | endtxt_idx = utf8_len; | ||||
| 906 | } | ||||
| 907 | |||||
| 908 | res = guit->layout->width(&textplain_style, | ||||
| 909 | utf8_text, | ||||
| 910 | start_idx, | ||||
| 911 | &startx); | ||||
| 912 | if (res != NSERROR_OK) { | ||||
| 913 | startx = 0; | ||||
| 914 | } | ||||
| 915 | |||||
| 916 | res = guit->layout->width(&textplain_style, | ||||
| 917 | utf8_text, | ||||
| 918 | endtxt_idx, | ||||
| 919 | &endx); | ||||
| 920 | if (res != NSERROR_OK) { | ||||
| 921 | endx = 0; | ||||
| 922 | } | ||||
| 923 | |||||
| 924 | if (scale != 1.0) { | ||||
| 925 | startx *= scale; | ||||
| 926 | endx *= scale; | ||||
| 927 | } | ||||
| 928 | |||||
| 929 | /* draw any text preceding highlighted portion */ | ||||
| 930 | if (start_idx > 0) { | ||||
| 931 | res = ctx->plot->text(ctx, | ||||
| 932 | &plot_fstyle, | ||||
| 933 | x, | ||||
| 934 | y + (int)(height * 0.75 * scale), | ||||
| 935 | utf8_text, | ||||
| 936 | start_idx); | ||||
| 937 | if (res != NSERROR_OK) { | ||||
| 938 | return false0; | ||||
| 939 | } | ||||
| 940 | } | ||||
| 941 | |||||
| 942 | pstyle_fill_hback.fill_colour = textplain_style.foreground; | ||||
| 943 | |||||
| 944 | /* highlighted portion */ | ||||
| 945 | r.x0 = x + startx; | ||||
| 946 | r.y0 = y; | ||||
| 947 | r.x1 = x + endx; | ||||
| 948 | r.y1 = y + height * scale; | ||||
| 949 | res = ctx->plot->rectangle(ctx, &pstyle_fill_hback, &r); | ||||
| 950 | if (res != NSERROR_OK) { | ||||
| 951 | return false0; | ||||
| 952 | } | ||||
| 953 | |||||
| 954 | if (start_idx > 0) { | ||||
| 955 | int px0 = max(x + startx, clip->x0)(((x + startx)>(clip->x0))?(x + startx):(clip->x0)); | ||||
| 956 | int px1 = min(x + endx, clip->x1)(((x + endx)<(clip->x1))?(x + endx):(clip->x1)); | ||||
| 957 | |||||
| 958 | if (px0 < px1) { | ||||
| 959 | r.x0 = px0; | ||||
| 960 | r.y0 = clip->y0; | ||||
| 961 | r.x1 = px1; | ||||
| 962 | r.y1 = clip->y1; | ||||
| 963 | res = ctx->plot->clip(ctx, &r); | ||||
| 964 | if (res != NSERROR_OK) { | ||||
| 965 | return false0; | ||||
| 966 | } | ||||
| 967 | |||||
| 968 | clip_changed = true1; | ||||
| 969 | } else { | ||||
| 970 | text_visible = false0; | ||||
| 971 | } | ||||
| 972 | } | ||||
| 973 | |||||
| 974 | fstyle_hback.background = | ||||
| 975 | pstyle_fill_hback.fill_colour; | ||||
| 976 | fstyle_hback.foreground = colour_to_bw_furthest(((((((pstyle_fill_hback.fill_colour & 0x0000ff) * 77) >> 8) + (((pstyle_fill_hback.fill_colour & 0x00ff00) * 151) >> 16) + (((pstyle_fill_hback.fill_colour & 0xff0000 ) * 30) >> 24)) > (0xff / 2)) ? 0x000000 : 0xffffff) | ||||
| 977 | pstyle_fill_hback.fill_colour)((((((pstyle_fill_hback.fill_colour & 0x0000ff) * 77) >> 8) + (((pstyle_fill_hback.fill_colour & 0x00ff00) * 151) >> 16) + (((pstyle_fill_hback.fill_colour & 0xff0000 ) * 30) >> 24)) > (0xff / 2)) ? 0x000000 : 0xffffff); | ||||
| 978 | |||||
| 979 | if (text_visible && | ||||
| 980 | (ctx->plot->text(ctx, | ||||
| 981 | &fstyle_hback, | ||||
| 982 | x, | ||||
| 983 | y + (int)(height * 0.75 * scale), | ||||
| 984 | utf8_text, | ||||
| 985 | endtxt_idx) != NSERROR_OK)) { | ||||
| 986 | return false0; | ||||
| 987 | } | ||||
| 988 | |||||
| 989 | /* draw any text succeeding highlighted portion */ | ||||
| 990 | if (endtxt_idx < utf8_len) { | ||||
| 991 | int px0 = max(x + endx, clip->x0)(((x + endx)>(clip->x0))?(x + endx):(clip->x0)); | ||||
| 992 | if (px0 < clip->x1) { | ||||
| 993 | |||||
| 994 | r.x0 = px0; | ||||
| 995 | r.y0 = clip->y0; | ||||
| 996 | r.x1 = clip->x1; | ||||
| 997 | r.y1 = clip->y1; | ||||
| 998 | res = ctx->plot->clip(ctx, &r); | ||||
| 999 | if (res != NSERROR_OK) { | ||||
| 1000 | return false0; | ||||
| 1001 | } | ||||
| 1002 | |||||
| 1003 | clip_changed = true1; | ||||
| 1004 | |||||
| 1005 | res = ctx->plot->text(ctx, | ||||
| 1006 | &plot_fstyle, | ||||
| 1007 | x, | ||||
| 1008 | y + (int)(height * 0.75 * scale), | ||||
| 1009 | utf8_text, | ||||
| 1010 | utf8_len); | ||||
| 1011 | if (res != NSERROR_OK) { | ||||
| 1012 | return false0; | ||||
| 1013 | } | ||||
| 1014 | } | ||||
| 1015 | } | ||||
| 1016 | |||||
| 1017 | if (clip_changed && | ||||
| 1018 | (ctx->plot->clip(ctx, clip) != NSERROR_OK)) { | ||||
| 1019 | return false0; | ||||
| 1020 | } | ||||
| 1021 | } | ||||
| 1022 | } | ||||
| 1023 | |||||
| 1024 | if (!highlighted) { | ||||
| 1025 | res = ctx->plot->text(ctx, | ||||
| 1026 | &plot_fstyle, | ||||
| 1027 | x, | ||||
| 1028 | y + (int) (height * 0.75 * scale), | ||||
| 1029 | utf8_text, | ||||
| 1030 | utf8_len); | ||||
| 1031 | if (res != NSERROR_OK) { | ||||
| 1032 | return false0; | ||||
| 1033 | } | ||||
| 1034 | } | ||||
| 1035 | return true1; | ||||
| 1036 | } | ||||
| 1037 | |||||
| 1038 | |||||
| 1039 | /** | ||||
| 1040 | * Draw a CONTENT_TEXTPLAIN using the current set of plotters (plot). | ||||
| 1041 | * | ||||
| 1042 | * x, y, clip_[xy][01] are in target coordinates. | ||||
| 1043 | * | ||||
| 1044 | * \param c content of type CONTENT_TEXTPLAIN | ||||
| 1045 | * \param data redraw data for this content redraw | ||||
| 1046 | * \param clip current clip region | ||||
| 1047 | * \param ctx current redraw context | ||||
| 1048 | * \return true if successful, false otherwise | ||||
| 1049 | */ | ||||
| 1050 | static bool_Bool | ||||
| 1051 | textplain_redraw(struct content *c, | ||||
| 1052 | struct content_redraw_data *data, | ||||
| 1053 | const struct rect *clip, | ||||
| 1054 | const struct redraw_context *ctx) | ||||
| 1055 | { | ||||
| 1056 | textplain_content *text = (textplain_content *) c; | ||||
| 1057 | struct browser_window *bw = text->bw; | ||||
| 1058 | char *utf8_data = text->utf8_data; | ||||
| 1059 | long lineno; | ||||
| 1060 | int x = data->x; | ||||
| 1061 | int y = data->y; | ||||
| 1062 | unsigned long line_count = text->physical_line_count; | ||||
| 1063 | float line_height = textplain_line_height(); | ||||
| 1064 | float scaled_line_height = line_height * data->scale; | ||||
| 1065 | long line0 = (clip->y0 - y * data->scale) / scaled_line_height - 1; | ||||
| 1066 | long line1 = (clip->y1 - y * data->scale) / scaled_line_height + 1; | ||||
| 1067 | struct textplain_line *line = text->physical_line; | ||||
| 1068 | size_t length; | ||||
| 1069 | plot_style_t *plot_style_highlight; | ||||
| 1070 | nserror res; | ||||
| 1071 | |||||
| 1072 | if (line0 < 0) | ||||
| 1073 | line0 = 0; | ||||
| 1074 | if (line1 < 0) | ||||
| 1075 | line1 = 0; | ||||
| 1076 | if (line_count < (unsigned long) line0) | ||||
| 1077 | line0 = line_count; | ||||
| 1078 | if (line_count < (unsigned long) line1) | ||||
| 1079 | line1 = line_count; | ||||
| 1080 | if (line1 < line0) | ||||
| 1081 | line1 = line0; | ||||
| 1082 | |||||
| 1083 | res = ctx->plot->rectangle(ctx, plot_style_fill_white, clip); | ||||
| 1084 | if (res != NSERROR_OK) { | ||||
| 1085 | return false0; | ||||
| 1086 | } | ||||
| 1087 | |||||
| 1088 | if (!line) | ||||
| 1089 | return true1; | ||||
| 1090 | |||||
| 1091 | /* choose a suitable background colour for any highlighted text */ | ||||
| 1092 | if ((data->background_colour & 0x808080) == 0x808080) | ||||
| 1093 | plot_style_highlight = plot_style_fill_black; | ||||
| 1094 | else | ||||
| 1095 | plot_style_highlight = plot_style_fill_white; | ||||
| 1096 | |||||
| 1097 | /* Set up font plot style */ | ||||
| 1098 | textplain_style.background = data->background_colour; | ||||
| 1099 | |||||
| 1100 | x = (x + MARGIN4) * data->scale; | ||||
| 1101 | y = (y + MARGIN4) * data->scale; | ||||
| 1102 | for (lineno = line0; lineno != line1; lineno++) { | ||||
| 1103 | const char *text_d = utf8_data + line[lineno].start; | ||||
| 1104 | int tab_width = textplain_tab_width * data->scale; | ||||
| 1105 | size_t offset = 0; | ||||
| 1106 | int tx = x; | ||||
| 1107 | |||||
| 1108 | if (!tab_width) tab_width = 1; | ||||
| 1109 | |||||
| 1110 | length = line[lineno].length; | ||||
| 1111 | if (!length) | ||||
| 1112 | continue; | ||||
| 1113 | |||||
| 1114 | while (offset < length) { | ||||
| 1115 | size_t next_offset = offset; | ||||
| 1116 | int width; | ||||
| 1117 | int ntx; | ||||
| 1118 | nserror res; | ||||
| 1119 | |||||
| 1120 | while ((next_offset < length) && | ||||
| 1121 | (text_d[next_offset] != '\t')) { | ||||
| 1122 | next_offset = utf8_next(text_d, | ||||
| 1123 | length, | ||||
| 1124 | next_offset); | ||||
| 1125 | } | ||||
| 1126 | |||||
| 1127 | if (!text_draw(text_d + offset, | ||||
| 1128 | next_offset - offset, | ||||
| 1129 | line[lineno].start + offset, | ||||
| 1130 | tx, | ||||
| 1131 | y + (lineno * scaled_line_height), | ||||
| 1132 | clip, | ||||
| 1133 | line_height, | ||||
| 1134 | data->scale, | ||||
| 1135 | text, | ||||
| 1136 | text->sel, | ||||
| 1137 | ctx)) { | ||||
| 1138 | return false0; | ||||
| 1139 | } | ||||
| 1140 | |||||
| 1141 | if (next_offset >= length) | ||||
| 1142 | break; | ||||
| 1143 | |||||
| 1144 | res = guit->layout->width(&textplain_style, | ||||
| 1145 | &text_d[offset], | ||||
| 1146 | next_offset - offset, | ||||
| 1147 | &width); | ||||
| 1148 | /* locate end of string and align to next tab position */ | ||||
| 1149 | if (res == NSERROR_OK) { | ||||
| 1150 | tx += (int)(width * data->scale); | ||||
| 1151 | } | ||||
| 1152 | |||||
| 1153 | ntx = x + ((1 + (tx - x) / tab_width) * tab_width); | ||||
| 1154 | |||||
| 1155 | /* if the tab character lies within the | ||||
| 1156 | * selection, if any, then we must draw it as | ||||
| 1157 | * a filled rectangle so that it's consistent | ||||
| 1158 | * with background of the selected text | ||||
| 1159 | */ | ||||
| 1160 | |||||
| 1161 | if (bw) { | ||||
| 1162 | unsigned tab_ofst = line[lineno].start + next_offset; | ||||
| 1163 | struct selection *sel = text->sel; | ||||
| 1164 | bool_Bool highlighted = false0; | ||||
| 1165 | |||||
| 1166 | unsigned start_idx, end_idx; | ||||
| 1167 | if (selection_highlighted(sel, | ||||
| 1168 | tab_ofst, | ||||
| 1169 | tab_ofst + 1, | ||||
| 1170 | &start_idx, | ||||
| 1171 | &end_idx)) { | ||||
| 1172 | highlighted = true1; | ||||
| 1173 | } | ||||
| 1174 | |||||
| 1175 | |||||
| 1176 | if (!highlighted && | ||||
| 1177 | (c->textsearch.context != NULL((void*)0))) { | ||||
| 1178 | unsigned start_idx, end_idx; | ||||
| 1179 | if (content_textsearch_ishighlighted( | ||||
| 1180 | c->textsearch.context, | ||||
| 1181 | tab_ofst, | ||||
| 1182 | tab_ofst + 1, | ||||
| 1183 | &start_idx, | ||||
| 1184 | &end_idx)) { | ||||
| 1185 | highlighted = true1; | ||||
| 1186 | } | ||||
| 1187 | } | ||||
| 1188 | |||||
| 1189 | if (highlighted) { | ||||
| 1190 | struct rect rect; | ||||
| 1191 | rect.x0 = tx; | ||||
| 1192 | rect.y0 = y + (lineno * scaled_line_height); | ||||
| 1193 | rect.x1 = ntx; | ||||
| 1194 | rect.y1 = rect.y0 + scaled_line_height; | ||||
| 1195 | res = ctx->plot->rectangle(ctx, | ||||
| 1196 | plot_style_highlight, | ||||
| 1197 | &rect); | ||||
| 1198 | if (res != NSERROR_OK) { | ||||
| 1199 | return false0; | ||||
| 1200 | } | ||||
| 1201 | } | ||||
| 1202 | } | ||||
| 1203 | |||||
| 1204 | offset = next_offset + 1; | ||||
| 1205 | tx = ntx; | ||||
| 1206 | } | ||||
| 1207 | } | ||||
| 1208 | |||||
| 1209 | return true1; | ||||
| 1210 | } | ||||
| 1211 | |||||
| 1212 | |||||
| 1213 | /** | ||||
| 1214 | * Handle a window containing a CONTENT_TEXTPLAIN being opened. | ||||
| 1215 | */ | ||||
| 1216 | static nserror | ||||
| 1217 | textplain_open(struct content *c, | ||||
| 1218 | struct browser_window *bw, | ||||
| 1219 | struct content *page, | ||||
| 1220 | struct object_params *params) | ||||
| 1221 | { | ||||
| 1222 | textplain_content *text = (textplain_content *) c; | ||||
| 1223 | |||||
| 1224 | text->bw = bw; | ||||
| 1225 | |||||
| 1226 | /* text selection */ | ||||
| 1227 | selection_init(text->sel); | ||||
| 1228 | |||||
| 1229 | return NSERROR_OK; | ||||
| 1230 | } | ||||
| 1231 | |||||
| 1232 | |||||
| 1233 | /** | ||||
| 1234 | * Handle a window containing a CONTENT_TEXTPLAIN being closed. | ||||
| 1235 | */ | ||||
| 1236 | static nserror textplain_close(struct content *c) | ||||
| 1237 | { | ||||
| 1238 | textplain_content *text = (textplain_content *) c; | ||||
| 1239 | |||||
| 1240 | text->bw = NULL((void*)0); | ||||
| 1241 | |||||
| 1242 | return NSERROR_OK; | ||||
| 1243 | } | ||||
| 1244 | |||||
| 1245 | |||||
| 1246 | /** | ||||
| 1247 | * Return an textplain content's selection context | ||||
| 1248 | */ | ||||
| 1249 | static char *textplain_get_selection(struct content *c) | ||||
| 1250 | { | ||||
| 1251 | textplain_content *text = (textplain_content *) c; | ||||
| 1252 | |||||
| 1253 | return selection_get_copy(text->sel); | ||||
| 1254 | } | ||||
| 1255 | |||||
| 1256 | |||||
| 1257 | /** | ||||
| 1258 | * Convert a character offset within a line of text into the | ||||
| 1259 | * horizontal co-ordinate | ||||
| 1260 | * | ||||
| 1261 | * The conversion takes into account the font being used and any tabs | ||||
| 1262 | * in the text | ||||
| 1263 | * | ||||
| 1264 | * \param text line of text | ||||
| 1265 | * \param offset char offset within text | ||||
| 1266 | * \param length line length | ||||
| 1267 | * \return x ordinate | ||||
| 1268 | */ | ||||
| 1269 | static int | ||||
| 1270 | textplain_coord_from_offset(const char *text, size_t offset, size_t length) | ||||
| 1271 | { | ||||
| 1272 | int x = 0; | ||||
| 1273 | |||||
| 1274 | while (offset > 0) { | ||||
| 1275 | size_t next_offset = 0; | ||||
| 1276 | int tx; | ||||
| 1277 | |||||
| 1278 | while (next_offset < offset && text[next_offset] != '\t') { | ||||
| 1279 | next_offset = utf8_next(text, length, next_offset); | ||||
| 1280 | } | ||||
| 1281 | |||||
| 1282 | guit->layout->width(&textplain_style, text, next_offset, &tx); | ||||
| 1283 | |||||
| 1284 | x += tx; | ||||
| 1285 | |||||
| 1286 | if (next_offset >= offset) | ||||
| 1287 | break; | ||||
| 1288 | |||||
| 1289 | /* align to next tab boundary */ | ||||
| 1290 | next_offset++; | ||||
| 1291 | x = (1 + (x / textplain_tab_width)) * textplain_tab_width; | ||||
| 1292 | offset -= next_offset; | ||||
| 1293 | text += next_offset; | ||||
| 1294 | length -= next_offset; | ||||
| 1295 | } | ||||
| 1296 | |||||
| 1297 | return x; | ||||
| 1298 | } | ||||
| 1299 | |||||
| 1300 | |||||
| 1301 | /** | ||||
| 1302 | * Retrieve number of lines in content | ||||
| 1303 | * | ||||
| 1304 | * \param[in] c Content to retrieve line count from | ||||
| 1305 | * \return Number of lines | ||||
| 1306 | */ | ||||
| 1307 | static unsigned long textplain_line_count(struct content *c) | ||||
| 1308 | { | ||||
| 1309 | textplain_content *text = (textplain_content *) c; | ||||
| 1310 | |||||
| 1311 | assert(c != NULL)((c != ((void*)0)) ? (void) (0) : __assert_fail ("c != NULL", "content/handlers/text/textplain.c", 1311, __extension__ __PRETTY_FUNCTION__ )); | ||||
| 1312 | |||||
| 1313 | return text->physical_line_count; | ||||
| 1314 | } | ||||
| 1315 | |||||
| 1316 | |||||
| 1317 | /** | ||||
| 1318 | * Return a pointer to the requested line of text. | ||||
| 1319 | * | ||||
| 1320 | * \param[in] c content of type CONTENT_TEXTPLAIN | ||||
| 1321 | * \param[in] lineno line number | ||||
| 1322 | * \param[out] poffset receives byte offset of line start within text | ||||
| 1323 | * \param[out] plen receives length of returned line | ||||
| 1324 | * \return pointer to text, or NULL if invalid line number | ||||
| 1325 | */ | ||||
| 1326 | static char * | ||||
| 1327 | textplain_get_line(struct content *c, | ||||
| 1328 | unsigned lineno, | ||||
| 1329 | size_t *poffset, | ||||
| 1330 | size_t *plen) | ||||
| 1331 | { | ||||
| 1332 | textplain_content *text = (textplain_content *) c; | ||||
| 1333 | struct textplain_line *line; | ||||
| 1334 | |||||
| 1335 | assert(c != NULL)((c != ((void*)0)) ? (void) (0) : __assert_fail ("c != NULL", "content/handlers/text/textplain.c", 1335, __extension__ __PRETTY_FUNCTION__ )); | ||||
| 1336 | |||||
| 1337 | if (lineno >= text->physical_line_count) | ||||
| 1338 | return NULL((void*)0); | ||||
| 1339 | line = &text->physical_line[lineno]; | ||||
| 1340 | |||||
| 1341 | *poffset = line->start; | ||||
| 1342 | *plen = line->length; | ||||
| 1343 | return text->utf8_data + line->start; | ||||
| 1344 | } | ||||
| 1345 | |||||
| 1346 | |||||
| 1347 | /** | ||||
| 1348 | * Find line number of byte in text | ||||
| 1349 | * | ||||
| 1350 | * Given a byte offset within the text, return the line number | ||||
| 1351 | * of the line containing that offset. | ||||
| 1352 | * | ||||
| 1353 | * \param[in] c content of type CONTENT_TEXTPLAIN | ||||
| 1354 | * \param[in] offset byte offset within textual representation | ||||
| 1355 | * \return line number, or -1 if offset invalid (larger than size) | ||||
| 1356 | */ | ||||
| 1357 | static int textplain_find_line(struct content *c, unsigned offset) | ||||
| 1358 | { | ||||
| 1359 | textplain_content *text = (textplain_content *) c; | ||||
| 1360 | struct textplain_line *line; | ||||
| 1361 | int nlines; | ||||
| 1362 | int lineno = 0; | ||||
| 1363 | |||||
| 1364 | assert(c != NULL)((c != ((void*)0)) ? (void) (0) : __assert_fail ("c != NULL", "content/handlers/text/textplain.c", 1364, __extension__ __PRETTY_FUNCTION__ )); | ||||
| 1365 | |||||
| 1366 | line = text->physical_line; | ||||
| 1367 | nlines = text->physical_line_count; | ||||
| 1368 | |||||
| 1369 | if (offset > text->utf8_data_size) { | ||||
| 1370 | return -1; | ||||
| 1371 | } | ||||
| 1372 | |||||
| 1373 | /* \todo - implement binary search here */ | ||||
| 1374 | while (lineno < nlines && line[lineno].start < offset) { | ||||
| 1375 | lineno++; | ||||
| 1376 | } | ||||
| 1377 | if (line[lineno].start > offset) { | ||||
| 1378 | lineno--; | ||||
| 1379 | } | ||||
| 1380 | |||||
| 1381 | return lineno; | ||||
| 1382 | } | ||||
| 1383 | |||||
| 1384 | |||||
| 1385 | /** | ||||
| 1386 | * Finds all occurrences of a given string in a textplain content | ||||
| 1387 | * | ||||
| 1388 | * \param c the content to be searched | ||||
| 1389 | * \param context The search context to add the entry to. | ||||
| 1390 | * \param pattern the string pattern to search for | ||||
| 1391 | * \param p_len pattern length | ||||
| 1392 | * \param case_sens whether to perform a case sensitive search | ||||
| 1393 | * \return NSERROR_OK on success else error code on faliure | ||||
| 1394 | */ | ||||
| 1395 | static nserror | ||||
| 1396 | textplain_textsearch_find(struct content *c, | ||||
| 1397 | struct textsearch_context *context, | ||||
| 1398 | const char *pattern, | ||||
| 1399 | int p_len, | ||||
| 1400 | bool_Bool case_sens) | ||||
| 1401 | { | ||||
| 1402 | int nlines = textplain_line_count(c); | ||||
| 1403 | int line; | ||||
| 1404 | nserror res = NSERROR_OK; | ||||
| 1405 | |||||
| 1406 | for(line = 0; line < nlines; line++) { | ||||
| 1407 | size_t offset, length; | ||||
| 1408 | const char *text; | ||||
| 1409 | |||||
| 1410 | text = textplain_get_line(c, line, &offset, &length); | ||||
| 1411 | if (text) { | ||||
| 1412 | while (length > 0) { | ||||
| 1413 | unsigned match_length; | ||||
| 1414 | size_t start_idx; | ||||
| 1415 | const char *new_text; | ||||
| 1416 | const char *pos; | ||||
| 1417 | |||||
| 1418 | pos = content_textsearch_find_pattern( | ||||
| 1419 | text, | ||||
| 1420 | length, | ||||
| 1421 | pattern, | ||||
| 1422 | p_len, | ||||
| 1423 | case_sens, | ||||
| 1424 | &match_length); | ||||
| 1425 | if (!pos) | ||||
| 1426 | break; | ||||
| 1427 | |||||
| 1428 | /* found string in line => add to list */ | ||||
| 1429 | start_idx = offset + (pos - text); | ||||
| 1430 | res = content_textsearch_add_match(context, | ||||
| 1431 | start_idx, | ||||
| 1432 | start_idx + match_length, | ||||
| 1433 | NULL((void*)0), | ||||
| 1434 | NULL((void*)0)); | ||||
| 1435 | if (res != NSERROR_OK) { | ||||
| 1436 | return res; | ||||
| 1437 | } | ||||
| 1438 | |||||
| 1439 | new_text = pos + match_length; | ||||
| 1440 | offset += (new_text - text); | ||||
| 1441 | length -= (new_text - text); | ||||
| 1442 | text = new_text; | ||||
| 1443 | } | ||||
| 1444 | } | ||||
| 1445 | } | ||||
| 1446 | |||||
| 1447 | return res; | ||||
| 1448 | } | ||||
| 1449 | |||||
| 1450 | |||||
| 1451 | /** | ||||
| 1452 | * Given a range of byte offsets within a UTF8 textplain content, | ||||
| 1453 | * return a box that fully encloses the text | ||||
| 1454 | * | ||||
| 1455 | * \param[in] c content of type CONTENT_TEXTPLAIN | ||||
| 1456 | * \param[in] start byte offset of start of text range | ||||
| 1457 | * \param[in] end byte offset of end | ||||
| 1458 | * \param[out] r rectangle to be completed | ||||
| 1459 | */ | ||||
| 1460 | static void | ||||
| 1461 | textplain_coords_from_range(struct content *c, | ||||
| 1462 | unsigned start, | ||||
| 1463 | unsigned end, | ||||
| 1464 | struct rect *r) | ||||
| 1465 | { | ||||
| 1466 | textplain_content *text = (textplain_content *) c; | ||||
| 1467 | float line_height = textplain_line_height(); | ||||
| 1468 | char *utf8_data; | ||||
| 1469 | struct textplain_line *line; | ||||
| 1470 | unsigned lineno = 0; | ||||
| 1471 | unsigned nlines; | ||||
| 1472 | |||||
| 1473 | assert(c != NULL)((c != ((void*)0)) ? (void) (0) : __assert_fail ("c != NULL", "content/handlers/text/textplain.c", 1473, __extension__ __PRETTY_FUNCTION__ )); | ||||
| 1474 | assert(start <= end)((start <= end) ? (void) (0) : __assert_fail ("start <= end" , "content/handlers/text/textplain.c", 1474, __extension__ __PRETTY_FUNCTION__ )); | ||||
| 1475 | assert(end <= text->utf8_data_size)((end <= text->utf8_data_size) ? (void) (0) : __assert_fail ("end <= text->utf8_data_size", "content/handlers/text/textplain.c" , 1475, __extension__ __PRETTY_FUNCTION__)); | ||||
| 1476 | |||||
| 1477 | utf8_data = text->utf8_data; | ||||
| 1478 | nlines = text->physical_line_count; | ||||
| 1479 | line = text->physical_line; | ||||
| 1480 | |||||
| 1481 | /* find start */ | ||||
| 1482 | lineno = textplain_find_line(c, start); | ||||
| 1483 | |||||
| 1484 | r->y0 = (int)(MARGIN4 + lineno * line_height); | ||||
| 1485 | |||||
| 1486 | if (lineno + 1 <= nlines || line[lineno + 1].start >= end) { | ||||
| 1487 | /* \todo - it may actually be more efficient just to | ||||
| 1488 | * run forwards most of the time | ||||
| 1489 | */ | ||||
| 1490 | |||||
| 1491 | /* find end */ | ||||
| 1492 | lineno = textplain_find_line(c, end); | ||||
| 1493 | |||||
| 1494 | r->x0 = 0; | ||||
| 1495 | r->x1 = text->formatted_width; | ||||
| 1496 | } else { | ||||
| 1497 | /* single line */ | ||||
| 1498 | const char *text = utf8_data + line[lineno].start; | ||||
| 1499 | |||||
| 1500 | r->x0 = textplain_coord_from_offset(text, | ||||
| 1501 | start - line[lineno].start, | ||||
| 1502 | line[lineno].length); | ||||
| 1503 | |||||
| 1504 | r->x1 = textplain_coord_from_offset(text, | ||||
| 1505 | end - line[lineno].start, | ||||
| 1506 | line[lineno].length); | ||||
| 1507 | } | ||||
| 1508 | |||||
| 1509 | r->y1 = (int)(MARGIN4 + (lineno + 1) * line_height); | ||||
| 1510 | } | ||||
| 1511 | |||||
| 1512 | |||||
| 1513 | /** | ||||
| 1514 | * Return a pointer to the raw UTF-8 data, as opposed to the reformatted | ||||
| 1515 | * text to fit the window width. Thus only hard newlines are preserved | ||||
| 1516 | * in the saved/copied text of a selection. | ||||
| 1517 | * | ||||
| 1518 | * \param[in] c content of type CONTENT_TEXTPLAIN | ||||
| 1519 | * \param[in] start starting byte offset within UTF-8 text | ||||
| 1520 | * \param[in] end ending byte offset | ||||
| 1521 | * \param[out] plen receives validated length | ||||
| 1522 | * \return pointer to text, or NULL if no text | ||||
| 1523 | */ | ||||
| 1524 | static char * | ||||
| 1525 | textplain_get_raw_data(struct content *c, | ||||
| 1526 | unsigned start, | ||||
| 1527 | unsigned end, | ||||
| 1528 | size_t *plen) | ||||
| 1529 | { | ||||
| 1530 | textplain_content *text = (textplain_content *) c; | ||||
| 1531 | size_t utf8_size; | ||||
| 1532 | |||||
| 1533 | assert(c != NULL)((c != ((void*)0)) ? (void) (0) : __assert_fail ("c != NULL", "content/handlers/text/textplain.c", 1533, __extension__ __PRETTY_FUNCTION__ )); | ||||
| 1534 | |||||
| 1535 | utf8_size = text->utf8_data_size; | ||||
| 1536 | |||||
| 1537 | /* any text at all? */ | ||||
| 1538 | if (!utf8_size) return NULL((void*)0); | ||||
| 1539 | |||||
| 1540 | /* clamp to valid offset range */ | ||||
| 1541 | if (start >= utf8_size) start = utf8_size; | ||||
| 1542 | if (end >= utf8_size) end = utf8_size; | ||||
| 1543 | |||||
| 1544 | *plen = end - start; | ||||
| 1545 | |||||
| 1546 | return text->utf8_data + start; | ||||
| 1547 | } | ||||
| 1548 | |||||
| 1549 | |||||
| 1550 | /** | ||||
| 1551 | * get bounds of a free text search match | ||||
| 1552 | */ | ||||
| 1553 | static nserror | ||||
| 1554 | textplain_textsearch_bounds(struct content *c, | ||||
| 1555 | unsigned start_idx, | ||||
| 1556 | unsigned end_idx, | ||||
| 1557 | struct box *start_box, | ||||
| 1558 | struct box *end_box, | ||||
| 1559 | struct rect *bounds) | ||||
| 1560 | { | ||||
| 1561 | textplain_coords_from_range(c, start_idx, end_idx, bounds); | ||||
| 1562 | |||||
| 1563 | return NSERROR_OK; | ||||
| 1564 | } | ||||
| 1565 | |||||
| 1566 | |||||
| 1567 | /** | ||||
| 1568 | * invalidate a region based on offsets into the text cauing a redraw | ||||
| 1569 | */ | ||||
| 1570 | static nserror | ||||
| 1571 | textplain_textselection_redraw(struct content *c, | ||||
| 1572 | unsigned start_idx, | ||||
| 1573 | unsigned end_idx) | ||||
| 1574 | { | ||||
| 1575 | struct rect r; | ||||
| 1576 | |||||
| 1577 | if (end_idx <= start_idx) { | ||||
| 1578 | return NSERROR_BAD_PARAMETER; | ||||
| 1579 | } | ||||
| 1580 | |||||
| 1581 | textplain_coords_from_range(c, start_idx, end_idx, &r); | ||||
| 1582 | |||||
| 1583 | content__request_redraw(c, r.x0, r.y0, r.x1 - r.x0, r.y1 - r.y0); | ||||
| 1584 | |||||
| 1585 | return NSERROR_OK; | ||||
| 1586 | } | ||||
| 1587 | |||||
| 1588 | static nserror | ||||
| 1589 | textplain_textselection_copy(struct content *c, | ||||
| 1590 | unsigned start_idx, | ||||
| 1591 | unsigned end_idx, | ||||
| 1592 | struct selection_string *selstr) | ||||
| 1593 | { | ||||
| 1594 | const char *text; | ||||
| 1595 | size_t length; | ||||
| 1596 | bool_Bool res = false0; | ||||
| 1597 | |||||
| 1598 | text = textplain_get_raw_data(c, start_idx, end_idx, &length); | ||||
| 1599 | if (text != NULL((void*)0)) { | ||||
| 1600 | res = selection_string_append(text, length, false0, NULL((void*)0), selstr); | ||||
| 1601 | } | ||||
| 1602 | if (res == false0) { | ||||
| 1603 | return NSERROR_NOMEM; | ||||
| 1604 | } | ||||
| 1605 | return NSERROR_OK; | ||||
| 1606 | } | ||||
| 1607 | |||||
| 1608 | |||||
| 1609 | /** | ||||
| 1610 | * Retrieve the index of the end of the text | ||||
| 1611 | * | ||||
| 1612 | * \param[in] c Content to retrieve size of | ||||
| 1613 | * \return Size, in bytes, of data | ||||
| 1614 | */ | ||||
| 1615 | static nserror | ||||
| 1616 | textplain_textselection_get_end(struct content *c, unsigned *end_idx) | ||||
| 1617 | { | ||||
| 1618 | textplain_content *text = (textplain_content *)c; | ||||
| 1619 | |||||
| 1620 | *end_idx = text->utf8_data_size; | ||||
| 1621 | return NSERROR_OK; | ||||
| 1622 | } | ||||
| 1623 | |||||
| 1624 | |||||
| 1625 | /** | ||||
| 1626 | * plain text content handler table | ||||
| 1627 | */ | ||||
| 1628 | static const content_handler textplain_content_handler = { | ||||
| 1629 | .fini = textplain_fini, | ||||
| 1630 | .create = textplain_create, | ||||
| 1631 | .process_data = textplain_process_data, | ||||
| 1632 | .data_complete = textplain_convert, | ||||
| 1633 | .reformat = textplain_reformat, | ||||
| 1634 | .destroy = textplain_destroy, | ||||
| 1635 | .mouse_track = textplain_mouse_track, | ||||
| 1636 | .mouse_action = textplain_mouse_action, | ||||
| 1637 | .keypress = textplain_keypress, | ||||
| 1638 | .redraw = textplain_redraw, | ||||
| 1639 | .open = textplain_open, | ||||
| 1640 | .close = textplain_close, | ||||
| 1641 | .get_selection = textplain_get_selection, | ||||
| 1642 | .clone = textplain_clone, | ||||
| 1643 | .type = textplain_content_type, | ||||
| 1644 | .textsearch_find = textplain_textsearch_find, | ||||
| 1645 | .textsearch_bounds = textplain_textsearch_bounds, | ||||
| 1646 | .textselection_redraw = textplain_textselection_redraw, | ||||
| 1647 | .textselection_copy = textplain_textselection_copy, | ||||
| 1648 | .textselection_get_end = textplain_textselection_get_end, | ||||
| 1649 | .no_share = true1, | ||||
| 1650 | }; | ||||
| 1651 | |||||
| 1652 | |||||
| 1653 | /* exported interface documented in html/textplain.h */ | ||||
| 1654 | nserror textplain_init(void) | ||||
| 1655 | { | ||||
| 1656 | lwc_error lerror; | ||||
| 1657 | nserror error; | ||||
| 1658 | |||||
| 1659 | lerror = lwc_intern_string("Windows-1252", | ||||
| 1660 | SLEN("Windows-1252")(sizeof(("Windows-1252")) - 1), | ||||
| 1661 | &textplain_default_charset); | ||||
| 1662 | if (lerror != lwc_error_ok) { | ||||
| 1663 | return NSERROR_NOMEM; | ||||
| 1664 | } | ||||
| 1665 | |||||
| 1666 | error = content_factory_register_handler("text/plain", | ||||
| 1667 | &textplain_content_handler); | ||||
| 1668 | if (error != NSERROR_OK) { | ||||
| 1669 | lwc_string_unref(textplain_default_charset){ lwc_string *__lwc_s = (textplain_default_charset); if (__lwc_s != ((void*)0)) { __lwc_s->refcnt--; if ((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1) && (__lwc_s-> insensitive == __lwc_s))) lwc_string_destroy(__lwc_s); } }; | ||||
| 1670 | } | ||||
| 1671 | |||||
| 1672 | error = content_factory_register_handler("application/json", | ||||
| 1673 | &textplain_content_handler); | ||||
| 1674 | if (error != NSERROR_OK) { | ||||
| 1675 | lwc_string_unref(textplain_default_charset){ lwc_string *__lwc_s = (textplain_default_charset); if (__lwc_s != ((void*)0)) { __lwc_s->refcnt--; if ((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1) && (__lwc_s-> insensitive == __lwc_s))) lwc_string_destroy(__lwc_s); } }; | ||||
| 1676 | } | ||||
| 1677 | |||||
| 1678 | return error; | ||||
| 1679 | } | ||||
| 1680 | |||||
| 1681 | |||||
| 1682 | |||||
| 1683 |
| 1 | /* |
| 2 | * This file is part of LibParserUtils. |
| 3 | * Licensed under the MIT License, |
| 4 | * http://www.opensource.org/licenses/mit-license.php |
| 5 | * Copyright 2007 John-Mark Bell <jmb@netsurf-browser.org> |
| 6 | */ |
| 7 | |
| 8 | #ifndef parserutils_input_inputstream_h_ |
| 9 | #define parserutils_input_inputstream_h_ |
| 10 | |
| 11 | #ifdef __cplusplus |
| 12 | extern "C" |
| 13 | { |
| 14 | #endif |
| 15 | |
| 16 | #include <stdbool.h> |
| 17 | #ifndef NDEBUG |
| 18 | #include <stdio.h> |
| 19 | #endif |
| 20 | #include <stdlib.h> |
| 21 | #include <inttypes.h> |
| 22 | |
| 23 | #include <parserutils/errors.h> |
| 24 | #include <parserutils/functypes.h> |
| 25 | #include <parserutils/types.h> |
| 26 | #include <parserutils/charset/utf8.h> |
| 27 | #include <parserutils/utils/buffer.h> |
| 28 | |
| 29 | /** |
| 30 | * Type of charset detection function |
| 31 | */ |
| 32 | typedef parserutils_error (*parserutils_charset_detect_func)( |
| 33 | const uint8_t *data, size_t len, |
| 34 | uint16_t *mibenum, uint32_t *source); |
| 35 | |
| 36 | /** |
| 37 | * Input stream object |
| 38 | */ |
| 39 | typedef struct parserutils_inputstream |
| 40 | { |
| 41 | parserutils_buffer *utf8; /**< Buffer containing UTF-8 data */ |
| 42 | |
| 43 | uint32_t cursor; /**< Byte offset of current position */ |
| 44 | |
| 45 | bool_Bool had_eof; /**< Whether EOF has been reached */ |
| 46 | } parserutils_inputstream; |
| 47 | |
| 48 | /* Create an input stream */ |
| 49 | parserutils_error parserutils_inputstream_create(const char *enc, |
| 50 | uint32_t encsrc, parserutils_charset_detect_func csdetect, |
| 51 | parserutils_inputstream **stream); |
| 52 | /* Destroy an input stream */ |
| 53 | parserutils_error parserutils_inputstream_destroy( |
| 54 | parserutils_inputstream *stream); |
| 55 | |
| 56 | /* Append data to an input stream */ |
| 57 | parserutils_error parserutils_inputstream_append( |
| 58 | parserutils_inputstream *stream, |
| 59 | const uint8_t *data, size_t len); |
| 60 | /* Insert data into stream at current location */ |
| 61 | parserutils_error parserutils_inputstream_insert( |
| 62 | parserutils_inputstream *stream, |
| 63 | const uint8_t *data, size_t len); |
| 64 | |
| 65 | /* Slow form of css_inputstream_peek. */ |
| 66 | parserutils_error parserutils_inputstream_peek_slow( |
| 67 | parserutils_inputstream *stream, |
| 68 | size_t offset, const uint8_t **ptr, size_t *length); |
| 69 | |
| 70 | /** |
| 71 | * Look at the character in the stream that starts at |
| 72 | * offset bytes from the cursor |
| 73 | * |
| 74 | * \param stream Stream to look in |
| 75 | * \param offset Byte offset of start of character |
| 76 | * \param ptr Pointer to location to receive pointer to character data |
| 77 | * \param length Pointer to location to receive character length (in bytes) |
| 78 | * \return PARSERUTILS_OK on success, |
| 79 | * _NEEDDATA on reaching the end of available input, |
| 80 | * _EOF on reaching the end of all input, |
| 81 | * _BADENCODING if the input cannot be decoded, |
| 82 | * _NOMEM on memory exhaustion, |
| 83 | * _BADPARM if bad parameters are passed. |
| 84 | * |
| 85 | * Once the character pointed to by the result of this call has been advanced |
| 86 | * past (i.e. parserutils_inputstream_advance has caused the stream cursor to |
| 87 | * pass over the character), then no guarantee is made as to the validity of |
| 88 | * the data pointed to. Thus, any attempt to dereference the pointer after |
| 89 | * advancing past the data it points to is a bug. |
| 90 | */ |
| 91 | static inline parserutils_error parserutils_inputstream_peek( |
| 92 | parserutils_inputstream *stream, size_t offset, |
| 93 | const uint8_t **ptr, size_t *length) |
| 94 | { |
| 95 | parserutils_error error = PARSERUTILS_OK; |
| 96 | const parserutils_buffer *utf8; |
| 97 | const uint8_t *utf8_data; |
| 98 | size_t len, off, utf8_len; |
| 99 | |
| 100 | if (stream == NULL((void*)0) || ptr == NULL((void*)0) || length == NULL((void*)0)) |
| 101 | return PARSERUTILS_BADPARM; |
| 102 | |
| 103 | #ifndef NDEBUG |
| 104 | #ifdef VERBOSE_INPUTSTREAM |
| 105 | fprintf(stdoutstdout, "Peek: len: %zu cur: %u off: %zu\n", |
| 106 | stream->utf8->length, stream->cursor, offset); |
| 107 | #endif |
| 108 | #ifdef RANDOMISE_INPUTSTREAM |
| 109 | parserutils_buffer_randomise(stream->utf8); |
| 110 | #endif |
| 111 | #endif |
| 112 | |
| 113 | utf8 = stream->utf8; |
| 114 | utf8_data = utf8->data; |
| 115 | utf8_len = utf8->length; |
| 116 | off = stream->cursor + offset; |
| 117 | |
| 118 | #define IS_ASCII(x) (((x) & 0x80) == 0) |
| 119 | |
| 120 | if (off < utf8_len) { |
| 121 | if (IS_ASCII(utf8_data[off])) { |
| 122 | /* Early exit for ASCII case */ |
| 123 | (*length) = 1; |
| 124 | (*ptr) = (utf8_data + off); |
| 125 | return PARSERUTILS_OK; |
| 126 | } else { |
| 127 | error = parserutils_charset_utf8_char_byte_length( |
| 128 | utf8_data + off, &len); |
| 129 | |
| 130 | if (error == PARSERUTILS_OK) { |
| 131 | (*length) = len; |
| 132 | (*ptr) = (utf8_data + off); |
| 133 | return PARSERUTILS_OK; |
| 134 | } else if (error != PARSERUTILS_NEEDDATA) { |
| 135 | return error; |
| 136 | } |
| 137 | } |
| 138 | } |
| 139 | |
| 140 | #undef IS_ASCII |
| 141 | |
| 142 | return parserutils_inputstream_peek_slow(stream, offset, ptr, length); |
| 143 | } |
| 144 | |
| 145 | /** |
| 146 | * Advance the stream's current position |
| 147 | * |
| 148 | * \param stream The stream whose position to advance |
| 149 | * \param bytes The number of bytes to advance |
| 150 | */ |
| 151 | static inline void parserutils_inputstream_advance( |
| 152 | parserutils_inputstream *stream, size_t bytes) |
| 153 | { |
| 154 | if (stream == NULL((void*)0)) |
| 155 | return; |
| 156 | |
| 157 | #if !defined(NDEBUG) && defined(VERBOSE_INPUTSTREAM) |
| 158 | fprintf(stdoutstdout, "Advance: len: %zu cur: %u bytes: %zu\n", |
| 159 | stream->utf8->length, stream->cursor, bytes); |
| 160 | #endif |
| 161 | |
| 162 | if (bytes > stream->utf8->length - stream->cursor) |
| 163 | bytes = stream->utf8->length - stream->cursor; |
| 164 | |
| 165 | if (stream->cursor == stream->utf8->length) |
| 166 | return; |
| 167 | |
| 168 | stream->cursor += bytes; |
| 169 | } |
| 170 | |
| 171 | /* Read the document charset */ |
| 172 | const char *parserutils_inputstream_read_charset( |
| 173 | parserutils_inputstream *stream, uint32_t *source); |
| 174 | /* Change the document charset */ |
| 175 | parserutils_error parserutils_inputstream_change_charset( |
| 176 | parserutils_inputstream *stream, |
| 177 | const char *enc, uint32_t source); |
| 178 | |
| 179 | #ifdef __cplusplus |
| 180 | } |
| 181 | #endif |
| 182 | |
| 183 | #endif |
| 184 |