Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/source/html/css-apply.c @ 3:2c135c81b16c
MERGE: upstream PyMuPDF 1.26.4 with MuPDF 1.26.7
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:44:09 +0200 |
| parents | b50eed0cc0ef |
| children |
comparison
equal
deleted
inserted
replaced
| 0:6015a75abc2d | 3:2c135c81b16c |
|---|---|
| 1 // Copyright (C) 2004-2024 Artifex Software, Inc. | |
| 2 // | |
| 3 // This file is part of MuPDF. | |
| 4 // | |
| 5 // MuPDF is free software: you can redistribute it and/or modify it under the | |
| 6 // terms of the GNU Affero General Public License as published by the Free | |
| 7 // Software Foundation, either version 3 of the License, or (at your option) | |
| 8 // any later version. | |
| 9 // | |
| 10 // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY | |
| 11 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | |
| 12 // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more | |
| 13 // details. | |
| 14 // | |
| 15 // You should have received a copy of the GNU Affero General Public License | |
| 16 // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html> | |
| 17 // | |
| 18 // Alternative licensing terms are available from the licensor. | |
| 19 // For commercial licensing, see <https://www.artifex.com/> or contact | |
| 20 // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, | |
| 21 // CA 94129, USA, for further information. | |
| 22 | |
| 23 #include "mupdf/fitz.h" | |
| 24 #include "html-imp.h" | |
| 25 | |
| 26 #include <string.h> | |
| 27 #include <stdlib.h> | |
| 28 #include <stdio.h> | |
| 29 #include <assert.h> | |
| 30 | |
| 31 static const char *border_width_kw[] = { | |
| 32 "medium", | |
| 33 "thick", | |
| 34 "thin", | |
| 35 }; | |
| 36 | |
| 37 static const char *border_style_kw[] = { | |
| 38 "dashed", | |
| 39 "dotted", | |
| 40 "double", | |
| 41 "groove", | |
| 42 "hidden", | |
| 43 "inset", | |
| 44 "none", | |
| 45 "outset", | |
| 46 "ridge", | |
| 47 "solid", | |
| 48 }; | |
| 49 | |
| 50 static const char *color_kw[] = { | |
| 51 "aqua", | |
| 52 "black", | |
| 53 "blue", | |
| 54 "fuchsia", | |
| 55 "gray", | |
| 56 "green", | |
| 57 "lime", | |
| 58 "maroon", | |
| 59 "navy", | |
| 60 "olive", | |
| 61 "orange", | |
| 62 "purple", | |
| 63 "red", | |
| 64 "silver", | |
| 65 "teal", | |
| 66 "transparent", | |
| 67 "white", | |
| 68 "yellow", | |
| 69 }; | |
| 70 | |
| 71 static const char *list_style_type_kw[] = { | |
| 72 "armenian", | |
| 73 "circle", | |
| 74 "decimal", | |
| 75 "decimal-leading-zero", | |
| 76 "disc", | |
| 77 "georgian", | |
| 78 "lower-alpha", | |
| 79 "lower-greek", | |
| 80 "lower-latin", | |
| 81 "lower-roman", | |
| 82 "none", | |
| 83 "square", | |
| 84 "upper-alpha", | |
| 85 "upper-greek", | |
| 86 "upper-latin", | |
| 87 "upper-roman", | |
| 88 }; | |
| 89 | |
| 90 static const char *list_style_position_kw[] = { | |
| 91 "inside", | |
| 92 "outside", | |
| 93 }; | |
| 94 | |
| 95 static const char *font_style_kw[] = { | |
| 96 "italic", | |
| 97 "oblique", | |
| 98 }; | |
| 99 | |
| 100 static const char *font_variant_kw[] = { | |
| 101 "small-caps", | |
| 102 }; | |
| 103 | |
| 104 static const char *font_weight_kw[] = { | |
| 105 "bold", | |
| 106 "bolder", | |
| 107 "lighter", | |
| 108 }; | |
| 109 | |
| 110 static const char *font_size_kw[] = { | |
| 111 "large", | |
| 112 "larger", | |
| 113 "medium", | |
| 114 "small", | |
| 115 "smaller", | |
| 116 "x-large", | |
| 117 "x-small", | |
| 118 "xx-large", | |
| 119 "xx-small", | |
| 120 }; | |
| 121 | |
| 122 /* Properties to ignore when scanning through font-family. We set font-family | |
| 123 * to the full font shorthand value list because Adobe generates DS strings | |
| 124 * where the font-family comes before the font-size (and not at the end as it's | |
| 125 * supposed to). This lets us scan the font shorthand list without trying to | |
| 126 * look up fonts named "bold", etc. | |
| 127 */ | |
| 128 static const char *font_family_ignore[] = { | |
| 129 ",", | |
| 130 "/", | |
| 131 "bold", | |
| 132 "bolder", | |
| 133 "italic", | |
| 134 "large", | |
| 135 "larger", | |
| 136 "lighter", | |
| 137 "medium", | |
| 138 "oblique", | |
| 139 "small", | |
| 140 "small-caps", | |
| 141 "smaller", | |
| 142 "x-large", | |
| 143 "x-small", | |
| 144 "xx-large", | |
| 145 "xx-small", | |
| 146 }; | |
| 147 | |
| 148 static int | |
| 149 keyword_in_list(const char *name, const char **list, int n) | |
| 150 { | |
| 151 int l = 0; | |
| 152 int r = n - 1; | |
| 153 while (l <= r) | |
| 154 { | |
| 155 int m = (l + r) >> 1; | |
| 156 int c = strcmp(name, list[m]); | |
| 157 if (c < 0) | |
| 158 r = m - 1; | |
| 159 else if (c > 0) | |
| 160 l = m + 1; | |
| 161 else | |
| 162 return 1; | |
| 163 } | |
| 164 return 0; | |
| 165 } | |
| 166 | |
| 167 static int | |
| 168 is_bold_from_font_weight(const char *weight) | |
| 169 { | |
| 170 return !strcmp(weight, "bold") || !strcmp(weight, "bolder") || atoi(weight) > 400; | |
| 171 } | |
| 172 | |
| 173 static int | |
| 174 is_italic_from_font_style(const char *style) | |
| 175 { | |
| 176 return !strcmp(style, "italic") || !strcmp(style, "oblique"); | |
| 177 } | |
| 178 | |
| 179 /* | |
| 180 * Compute specificity | |
| 181 */ | |
| 182 | |
| 183 static int | |
| 184 count_condition_ids(fz_css_condition *cond) | |
| 185 { | |
| 186 int n = 0; | |
| 187 while (cond) | |
| 188 { | |
| 189 if (cond->type == '#') | |
| 190 n ++; | |
| 191 cond = cond->next; | |
| 192 } | |
| 193 return n; | |
| 194 } | |
| 195 | |
| 196 static int | |
| 197 count_selector_ids(fz_css_selector *sel) | |
| 198 { | |
| 199 int n = count_condition_ids(sel->cond); | |
| 200 if (sel->left && sel->right) | |
| 201 { | |
| 202 n += count_selector_ids(sel->left); | |
| 203 n += count_selector_ids(sel->right); | |
| 204 } | |
| 205 return n; | |
| 206 } | |
| 207 | |
| 208 static int | |
| 209 count_condition_atts(fz_css_condition *cond) | |
| 210 { | |
| 211 int n = 0; | |
| 212 while (cond) | |
| 213 { | |
| 214 if (cond->type != '#' && cond->type != ':') | |
| 215 n ++; | |
| 216 cond = cond->next; | |
| 217 } | |
| 218 return n; | |
| 219 } | |
| 220 | |
| 221 static int | |
| 222 count_selector_atts(fz_css_selector *sel) | |
| 223 { | |
| 224 int n = count_condition_atts(sel->cond); | |
| 225 if (sel->left && sel->right) | |
| 226 { | |
| 227 n += count_selector_atts(sel->left); | |
| 228 n += count_selector_atts(sel->right); | |
| 229 } | |
| 230 return n; | |
| 231 } | |
| 232 | |
| 233 static int | |
| 234 count_condition_names(fz_css_condition *cond) | |
| 235 { | |
| 236 int n = 0; | |
| 237 while (cond) | |
| 238 { | |
| 239 if (cond->type == ':') | |
| 240 n ++; | |
| 241 cond = cond->next; | |
| 242 } | |
| 243 return n; | |
| 244 } | |
| 245 | |
| 246 static int | |
| 247 count_selector_names(fz_css_selector *sel) | |
| 248 { | |
| 249 int n = count_condition_names(sel->cond); | |
| 250 if (sel->left && sel->right) | |
| 251 { | |
| 252 n += count_selector_names(sel->left); | |
| 253 n += count_selector_names(sel->right); | |
| 254 } | |
| 255 else if (sel->name) | |
| 256 { | |
| 257 n ++; | |
| 258 } | |
| 259 return n; | |
| 260 } | |
| 261 | |
| 262 #define INLINE_SPECIFICITY 10000 | |
| 263 | |
| 264 static int | |
| 265 selector_specificity(fz_css_selector *sel, int important) | |
| 266 { | |
| 267 int b = count_selector_ids(sel); | |
| 268 int c = count_selector_atts(sel); | |
| 269 int d = count_selector_names(sel); | |
| 270 return important * 1000 + b * 100 + c * 10 + d; | |
| 271 } | |
| 272 | |
| 273 /* | |
| 274 * Selector matching | |
| 275 */ | |
| 276 | |
| 277 static int | |
| 278 match_att_exists_condition(fz_xml *node, const char *key) | |
| 279 { | |
| 280 const char *s = fz_xml_att(node, key); | |
| 281 return s != NULL; | |
| 282 } | |
| 283 | |
| 284 static int | |
| 285 match_att_is_condition(fz_xml *node, const char *key, const char *val) | |
| 286 { | |
| 287 const char *att = fz_xml_att(node, key); | |
| 288 return att && !strcmp(val, att); | |
| 289 } | |
| 290 | |
| 291 static int | |
| 292 match_att_has_condition(fz_xml *node, const char *att, const char *needle) | |
| 293 { | |
| 294 const char *haystack = fz_xml_att(node, att); | |
| 295 const char *ss; | |
| 296 size_t n; | |
| 297 if (haystack) { | |
| 298 ss = strstr(haystack, needle); | |
| 299 if (ss) | |
| 300 { | |
| 301 n = strlen(needle); | |
| 302 | |
| 303 /* Look for exact matches or matching words. */ | |
| 304 if ((ss[n] == ' ' || ss[n] == 0) && (ss == haystack || ss[-1] == ' ')) | |
| 305 return 1; | |
| 306 } | |
| 307 } | |
| 308 return 0; | |
| 309 } | |
| 310 | |
| 311 static int | |
| 312 match_condition(fz_css_condition *cond, fz_xml *node) | |
| 313 { | |
| 314 if (!cond) | |
| 315 return 1; | |
| 316 | |
| 317 switch (cond->type) { | |
| 318 default: return 0; | |
| 319 case ':': return 0; /* don't support pseudo-classes */ | |
| 320 case '#': if (!match_att_is_condition(node, "id", cond->val)) return 0; break; | |
| 321 case '.': if (!match_att_has_condition(node, "class", cond->val)) return 0; break; | |
| 322 case '[': if (!match_att_exists_condition(node, cond->key)) return 0; break; | |
| 323 case '=': if (!match_att_is_condition(node, cond->key, cond->val)) return 0; break; | |
| 324 case '~': if (!match_att_has_condition(node, cond->key, cond->val)) return 0; break; | |
| 325 case '|': if (!match_att_is_condition(node, cond->key, cond->val)) return 0; break; | |
| 326 } | |
| 327 | |
| 328 return match_condition(cond->next, node); | |
| 329 } | |
| 330 | |
| 331 static int | |
| 332 match_selector(fz_css_selector *sel, fz_xml *node) | |
| 333 { | |
| 334 if (!node) | |
| 335 return 0; | |
| 336 | |
| 337 if (sel->combine) | |
| 338 { | |
| 339 /* descendant */ | |
| 340 if (sel->combine == ' ') | |
| 341 { | |
| 342 fz_xml *parent = fz_xml_up(node); | |
| 343 if (!parent || !match_selector(sel->right, node)) | |
| 344 return 0; | |
| 345 | |
| 346 while (parent) | |
| 347 { | |
| 348 if (match_selector(sel->left, parent)) | |
| 349 return 1; | |
| 350 parent = fz_xml_up(parent); | |
| 351 } | |
| 352 return 0; | |
| 353 } | |
| 354 | |
| 355 /* child */ | |
| 356 if (sel->combine == '>') | |
| 357 { | |
| 358 fz_xml *parent = fz_xml_up(node); | |
| 359 if (!parent) | |
| 360 return 0; | |
| 361 if (!match_selector(sel->left, parent)) | |
| 362 return 0; | |
| 363 if (!match_selector(sel->right, node)) | |
| 364 return 0; | |
| 365 } | |
| 366 | |
| 367 /* adjacent */ | |
| 368 if (sel->combine == '+') | |
| 369 { | |
| 370 fz_xml *prev = fz_xml_prev(node); | |
| 371 while (prev && !fz_xml_tag(prev)) | |
| 372 prev = fz_xml_prev(prev); | |
| 373 if (!prev) | |
| 374 return 0; | |
| 375 if (!fz_xml_tag(prev)) | |
| 376 return 0; | |
| 377 if (!match_selector(sel->left, prev)) | |
| 378 return 0; | |
| 379 if (!match_selector(sel->right, node)) | |
| 380 return 0; | |
| 381 } | |
| 382 } | |
| 383 | |
| 384 if (sel->name) | |
| 385 { | |
| 386 if (!fz_xml_is_tag(node, sel->name)) | |
| 387 return 0; | |
| 388 } | |
| 389 | |
| 390 if (sel->cond) | |
| 391 { | |
| 392 if (!match_condition(sel->cond, node)) | |
| 393 return 0; | |
| 394 } | |
| 395 | |
| 396 return 1; | |
| 397 } | |
| 398 | |
| 399 /* | |
| 400 * Annotating nodes with properties and expanding shorthand forms. | |
| 401 */ | |
| 402 | |
| 403 static int | |
| 404 count_values(fz_css_value *value) | |
| 405 { | |
| 406 int n = 0; | |
| 407 while (value) | |
| 408 { | |
| 409 n++; | |
| 410 value = value->next; | |
| 411 } | |
| 412 return n; | |
| 413 } | |
| 414 | |
| 415 static void add_property(fz_css_match *match, int name, fz_css_value *value, int spec); | |
| 416 | |
| 417 static void | |
| 418 add_shorthand_trbl(fz_css_match *match, fz_css_value *value, int spec, | |
| 419 int name_t, int name_r, int name_b, int name_l) | |
| 420 { | |
| 421 int n = count_values(value); | |
| 422 | |
| 423 if (n == 1) | |
| 424 { | |
| 425 add_property(match, name_t, value, spec); | |
| 426 add_property(match, name_r, value, spec); | |
| 427 add_property(match, name_b, value, spec); | |
| 428 add_property(match, name_l, value, spec); | |
| 429 } | |
| 430 | |
| 431 if (n == 2) | |
| 432 { | |
| 433 fz_css_value *a = value; | |
| 434 fz_css_value *b = value->next; | |
| 435 | |
| 436 add_property(match, name_t, a, spec); | |
| 437 add_property(match, name_r, b, spec); | |
| 438 add_property(match, name_b, a, spec); | |
| 439 add_property(match, name_l, b, spec); | |
| 440 } | |
| 441 | |
| 442 if (n == 3) | |
| 443 { | |
| 444 fz_css_value *a = value; | |
| 445 fz_css_value *b = value->next; | |
| 446 fz_css_value *c = value->next->next; | |
| 447 | |
| 448 add_property(match, name_t, a, spec); | |
| 449 add_property(match, name_r, b, spec); | |
| 450 add_property(match, name_b, c, spec); | |
| 451 add_property(match, name_l, b, spec); | |
| 452 } | |
| 453 | |
| 454 if (n == 4) | |
| 455 { | |
| 456 fz_css_value *a = value; | |
| 457 fz_css_value *b = value->next; | |
| 458 fz_css_value *c = value->next->next; | |
| 459 fz_css_value *d = value->next->next->next; | |
| 460 | |
| 461 add_property(match, name_t, a, spec); | |
| 462 add_property(match, name_r, b, spec); | |
| 463 add_property(match, name_b, c, spec); | |
| 464 add_property(match, name_l, d, spec); | |
| 465 } | |
| 466 } | |
| 467 | |
| 468 static void | |
| 469 add_shorthand_margin(fz_css_match *match, fz_css_value *value, int spec) | |
| 470 { | |
| 471 add_shorthand_trbl(match, value, spec, | |
| 472 PRO_MARGIN_TOP, | |
| 473 PRO_MARGIN_RIGHT, | |
| 474 PRO_MARGIN_BOTTOM, | |
| 475 PRO_MARGIN_LEFT); | |
| 476 } | |
| 477 | |
| 478 static void | |
| 479 add_shorthand_padding(fz_css_match *match, fz_css_value *value, int spec) | |
| 480 { | |
| 481 add_shorthand_trbl(match, value, spec, | |
| 482 PRO_PADDING_TOP, | |
| 483 PRO_PADDING_RIGHT, | |
| 484 PRO_PADDING_BOTTOM, | |
| 485 PRO_PADDING_LEFT); | |
| 486 } | |
| 487 | |
| 488 static void | |
| 489 add_shorthand_border_width(fz_css_match *match, fz_css_value *value, int spec) | |
| 490 { | |
| 491 add_shorthand_trbl(match, value, spec, | |
| 492 PRO_BORDER_TOP_WIDTH, | |
| 493 PRO_BORDER_RIGHT_WIDTH, | |
| 494 PRO_BORDER_BOTTOM_WIDTH, | |
| 495 PRO_BORDER_LEFT_WIDTH); | |
| 496 } | |
| 497 | |
| 498 static void | |
| 499 add_shorthand_border_color(fz_css_match *match, fz_css_value *value, int spec) | |
| 500 { | |
| 501 add_shorthand_trbl(match, value, spec, | |
| 502 PRO_BORDER_TOP_COLOR, | |
| 503 PRO_BORDER_RIGHT_COLOR, | |
| 504 PRO_BORDER_BOTTOM_COLOR, | |
| 505 PRO_BORDER_LEFT_COLOR); | |
| 506 } | |
| 507 | |
| 508 static void | |
| 509 add_shorthand_border_style(fz_css_match *match, fz_css_value *value, int spec) | |
| 510 { | |
| 511 add_shorthand_trbl(match, value, spec, | |
| 512 PRO_BORDER_TOP_STYLE, | |
| 513 PRO_BORDER_RIGHT_STYLE, | |
| 514 PRO_BORDER_BOTTOM_STYLE, | |
| 515 PRO_BORDER_LEFT_STYLE); | |
| 516 } | |
| 517 | |
| 518 static void | |
| 519 add_shorthand_border(fz_css_match *match, fz_css_value *value, int spec, int T, int R, int B, int L) | |
| 520 { | |
| 521 while (value) | |
| 522 { | |
| 523 if (value->type == CSS_HASH) | |
| 524 { | |
| 525 if (T) add_property(match, PRO_BORDER_TOP_COLOR, value, spec); | |
| 526 if (R) add_property(match, PRO_BORDER_RIGHT_COLOR, value, spec); | |
| 527 if (B) add_property(match, PRO_BORDER_BOTTOM_COLOR, value, spec); | |
| 528 if (L) add_property(match, PRO_BORDER_LEFT_COLOR, value, spec); | |
| 529 } | |
| 530 else if (value->type == CSS_KEYWORD) | |
| 531 { | |
| 532 if (keyword_in_list(value->data, border_width_kw, nelem(border_width_kw))) | |
| 533 { | |
| 534 if (T) add_property(match, PRO_BORDER_TOP_WIDTH, value, spec); | |
| 535 if (R) add_property(match, PRO_BORDER_RIGHT_WIDTH, value, spec); | |
| 536 if (B) add_property(match, PRO_BORDER_BOTTOM_WIDTH, value, spec); | |
| 537 if (L) add_property(match, PRO_BORDER_LEFT_WIDTH, value, spec); | |
| 538 } | |
| 539 else if (keyword_in_list(value->data, border_style_kw, nelem(border_style_kw))) | |
| 540 { | |
| 541 if (T) add_property(match, PRO_BORDER_TOP_STYLE, value, spec); | |
| 542 if (R) add_property(match, PRO_BORDER_RIGHT_STYLE, value, spec); | |
| 543 if (B) add_property(match, PRO_BORDER_BOTTOM_STYLE, value, spec); | |
| 544 if (L) add_property(match, PRO_BORDER_LEFT_STYLE, value, spec); | |
| 545 } | |
| 546 else if (keyword_in_list(value->data, color_kw, nelem(color_kw))) | |
| 547 { | |
| 548 if (T) add_property(match, PRO_BORDER_TOP_COLOR, value, spec); | |
| 549 if (R) add_property(match, PRO_BORDER_RIGHT_COLOR, value, spec); | |
| 550 if (B) add_property(match, PRO_BORDER_BOTTOM_COLOR, value, spec); | |
| 551 if (L) add_property(match, PRO_BORDER_LEFT_COLOR, value, spec); | |
| 552 } | |
| 553 } | |
| 554 else | |
| 555 { | |
| 556 if (T) add_property(match, PRO_BORDER_TOP_WIDTH, value, spec); | |
| 557 if (R) add_property(match, PRO_BORDER_RIGHT_WIDTH, value, spec); | |
| 558 if (B) add_property(match, PRO_BORDER_BOTTOM_WIDTH, value, spec); | |
| 559 if (L) add_property(match, PRO_BORDER_LEFT_WIDTH, value, spec); | |
| 560 } | |
| 561 value = value->next; | |
| 562 } | |
| 563 } | |
| 564 | |
| 565 static void | |
| 566 add_shorthand_list_style(fz_css_match *match, fz_css_value *value, int spec) | |
| 567 { | |
| 568 while (value) | |
| 569 { | |
| 570 if (value->type == CSS_KEYWORD) | |
| 571 { | |
| 572 if (keyword_in_list(value->data, list_style_type_kw, nelem(list_style_type_kw))) | |
| 573 { | |
| 574 add_property(match, PRO_LIST_STYLE_TYPE, value, spec); | |
| 575 } | |
| 576 else if (keyword_in_list(value->data, list_style_position_kw, nelem(list_style_position_kw))) | |
| 577 { | |
| 578 add_property(match, PRO_LIST_STYLE_POSITION, value, spec); | |
| 579 } | |
| 580 } | |
| 581 value = value->next; | |
| 582 } | |
| 583 } | |
| 584 | |
| 585 static fz_css_value static_value_normal = { CSS_KEYWORD, "normal", NULL, NULL }; | |
| 586 | |
| 587 static fz_css_value * | |
| 588 add_shorthand_font_size(fz_css_match *match, fz_css_value *value, int spec) | |
| 589 { | |
| 590 /* font-size */ | |
| 591 add_property(match, PRO_FONT_SIZE, value, spec); | |
| 592 | |
| 593 /* / line-height */ | |
| 594 if (value->next && value->next->next && !strcmp(value->next->data, "/")) | |
| 595 { | |
| 596 value = value->next->next; | |
| 597 add_property(match, PRO_LINE_HEIGHT, value, spec); | |
| 598 } | |
| 599 | |
| 600 return value; | |
| 601 } | |
| 602 | |
| 603 static void | |
| 604 add_shorthand_font(fz_css_match *match, fz_css_value *value, int spec) | |
| 605 { | |
| 606 fz_css_value *font_style = NULL; | |
| 607 fz_css_value *font_variant = NULL; | |
| 608 fz_css_value *font_weight = NULL; | |
| 609 | |
| 610 /* add the start as font-family for most robust scanning of matching font names */ | |
| 611 add_property(match, PRO_FONT_FAMILY, value, spec); | |
| 612 | |
| 613 /* then look for known style/variant/weight keywords and font-size/line-height */ | |
| 614 for (; value; value = value->next) | |
| 615 { | |
| 616 /* style/variant/weight/size */ | |
| 617 if (value->type == CSS_KEYWORD) | |
| 618 { | |
| 619 if (keyword_in_list(value->data, font_style_kw, nelem(font_style_kw))) | |
| 620 font_style = value; | |
| 621 else if (keyword_in_list(value->data, font_variant_kw, nelem(font_variant_kw))) | |
| 622 font_variant = value; | |
| 623 else if (keyword_in_list(value->data, font_weight_kw, nelem(font_weight_kw))) | |
| 624 font_weight = value; | |
| 625 else if (keyword_in_list(value->data, font_size_kw, nelem(font_size_kw))) | |
| 626 value = add_shorthand_font_size(match, value, spec); | |
| 627 } | |
| 628 else if (value->type == CSS_NUMBER) | |
| 629 font_weight = value; | |
| 630 else if (value->type == CSS_LENGTH || value->type == CSS_PERCENT) | |
| 631 value = add_shorthand_font_size(match, value, spec); | |
| 632 } | |
| 633 | |
| 634 /* set all properties to their initial values if not specified! */ | |
| 635 if (font_style) | |
| 636 add_property(match, PRO_FONT_STYLE, font_style, spec); | |
| 637 else | |
| 638 add_property(match, PRO_FONT_STYLE, &static_value_normal, spec); | |
| 639 | |
| 640 if (font_variant) | |
| 641 add_property(match, PRO_FONT_VARIANT, font_variant, spec); | |
| 642 else | |
| 643 add_property(match, PRO_FONT_VARIANT, &static_value_normal, spec); | |
| 644 | |
| 645 if (font_weight) | |
| 646 add_property(match, PRO_FONT_WEIGHT, font_weight, spec); | |
| 647 else | |
| 648 add_property(match, PRO_FONT_WEIGHT, &static_value_normal, spec); | |
| 649 } | |
| 650 | |
| 651 static void | |
| 652 add_property(fz_css_match *match, int name, fz_css_value *value, int spec) | |
| 653 { | |
| 654 /* shorthand expansions: */ | |
| 655 switch (name) | |
| 656 { | |
| 657 case PRO_MARGIN: | |
| 658 add_shorthand_margin(match, value, spec); | |
| 659 return; | |
| 660 case PRO_PADDING: | |
| 661 add_shorthand_padding(match, value, spec); | |
| 662 return; | |
| 663 case PRO_BORDER_WIDTH: | |
| 664 add_shorthand_border_width(match, value, spec); | |
| 665 return; | |
| 666 case PRO_BORDER_COLOR: | |
| 667 add_shorthand_border_color(match, value, spec); | |
| 668 return; | |
| 669 case PRO_BORDER_STYLE: | |
| 670 add_shorthand_border_style(match, value, spec); | |
| 671 return; | |
| 672 case PRO_BORDER: | |
| 673 add_shorthand_border(match, value, spec, 1, 1, 1, 1); | |
| 674 return; | |
| 675 case PRO_BORDER_TOP: | |
| 676 add_shorthand_border(match, value, spec, 1, 0, 0, 0); | |
| 677 return; | |
| 678 case PRO_BORDER_RIGHT: | |
| 679 add_shorthand_border(match, value, spec, 0, 1, 0, 0); | |
| 680 return; | |
| 681 case PRO_BORDER_BOTTOM: | |
| 682 add_shorthand_border(match, value, spec, 0, 0, 1, 0); | |
| 683 return; | |
| 684 case PRO_BORDER_LEFT: | |
| 685 add_shorthand_border(match, value, spec, 0, 0, 0, 1); | |
| 686 return; | |
| 687 case PRO_LIST_STYLE: | |
| 688 add_shorthand_list_style(match, value, spec); | |
| 689 return; | |
| 690 case PRO_FONT: | |
| 691 add_shorthand_font(match, value, spec); | |
| 692 return; | |
| 693 /* TODO: background */ | |
| 694 } | |
| 695 | |
| 696 if (name < NUM_PROPERTIES && match->spec[name] <= spec) | |
| 697 { | |
| 698 match->value[name] = value; | |
| 699 match->spec[name] = spec; | |
| 700 } | |
| 701 } | |
| 702 | |
| 703 void | |
| 704 fz_match_css(fz_context *ctx, fz_css_match *match, fz_css_match *up, fz_css *css, fz_xml *node) | |
| 705 { | |
| 706 fz_css_rule *rule; | |
| 707 fz_css_selector *sel; | |
| 708 fz_css_property *prop; | |
| 709 const char *s; | |
| 710 int i; | |
| 711 | |
| 712 match->up = up; | |
| 713 for (i = 0; i < NUM_PROPERTIES; ++i) | |
| 714 { | |
| 715 match->spec[i] = -1; | |
| 716 match->value[i] = NULL; | |
| 717 } | |
| 718 | |
| 719 for (rule = css->rule; rule; rule = rule->next) | |
| 720 { | |
| 721 sel = rule->selector; | |
| 722 while (sel) | |
| 723 { | |
| 724 if (match_selector(sel, node)) | |
| 725 { | |
| 726 for (prop = rule->declaration; prop; prop = prop->next) | |
| 727 add_property(match, prop->name, prop->value, selector_specificity(sel, prop->important)); | |
| 728 break; | |
| 729 } | |
| 730 sel = sel->next; | |
| 731 } | |
| 732 } | |
| 733 | |
| 734 if (fz_use_document_css(ctx)) | |
| 735 { | |
| 736 s = fz_xml_att(node, "style"); | |
| 737 if (s) | |
| 738 { | |
| 739 fz_try(ctx) | |
| 740 { | |
| 741 prop = fz_parse_css_properties(ctx, css->pool, s); | |
| 742 while (prop) | |
| 743 { | |
| 744 add_property(match, prop->name, prop->value, INLINE_SPECIFICITY); | |
| 745 prop = prop->next; | |
| 746 } | |
| 747 /* We can "leak" the property here, since it is freed along with the pool allocator. */ | |
| 748 } | |
| 749 fz_catch(ctx) | |
| 750 { | |
| 751 fz_rethrow_if(ctx, FZ_ERROR_SYSTEM); | |
| 752 fz_report_error(ctx); | |
| 753 fz_warn(ctx, "ignoring style attribute"); | |
| 754 } | |
| 755 } | |
| 756 } | |
| 757 } | |
| 758 | |
| 759 void | |
| 760 fz_match_css_at_page(fz_context *ctx, fz_css_match *match, fz_css *css) | |
| 761 { | |
| 762 fz_css_rule *rule; | |
| 763 fz_css_selector *sel; | |
| 764 fz_css_property *prop; | |
| 765 int i; | |
| 766 | |
| 767 match->up = NULL; | |
| 768 for (i = 0; i < NUM_PROPERTIES; ++i) | |
| 769 { | |
| 770 match->spec[i] = -1; | |
| 771 match->value[i] = NULL; | |
| 772 } | |
| 773 | |
| 774 for (rule = css->rule; rule; rule = rule->next) | |
| 775 { | |
| 776 sel = rule->selector; | |
| 777 while (sel) | |
| 778 { | |
| 779 if (sel->name && !strcmp(sel->name, "@page")) | |
| 780 { | |
| 781 for (prop = rule->declaration; prop; prop = prop->next) | |
| 782 add_property(match, prop->name, prop->value, selector_specificity(sel, prop->important)); | |
| 783 break; | |
| 784 } | |
| 785 sel = sel->next; | |
| 786 } | |
| 787 } | |
| 788 } | |
| 789 | |
| 790 void | |
| 791 fz_add_css_font_face(fz_context *ctx, fz_html_font_set *set, fz_archive *zip, const char *base_uri, fz_css_property *declaration) | |
| 792 { | |
| 793 fz_html_font_face *custom; | |
| 794 fz_css_property *prop; | |
| 795 fz_font *font = NULL; | |
| 796 fz_buffer *buf = NULL; | |
| 797 fz_stream *stm = NULL; | |
| 798 int is_bold, is_italic, is_small_caps; | |
| 799 char path[2048]; | |
| 800 | |
| 801 const char *family = "serif"; | |
| 802 const char *weight = "normal"; | |
| 803 const char *style = "normal"; | |
| 804 const char *variant = "normal"; | |
| 805 const char *src = NULL; | |
| 806 | |
| 807 for (prop = declaration; prop; prop = prop->next) | |
| 808 { | |
| 809 if (prop->name == PRO_FONT_FAMILY) family = prop->value->data; | |
| 810 if (prop->name == PRO_FONT_WEIGHT) weight = prop->value->data; | |
| 811 if (prop->name == PRO_FONT_STYLE) style = prop->value->data; | |
| 812 if (prop->name == PRO_FONT_VARIANT) variant = prop->value->data; | |
| 813 if (prop->name == PRO_SRC) src = prop->value->data; | |
| 814 } | |
| 815 | |
| 816 if (!src) | |
| 817 return; | |
| 818 | |
| 819 is_bold = is_bold_from_font_weight(weight); | |
| 820 is_italic = is_italic_from_font_style(style); | |
| 821 is_small_caps = !strcmp(variant, "small-caps"); | |
| 822 | |
| 823 fz_strlcpy(path, base_uri, sizeof path); | |
| 824 fz_strlcat(path, "/", sizeof path); | |
| 825 fz_strlcat(path, src, sizeof path); | |
| 826 fz_urldecode(path); | |
| 827 | |
| 828 for (custom = set->custom; custom; custom = custom->next) | |
| 829 if (!strcmp(custom->src, path) && !strcmp(custom->family, family) && | |
| 830 custom->is_bold == is_bold && | |
| 831 custom->is_italic == is_italic && | |
| 832 custom->is_small_caps == is_small_caps) | |
| 833 return; /* already loaded */ | |
| 834 | |
| 835 fz_var(buf); | |
| 836 fz_var(font); | |
| 837 fz_var(stm); | |
| 838 | |
| 839 fz_try(ctx) | |
| 840 { | |
| 841 if (fz_has_archive_entry(ctx, zip, path)) | |
| 842 buf = fz_read_archive_entry(ctx, zip, path); | |
| 843 else | |
| 844 { | |
| 845 stm = fz_try_open_file(ctx, src); | |
| 846 if (stm == NULL) | |
| 847 fz_throw(ctx, FZ_ERROR_FORMAT, "cannot locate font '%s' specified by css", src); | |
| 848 buf = fz_read_all(ctx, stm, 0); | |
| 849 } | |
| 850 font = fz_new_font_from_buffer(ctx, NULL, buf, 0, 0); | |
| 851 fz_add_html_font_face(ctx, set, family, is_bold, is_italic, is_small_caps, path, font); | |
| 852 } | |
| 853 fz_always(ctx) | |
| 854 { | |
| 855 fz_drop_buffer(ctx, buf); | |
| 856 fz_drop_stream(ctx, stm); | |
| 857 fz_drop_font(ctx, font); | |
| 858 } | |
| 859 fz_catch(ctx) | |
| 860 { | |
| 861 fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); | |
| 862 fz_rethrow_if(ctx, FZ_ERROR_SYSTEM); | |
| 863 fz_report_error(ctx); | |
| 864 fz_warn(ctx, "cannot load font-face: %s", src); | |
| 865 } | |
| 866 } | |
| 867 | |
| 868 void | |
| 869 fz_add_css_font_faces(fz_context *ctx, fz_html_font_set *set, fz_archive *zip, const char *base_uri, fz_css *css) | |
| 870 { | |
| 871 fz_css_rule *rule; | |
| 872 fz_css_selector *sel; | |
| 873 | |
| 874 for (rule = css->rule; rule; rule = rule->next) | |
| 875 { | |
| 876 if (!rule->loaded) | |
| 877 { | |
| 878 rule->loaded = 1; | |
| 879 sel = rule->selector; | |
| 880 while (sel) | |
| 881 { | |
| 882 if (sel->name && !strcmp(sel->name, "@font-face")) | |
| 883 { | |
| 884 fz_add_css_font_face(ctx, set, zip, base_uri, rule->declaration); | |
| 885 break; | |
| 886 } | |
| 887 sel = sel->next; | |
| 888 } | |
| 889 } | |
| 890 } | |
| 891 } | |
| 892 | |
| 893 static int | |
| 894 is_inheritable_property(int name) | |
| 895 { | |
| 896 return | |
| 897 name == PRO_COLOR || | |
| 898 name == PRO_DIRECTION || | |
| 899 name == PRO_FONT_FAMILY || | |
| 900 name == PRO_FONT_STYLE || | |
| 901 name == PRO_FONT_VARIANT || | |
| 902 name == PRO_FONT_WEIGHT || | |
| 903 name == PRO_LEADING || | |
| 904 name == PRO_LETTER_SPACING || | |
| 905 name == PRO_LINE_HEIGHT || | |
| 906 name == PRO_LIST_STYLE_IMAGE || | |
| 907 name == PRO_LIST_STYLE_POSITION || | |
| 908 name == PRO_LIST_STYLE_TYPE || | |
| 909 name == PRO_ORPHANS || | |
| 910 name == PRO_OVERFLOW_WRAP || | |
| 911 name == PRO_QUOTES || | |
| 912 name == PRO_TEXT_ALIGN || | |
| 913 name == PRO_TEXT_INDENT || | |
| 914 name == PRO_TEXT_TRANSFORM || | |
| 915 name == PRO_VISIBILITY || | |
| 916 name == PRO_WHITE_SPACE || | |
| 917 name == PRO_WIDOWS || | |
| 918 name == PRO_WORD_SPACING || | |
| 919 // Strictly speaking, text-decoration is not an inherited property, | |
| 920 // but since when drawing an underlined element, all children are also underlined, | |
| 921 // we may as well make it inherited. | |
| 922 name == PRO_TEXT_DECORATION; | |
| 923 } | |
| 924 | |
| 925 static fz_css_value * | |
| 926 value_from_inheritable_property(fz_css_match *match, int name) | |
| 927 { | |
| 928 fz_css_value *value = match->value[name]; | |
| 929 if (match->up) | |
| 930 { | |
| 931 if (value && !strcmp(value->data, "inherit")) | |
| 932 return value_from_inheritable_property(match->up, name); | |
| 933 if (!value) | |
| 934 return value_from_inheritable_property(match->up, name); | |
| 935 } | |
| 936 return value; | |
| 937 } | |
| 938 | |
| 939 static fz_css_value * | |
| 940 value_from_property(fz_css_match *match, int name) | |
| 941 { | |
| 942 fz_css_value *value = match->value[name]; | |
| 943 if (match->up) | |
| 944 { | |
| 945 if (value && !strcmp(value->data, "inherit")) | |
| 946 if (name != PRO_FONT_SIZE) /* never inherit 'font-size' textually */ | |
| 947 return value_from_property(match->up, name); | |
| 948 if (!value && is_inheritable_property(name)) | |
| 949 return value_from_inheritable_property(match->up, name); | |
| 950 } | |
| 951 return value; | |
| 952 } | |
| 953 | |
| 954 static const char * | |
| 955 string_from_property(fz_css_match *match, int name, const char *initial) | |
| 956 { | |
| 957 fz_css_value *value = value_from_property(match, name); | |
| 958 if (!value) | |
| 959 return initial; | |
| 960 return value->data; | |
| 961 } | |
| 962 | |
| 963 static fz_css_number | |
| 964 make_number(float v, int u) | |
| 965 { | |
| 966 fz_css_number n; | |
| 967 n.value = v; | |
| 968 n.unit = u; | |
| 969 return n; | |
| 970 } | |
| 971 | |
| 972 static fz_css_number | |
| 973 make_undefined_number(void) | |
| 974 { | |
| 975 fz_css_number n; | |
| 976 n.value = 0; | |
| 977 n.unit = N_UNDEFINED; | |
| 978 return n; | |
| 979 } | |
| 980 | |
| 981 /* Fast but inaccurate strtof. */ | |
| 982 static float | |
| 983 fz_css_strtof(char *s, char **endptr) | |
| 984 { | |
| 985 float sign = 1; | |
| 986 float v = 0; | |
| 987 float n = 0; | |
| 988 float d = 1; | |
| 989 | |
| 990 if (*s == '-') | |
| 991 { | |
| 992 sign = -1; | |
| 993 ++s; | |
| 994 } | |
| 995 | |
| 996 while (*s >= '0' && *s <= '9') | |
| 997 { | |
| 998 v = v * 10 + (*s - '0'); | |
| 999 ++s; | |
| 1000 } | |
| 1001 | |
| 1002 if (*s == '.') | |
| 1003 { | |
| 1004 ++s; | |
| 1005 while (*s >= '0' && *s <= '9') | |
| 1006 { | |
| 1007 n = n * 10 + (*s - '0'); | |
| 1008 d = d * 10; | |
| 1009 ++s; | |
| 1010 } | |
| 1011 v += n / d; | |
| 1012 } | |
| 1013 | |
| 1014 if (endptr) | |
| 1015 *endptr = s; | |
| 1016 | |
| 1017 return sign * v; | |
| 1018 } | |
| 1019 | |
| 1020 static fz_css_number | |
| 1021 number_from_value(fz_css_value *value, float initial, int initial_unit) | |
| 1022 { | |
| 1023 char *p; | |
| 1024 | |
| 1025 if (!value) | |
| 1026 return make_number(initial, initial_unit); | |
| 1027 | |
| 1028 if (value->type == CSS_PERCENT) | |
| 1029 return make_number(fz_css_strtof(value->data, NULL), N_PERCENT); | |
| 1030 | |
| 1031 if (value->type == CSS_NUMBER) | |
| 1032 return make_number(fz_css_strtof(value->data, NULL), N_NUMBER); | |
| 1033 | |
| 1034 if (value->type == CSS_LENGTH) | |
| 1035 { | |
| 1036 float x = fz_css_strtof(value->data, &p); | |
| 1037 | |
| 1038 if (p[0] == 'e' && p[1] == 'm' && p[2] == 0) | |
| 1039 return make_number(x, N_SCALE); | |
| 1040 if (p[0] == 'e' && p[1] == 'x' && p[2] == 0) | |
| 1041 return make_number(x / 2, N_SCALE); | |
| 1042 | |
| 1043 if (p[0] == 'i' && p[1] == 'n' && p[2] == 0) | |
| 1044 return make_number(x * 72, N_LENGTH); | |
| 1045 if (p[0] == 'c' && p[1] == 'm' && p[2] == 0) | |
| 1046 return make_number(x * 7200 / 254, N_LENGTH); | |
| 1047 if (p[0] == 'm' && p[1] == 'm' && p[2] == 0) | |
| 1048 return make_number(x * 720 / 254, N_LENGTH); | |
| 1049 if (p[0] == 'p' && p[1] == 'c' && p[2] == 0) | |
| 1050 return make_number(x * 12, N_LENGTH); | |
| 1051 | |
| 1052 if (p[0] == 'p' && p[1] == 't' && p[2] == 0) | |
| 1053 return make_number(x, N_LENGTH); | |
| 1054 if (p[0] == 'p' && p[1] == 'x' && p[2] == 0) | |
| 1055 return make_number(x, N_LENGTH); | |
| 1056 | |
| 1057 /* FIXME: 'rem' should be 'em' of root element. This is a bad approximation. */ | |
| 1058 if (p[0] == 'r' && p[1] == 'e' && p[2] == 'm' && p[3] == 0) | |
| 1059 return make_number(x * 16, N_LENGTH); | |
| 1060 | |
| 1061 /* FIXME: 'ch' should be width of '0' character. This is an approximation. */ | |
| 1062 if (p[0] == 'c' && p[1] == 'h' && p[2] == 0) | |
| 1063 return make_number(x / 2, N_LENGTH); | |
| 1064 | |
| 1065 return make_number(x, N_LENGTH); | |
| 1066 } | |
| 1067 | |
| 1068 if (value->type == CSS_KEYWORD) | |
| 1069 { | |
| 1070 if (!strcmp(value->data, "auto")) | |
| 1071 return make_number(0, N_AUTO); | |
| 1072 } | |
| 1073 | |
| 1074 return make_number(initial, initial_unit); | |
| 1075 } | |
| 1076 | |
| 1077 static fz_css_number | |
| 1078 number_from_property(fz_css_match *match, int property, float initial, int initial_unit) | |
| 1079 { | |
| 1080 return number_from_value(value_from_property(match, property), initial, initial_unit); | |
| 1081 } | |
| 1082 | |
| 1083 static fz_css_number | |
| 1084 border_width_from_property(fz_css_match *match, int property) | |
| 1085 { | |
| 1086 fz_css_value *value = value_from_property(match, property); | |
| 1087 if (value) | |
| 1088 { | |
| 1089 if (!strcmp(value->data, "thin")) | |
| 1090 return make_number(1, N_LENGTH); | |
| 1091 if (!strcmp(value->data, "medium")) | |
| 1092 return make_number(2, N_LENGTH); | |
| 1093 if (!strcmp(value->data, "thick")) | |
| 1094 return make_number(4, N_LENGTH); | |
| 1095 return number_from_value(value, 0, N_LENGTH); | |
| 1096 } | |
| 1097 return make_number(2, N_LENGTH); /* initial: 'medium' */ | |
| 1098 } | |
| 1099 | |
| 1100 static int | |
| 1101 border_style_from_property(fz_css_match *match, int property) | |
| 1102 { | |
| 1103 fz_css_value *value = value_from_property(match, property); | |
| 1104 if (value) | |
| 1105 { | |
| 1106 if (!strcmp(value->data, "none")) return BS_NONE; | |
| 1107 else if (!strcmp(value->data, "hidden")) return BS_NONE; | |
| 1108 else if (!strcmp(value->data, "solid")) return BS_SOLID; | |
| 1109 } | |
| 1110 return BS_NONE; | |
| 1111 } | |
| 1112 | |
| 1113 int fz_css_number_defined(fz_css_number number) | |
| 1114 { | |
| 1115 return number.unit != N_UNDEFINED; | |
| 1116 } | |
| 1117 | |
| 1118 float | |
| 1119 fz_from_css_number(fz_css_number number, float em, float percent_value, float auto_value) | |
| 1120 { | |
| 1121 switch (number.unit) { | |
| 1122 default: | |
| 1123 case N_NUMBER: return number.value; | |
| 1124 case N_LENGTH: return number.value; | |
| 1125 case N_SCALE: return number.value * em; | |
| 1126 case N_PERCENT: return number.value * 0.01f * percent_value; | |
| 1127 case N_AUTO: return auto_value; | |
| 1128 } | |
| 1129 } | |
| 1130 | |
| 1131 float | |
| 1132 fz_from_css_number_scale(fz_css_number number, float scale) | |
| 1133 { | |
| 1134 switch (number.unit) { | |
| 1135 default: | |
| 1136 case N_NUMBER: return number.value * scale; | |
| 1137 case N_LENGTH: return number.value; | |
| 1138 case N_SCALE: return number.value * scale; | |
| 1139 case N_PERCENT: return number.value * 0.01f * scale; | |
| 1140 case N_AUTO: return scale; | |
| 1141 } | |
| 1142 } | |
| 1143 | |
| 1144 static fz_css_color | |
| 1145 make_color(int r, int g, int b, int a) | |
| 1146 { | |
| 1147 fz_css_color c; | |
| 1148 c.r = r < 0 ? 0 : r > 255 ? 255 : r; | |
| 1149 c.g = g < 0 ? 0 : g > 255 ? 255 : g; | |
| 1150 c.b = b < 0 ? 0 : b > 255 ? 255 : b; | |
| 1151 c.a = a < 0 ? 0 : a > 255 ? 255 : a; | |
| 1152 return c; | |
| 1153 } | |
| 1154 | |
| 1155 static int tohex(int c) | |
| 1156 { | |
| 1157 if (c - '0' < 10) | |
| 1158 return c - '0'; | |
| 1159 return (c | 32) - 'a' + 10; | |
| 1160 } | |
| 1161 | |
| 1162 static fz_css_color | |
| 1163 color_from_value(fz_css_value *value, fz_css_color initial) | |
| 1164 { | |
| 1165 if (!value) | |
| 1166 return initial; | |
| 1167 | |
| 1168 if (value->type == CSS_HASH) | |
| 1169 { | |
| 1170 int r, g, b, a; | |
| 1171 size_t n; | |
| 1172 hex_color: | |
| 1173 n = strlen(value->data); | |
| 1174 if (n == 3) | |
| 1175 { | |
| 1176 r = tohex(value->data[0]) * 16 + tohex(value->data[0]); | |
| 1177 g = tohex(value->data[1]) * 16 + tohex(value->data[1]); | |
| 1178 b = tohex(value->data[2]) * 16 + tohex(value->data[2]); | |
| 1179 a = 255; | |
| 1180 } | |
| 1181 else if (n == 4) | |
| 1182 { | |
| 1183 r = tohex(value->data[0]) * 16 + tohex(value->data[0]); | |
| 1184 g = tohex(value->data[1]) * 16 + tohex(value->data[1]); | |
| 1185 b = tohex(value->data[2]) * 16 + tohex(value->data[2]); | |
| 1186 a = tohex(value->data[3]) * 16 + tohex(value->data[3]); | |
| 1187 } | |
| 1188 else if (n == 6) | |
| 1189 { | |
| 1190 r = tohex(value->data[0]) * 16 + tohex(value->data[1]); | |
| 1191 g = tohex(value->data[2]) * 16 + tohex(value->data[3]); | |
| 1192 b = tohex(value->data[4]) * 16 + tohex(value->data[5]); | |
| 1193 a = 255; | |
| 1194 } | |
| 1195 else if (n == 8) | |
| 1196 { | |
| 1197 r = tohex(value->data[0]) * 16 + tohex(value->data[1]); | |
| 1198 g = tohex(value->data[2]) * 16 + tohex(value->data[3]); | |
| 1199 b = tohex(value->data[4]) * 16 + tohex(value->data[5]); | |
| 1200 a = tohex(value->data[6]) * 16 + tohex(value->data[7]); | |
| 1201 } | |
| 1202 else | |
| 1203 { | |
| 1204 r = g = b = 0; | |
| 1205 a = 255; | |
| 1206 } | |
| 1207 return make_color(r, g, b, a); | |
| 1208 } | |
| 1209 | |
| 1210 if (value->type == '(' && !strcmp(value->data, "rgb")) | |
| 1211 { | |
| 1212 fz_css_value *vr, *vg, *vb; | |
| 1213 int r, g, b; | |
| 1214 vr = value->args; | |
| 1215 vg = vr && vr->next ? vr->next->next : NULL; /* skip the ',' nodes */ | |
| 1216 vb = vg && vg->next ? vg->next->next : NULL; /* skip the ',' nodes */ | |
| 1217 r = fz_from_css_number(number_from_value(vr, 0, N_NUMBER), 255, 255, 0); | |
| 1218 g = fz_from_css_number(number_from_value(vg, 0, N_NUMBER), 255, 255, 0); | |
| 1219 b = fz_from_css_number(number_from_value(vb, 0, N_NUMBER), 255, 255, 0); | |
| 1220 return make_color(r, g, b, 255); | |
| 1221 } | |
| 1222 | |
| 1223 if (value->type == '(' && !strcmp(value->data, "rgba")) | |
| 1224 { | |
| 1225 fz_css_value *vr, *vg, *vb, *va; | |
| 1226 int r, g, b, a; | |
| 1227 vr = value->args; | |
| 1228 vg = vr && vr->next ? vr->next->next : NULL; /* skip the ',' nodes */ | |
| 1229 vb = vg && vg->next ? vg->next->next : NULL; /* skip the ',' nodes */ | |
| 1230 va = vb && vb->next ? vb->next->next : NULL; /* skip the ',' nodes */ | |
| 1231 r = fz_from_css_number(number_from_value(vr, 0, N_NUMBER), 255, 255, 0); | |
| 1232 g = fz_from_css_number(number_from_value(vg, 0, N_NUMBER), 255, 255, 0); | |
| 1233 b = fz_from_css_number(number_from_value(vb, 0, N_NUMBER), 255, 255, 0); | |
| 1234 a = fz_from_css_number(number_from_value(va, 0, N_NUMBER), 255, 255, 255); | |
| 1235 return make_color(r, g, b, a); | |
| 1236 } | |
| 1237 | |
| 1238 if (value->type == CSS_KEYWORD) | |
| 1239 { | |
| 1240 if (!strcmp(value->data, "transparent")) | |
| 1241 return make_color(0, 0, 0, 0); | |
| 1242 if (!strcmp(value->data, "maroon")) | |
| 1243 return make_color(0x80, 0x00, 0x00, 255); | |
| 1244 if (!strcmp(value->data, "red")) | |
| 1245 return make_color(0xFF, 0x00, 0x00, 255); | |
| 1246 if (!strcmp(value->data, "orange")) | |
| 1247 return make_color(0xFF, 0xA5, 0x00, 255); | |
| 1248 if (!strcmp(value->data, "yellow")) | |
| 1249 return make_color(0xFF, 0xFF, 0x00, 255); | |
| 1250 if (!strcmp(value->data, "olive")) | |
| 1251 return make_color(0x80, 0x80, 0x00, 255); | |
| 1252 if (!strcmp(value->data, "purple")) | |
| 1253 return make_color(0x80, 0x00, 0x80, 255); | |
| 1254 if (!strcmp(value->data, "fuchsia")) | |
| 1255 return make_color(0xFF, 0x00, 0xFF, 255); | |
| 1256 if (!strcmp(value->data, "white")) | |
| 1257 return make_color(0xFF, 0xFF, 0xFF, 255); | |
| 1258 if (!strcmp(value->data, "lime")) | |
| 1259 return make_color(0x00, 0xFF, 0x00, 255); | |
| 1260 if (!strcmp(value->data, "green")) | |
| 1261 return make_color(0x00, 0x80, 0x00, 255); | |
| 1262 if (!strcmp(value->data, "navy")) | |
| 1263 return make_color(0x00, 0x00, 0x80, 255); | |
| 1264 if (!strcmp(value->data, "blue")) | |
| 1265 return make_color(0x00, 0x00, 0xFF, 255); | |
| 1266 if (!strcmp(value->data, "aqua")) | |
| 1267 return make_color(0x00, 0xFF, 0xFF, 255); | |
| 1268 if (!strcmp(value->data, "teal")) | |
| 1269 return make_color(0x00, 0x80, 0x80, 255); | |
| 1270 if (!strcmp(value->data, "black")) | |
| 1271 return make_color(0x00, 0x00, 0x00, 255); | |
| 1272 if (!strcmp(value->data, "silver")) | |
| 1273 return make_color(0xC0, 0xC0, 0xC0, 255); | |
| 1274 if (!strcmp(value->data, "gray")) | |
| 1275 return make_color(0x80, 0x80, 0x80, 255); | |
| 1276 goto hex_color; /* last ditch attempt: maybe it's a #XXXXXX color without the # */ | |
| 1277 } | |
| 1278 return initial; | |
| 1279 } | |
| 1280 | |
| 1281 static fz_css_color | |
| 1282 color_from_property(fz_css_match *match, int property, fz_css_color initial) | |
| 1283 { | |
| 1284 return color_from_value(value_from_property(match, property), initial); | |
| 1285 } | |
| 1286 | |
| 1287 static fz_css_color | |
| 1288 color_from_properties(fz_css_match *match, int property, int property2, fz_css_color initial) | |
| 1289 { | |
| 1290 fz_css_value *value = value_from_property(match, property); | |
| 1291 | |
| 1292 if (value == NULL) | |
| 1293 value = value_from_property(match, property2); | |
| 1294 | |
| 1295 return color_from_value(value, initial); | |
| 1296 } | |
| 1297 | |
| 1298 int | |
| 1299 fz_get_css_match_display(fz_css_match *match) | |
| 1300 { | |
| 1301 fz_css_value *value = value_from_property(match, PRO_DISPLAY); | |
| 1302 if (value) | |
| 1303 { | |
| 1304 if (!strcmp(value->data, "none")) | |
| 1305 return DIS_NONE; | |
| 1306 if (!strcmp(value->data, "inline")) | |
| 1307 return DIS_INLINE; | |
| 1308 if (!strcmp(value->data, "block")) | |
| 1309 return DIS_BLOCK; | |
| 1310 if (!strcmp(value->data, "list-item")) | |
| 1311 return DIS_LIST_ITEM; | |
| 1312 if (!strcmp(value->data, "inline-block")) | |
| 1313 return DIS_INLINE_BLOCK; | |
| 1314 if (!strcmp(value->data, "table")) | |
| 1315 return DIS_TABLE; | |
| 1316 if (!strcmp(value->data, "table-row")) | |
| 1317 return DIS_TABLE_ROW; | |
| 1318 if (!strcmp(value->data, "table-cell")) | |
| 1319 return DIS_TABLE_CELL; | |
| 1320 if (!strcmp(value->data, "table-row-group")) | |
| 1321 return DIS_TABLE_GROUP; | |
| 1322 if (!strcmp(value->data, "table-header-group")) | |
| 1323 return DIS_TABLE_GROUP; | |
| 1324 if (!strcmp(value->data, "table-footer-group")) | |
| 1325 return DIS_TABLE_GROUP; | |
| 1326 if (!strcmp(value->data, "table-column-group")) | |
| 1327 return DIS_NONE; | |
| 1328 if (!strcmp(value->data, "table-column")) | |
| 1329 return DIS_NONE; | |
| 1330 } | |
| 1331 return DIS_INLINE; | |
| 1332 } | |
| 1333 | |
| 1334 static int | |
| 1335 white_space_from_property(fz_css_match *match) | |
| 1336 { | |
| 1337 fz_css_value *value = value_from_property(match, PRO_WHITE_SPACE); | |
| 1338 if (value) | |
| 1339 { | |
| 1340 if (!strcmp(value->data, "normal")) return WS_NORMAL; | |
| 1341 else if (!strcmp(value->data, "pre")) return WS_PRE; | |
| 1342 else if (!strcmp(value->data, "nowrap")) return WS_NOWRAP; | |
| 1343 else if (!strcmp(value->data, "pre-wrap")) return WS_PRE_WRAP; | |
| 1344 else if (!strcmp(value->data, "pre-line")) return WS_PRE_LINE; | |
| 1345 } | |
| 1346 return WS_NORMAL; | |
| 1347 } | |
| 1348 | |
| 1349 static int | |
| 1350 text_decoration_from_property(fz_css_match *match) | |
| 1351 { | |
| 1352 fz_css_value *value = value_from_property(match, PRO_TEXT_DECORATION); | |
| 1353 if (value) | |
| 1354 { | |
| 1355 if (!strcmp(value->data, "underline")) return TD_UNDERLINE; | |
| 1356 if (!strcmp(value->data, "line-through")) return TD_LINE_THROUGH; | |
| 1357 } | |
| 1358 return TD_NONE; | |
| 1359 } | |
| 1360 | |
| 1361 static int | |
| 1362 visibility_from_property(fz_css_match *match) | |
| 1363 { | |
| 1364 fz_css_value *value = value_from_property(match, PRO_VISIBILITY); | |
| 1365 if (value) | |
| 1366 { | |
| 1367 if (!strcmp(value->data, "visible")) return V_VISIBLE; | |
| 1368 else if (!strcmp(value->data, "hidden")) return V_HIDDEN; | |
| 1369 else if (!strcmp(value->data, "collapse")) return V_COLLAPSE; | |
| 1370 } | |
| 1371 return V_VISIBLE; | |
| 1372 } | |
| 1373 | |
| 1374 static int | |
| 1375 page_break_from_property(fz_css_match *match, int prop) | |
| 1376 { | |
| 1377 fz_css_value *value = value_from_property(match, prop); | |
| 1378 if (value) | |
| 1379 { | |
| 1380 if (!strcmp(value->data, "auto")) return PB_AUTO; | |
| 1381 else if (!strcmp(value->data, "always")) return PB_ALWAYS; | |
| 1382 else if (!strcmp(value->data, "avoid")) return PB_AVOID; | |
| 1383 else if (!strcmp(value->data, "left")) return PB_LEFT; | |
| 1384 else if (!strcmp(value->data, "right")) return PB_RIGHT; | |
| 1385 } | |
| 1386 return PB_AUTO; | |
| 1387 } | |
| 1388 | |
| 1389 void | |
| 1390 fz_default_css_style(fz_context *ctx, fz_css_style *style) | |
| 1391 { | |
| 1392 memset(style, 0, sizeof *style); | |
| 1393 style->visibility = V_VISIBLE; | |
| 1394 style->text_align = TA_LEFT; | |
| 1395 style->vertical_align = VA_BASELINE; | |
| 1396 style->white_space = WS_NORMAL; | |
| 1397 style->list_style_type = LST_DISC; | |
| 1398 style->font_size = make_number(1, N_SCALE); | |
| 1399 style->width = make_number(0, N_AUTO); | |
| 1400 style->height = make_number(0, N_AUTO); | |
| 1401 style->leading = make_undefined_number(); | |
| 1402 } | |
| 1403 | |
| 1404 void | |
| 1405 fz_apply_css_style(fz_context *ctx, fz_html_font_set *set, fz_css_style *style, fz_css_match *match) | |
| 1406 { | |
| 1407 fz_css_value *value; | |
| 1408 | |
| 1409 fz_css_color black = { 0, 0, 0, 255 }; | |
| 1410 fz_css_color transparent = { 0, 0, 0, 0 }; | |
| 1411 | |
| 1412 fz_default_css_style(ctx, style); | |
| 1413 | |
| 1414 style->visibility = visibility_from_property(match); | |
| 1415 style->white_space = white_space_from_property(match); | |
| 1416 style->text_decoration = text_decoration_from_property(match); | |
| 1417 style->page_break_before = page_break_from_property(match, PRO_PAGE_BREAK_BEFORE); | |
| 1418 style->page_break_after = page_break_from_property(match, PRO_PAGE_BREAK_AFTER); | |
| 1419 | |
| 1420 value = value_from_property(match, PRO_TEXT_ALIGN); | |
| 1421 if (value) | |
| 1422 { | |
| 1423 if (!strcmp(value->data, "left")) style->text_align = TA_LEFT; | |
| 1424 else if (!strcmp(value->data, "right")) style->text_align = TA_RIGHT; | |
| 1425 else if (!strcmp(value->data, "center")) style->text_align = TA_CENTER; | |
| 1426 else if (!strcmp(value->data, "justify")) style->text_align = TA_JUSTIFY; | |
| 1427 } | |
| 1428 | |
| 1429 value = value_from_property(match, PRO_VERTICAL_ALIGN); | |
| 1430 if (value) | |
| 1431 { | |
| 1432 if (!strcmp(value->data, "baseline")) style->vertical_align = VA_BASELINE; | |
| 1433 else if (!strcmp(value->data, "sub")) style->vertical_align = VA_SUB; | |
| 1434 else if (!strcmp(value->data, "super")) style->vertical_align = VA_SUPER; | |
| 1435 else if (!strcmp(value->data, "top")) style->vertical_align = VA_TOP; | |
| 1436 else if (!strcmp(value->data, "bottom")) style->vertical_align = VA_BOTTOM; | |
| 1437 else if (!strcmp(value->data, "text-top")) style->vertical_align = VA_TEXT_TOP; | |
| 1438 else if (!strcmp(value->data, "text-bottom")) style->vertical_align = VA_TEXT_BOTTOM; | |
| 1439 } | |
| 1440 | |
| 1441 value = value_from_property(match, PRO_FONT_SIZE); | |
| 1442 if (value) | |
| 1443 { | |
| 1444 /* absolute-size */ | |
| 1445 if (!strcmp(value->data, "xx-large")) style->font_size = make_number(1.73f, N_SCALE); | |
| 1446 else if (!strcmp(value->data, "x-large")) style->font_size = make_number(1.44f, N_SCALE); | |
| 1447 else if (!strcmp(value->data, "large")) style->font_size = make_number(1.2f, N_SCALE); | |
| 1448 else if (!strcmp(value->data, "medium")) style->font_size = make_number(1.0f, N_SCALE); | |
| 1449 else if (!strcmp(value->data, "small")) style->font_size = make_number(0.83f, N_SCALE); | |
| 1450 else if (!strcmp(value->data, "x-small")) style->font_size = make_number(0.69f, N_SCALE); | |
| 1451 else if (!strcmp(value->data, "xx-small")) style->font_size = make_number(0.69f, N_SCALE); | |
| 1452 /* relative-size */ | |
| 1453 else if (!strcmp(value->data, "larger")) style->font_size = make_number(1.2f, N_SCALE); | |
| 1454 else if (!strcmp(value->data, "smaller")) style->font_size = make_number(1/1.2f, N_SCALE); | |
| 1455 /* percentage */ | |
| 1456 else if (value->type == CSS_PERCENT) style->font_size = number_from_value(value, 12, N_LENGTH); | |
| 1457 /* length */ | |
| 1458 else if (value->type == CSS_LENGTH) style->font_size = number_from_value(value, 12, N_LENGTH); | |
| 1459 /* default to 1em */ | |
| 1460 else style->font_size = make_number(1, N_SCALE); | |
| 1461 } | |
| 1462 else | |
| 1463 { | |
| 1464 style->font_size = make_number(1, N_SCALE); | |
| 1465 } | |
| 1466 | |
| 1467 value = value_from_property(match, PRO_LIST_STYLE_TYPE); | |
| 1468 if (value) | |
| 1469 { | |
| 1470 if (!strcmp(value->data, "none")) style->list_style_type = LST_NONE; | |
| 1471 else if (!strcmp(value->data, "disc")) style->list_style_type = LST_DISC; | |
| 1472 else if (!strcmp(value->data, "circle")) style->list_style_type = LST_CIRCLE; | |
| 1473 else if (!strcmp(value->data, "square")) style->list_style_type = LST_SQUARE; | |
| 1474 else if (!strcmp(value->data, "decimal")) style->list_style_type = LST_DECIMAL; | |
| 1475 else if (!strcmp(value->data, "decimal-leading-zero")) style->list_style_type = LST_DECIMAL_ZERO; | |
| 1476 else if (!strcmp(value->data, "lower-roman")) style->list_style_type = LST_LC_ROMAN; | |
| 1477 else if (!strcmp(value->data, "upper-roman")) style->list_style_type = LST_UC_ROMAN; | |
| 1478 else if (!strcmp(value->data, "lower-greek")) style->list_style_type = LST_LC_GREEK; | |
| 1479 else if (!strcmp(value->data, "upper-greek")) style->list_style_type = LST_UC_GREEK; | |
| 1480 else if (!strcmp(value->data, "lower-latin")) style->list_style_type = LST_LC_LATIN; | |
| 1481 else if (!strcmp(value->data, "upper-latin")) style->list_style_type = LST_UC_LATIN; | |
| 1482 else if (!strcmp(value->data, "lower-alpha")) style->list_style_type = LST_LC_ALPHA; | |
| 1483 else if (!strcmp(value->data, "upper-alpha")) style->list_style_type = LST_UC_ALPHA; | |
| 1484 else if (!strcmp(value->data, "armenian")) style->list_style_type = LST_ARMENIAN; | |
| 1485 else if (!strcmp(value->data, "georgian")) style->list_style_type = LST_GEORGIAN; | |
| 1486 } | |
| 1487 | |
| 1488 value = value_from_property(match, PRO_OVERFLOW_WRAP); | |
| 1489 if (value) | |
| 1490 { | |
| 1491 if (!strcmp(value->data, "break-word")) style->overflow_wrap = OVERFLOW_WRAP_BREAK_WORD; | |
| 1492 else style->overflow_wrap = OVERFLOW_WRAP_NORMAL; | |
| 1493 } | |
| 1494 | |
| 1495 style->line_height = number_from_property(match, PRO_LINE_HEIGHT, 1.2f, N_SCALE); | |
| 1496 style->leading = number_from_property(match, PRO_LEADING, 0, N_UNDEFINED); | |
| 1497 | |
| 1498 style->text_indent = number_from_property(match, PRO_TEXT_INDENT, 0, N_LENGTH); | |
| 1499 style->text_stroke_width = number_from_property(match, PRO_TEXT_STROKE_WIDTH, 0, N_LENGTH); | |
| 1500 | |
| 1501 style->width = number_from_property(match, PRO_WIDTH, 0, N_AUTO); | |
| 1502 style->height = number_from_property(match, PRO_HEIGHT, 0, N_AUTO); | |
| 1503 | |
| 1504 style->margin[0] = number_from_property(match, PRO_MARGIN_TOP, 0, N_LENGTH); | |
| 1505 style->margin[1] = number_from_property(match, PRO_MARGIN_RIGHT, 0, N_LENGTH); | |
| 1506 style->margin[2] = number_from_property(match, PRO_MARGIN_BOTTOM, 0, N_LENGTH); | |
| 1507 style->margin[3] = number_from_property(match, PRO_MARGIN_LEFT, 0, N_LENGTH); | |
| 1508 | |
| 1509 style->padding[0] = number_from_property(match, PRO_PADDING_TOP, 0, N_LENGTH); | |
| 1510 style->padding[1] = number_from_property(match, PRO_PADDING_RIGHT, 0, N_LENGTH); | |
| 1511 style->padding[2] = number_from_property(match, PRO_PADDING_BOTTOM, 0, N_LENGTH); | |
| 1512 style->padding[3] = number_from_property(match, PRO_PADDING_LEFT, 0, N_LENGTH); | |
| 1513 | |
| 1514 style->color = color_from_property(match, PRO_COLOR, black); | |
| 1515 style->text_fill_color = color_from_properties(match, PRO_TEXT_FILL_COLOR, PRO_COLOR, black); | |
| 1516 style->text_stroke_color = color_from_property(match, PRO_TEXT_STROKE_COLOR, transparent); | |
| 1517 style->background_color = color_from_property(match, PRO_BACKGROUND_COLOR, transparent); | |
| 1518 | |
| 1519 style->border_spacing = number_from_property(match, PRO_BORDER_SPACING, 0, N_LENGTH); | |
| 1520 | |
| 1521 style->border_style_0 = border_style_from_property(match, PRO_BORDER_TOP_STYLE); | |
| 1522 style->border_style_1 = border_style_from_property(match, PRO_BORDER_RIGHT_STYLE); | |
| 1523 style->border_style_2 = border_style_from_property(match, PRO_BORDER_BOTTOM_STYLE); | |
| 1524 style->border_style_3 = border_style_from_property(match, PRO_BORDER_LEFT_STYLE); | |
| 1525 | |
| 1526 style->border_color[0] = color_from_property(match, PRO_BORDER_TOP_COLOR, style->color); | |
| 1527 style->border_color[1] = color_from_property(match, PRO_BORDER_RIGHT_COLOR, style->color); | |
| 1528 style->border_color[2] = color_from_property(match, PRO_BORDER_BOTTOM_COLOR, style->color); | |
| 1529 style->border_color[3] = color_from_property(match, PRO_BORDER_LEFT_COLOR, style->color); | |
| 1530 | |
| 1531 style->border_width[0] = border_width_from_property(match, PRO_BORDER_TOP_WIDTH); | |
| 1532 style->border_width[1] = border_width_from_property(match, PRO_BORDER_RIGHT_WIDTH); | |
| 1533 style->border_width[2] = border_width_from_property(match, PRO_BORDER_BOTTOM_WIDTH); | |
| 1534 style->border_width[3] = border_width_from_property(match, PRO_BORDER_LEFT_WIDTH); | |
| 1535 | |
| 1536 { | |
| 1537 const char *font_weight = string_from_property(match, PRO_FONT_WEIGHT, "normal"); | |
| 1538 const char *font_style = string_from_property(match, PRO_FONT_STYLE, "normal"); | |
| 1539 const char *font_variant = string_from_property(match, PRO_FONT_VARIANT, "normal"); | |
| 1540 int is_bold = is_bold_from_font_weight(font_weight); | |
| 1541 int is_italic = is_italic_from_font_style(font_style); | |
| 1542 style->small_caps = !strcmp(font_variant, "small-caps"); | |
| 1543 value = value_from_property(match, PRO_FONT_FAMILY); | |
| 1544 for (; value; value = value->next) | |
| 1545 { | |
| 1546 /* ignore numbers and keywords used in font short-hand syntax */ | |
| 1547 if (value->type == CSS_STRING || (value->type == CSS_KEYWORD && !keyword_in_list(value->data, font_family_ignore, nelem(font_family_ignore)))) | |
| 1548 { | |
| 1549 style->font = fz_load_html_font(ctx, set, value->data, is_bold, is_italic, style->small_caps); | |
| 1550 if (style->font) | |
| 1551 break; | |
| 1552 } | |
| 1553 } | |
| 1554 if (!style->font) | |
| 1555 style->font = fz_load_html_font(ctx, set, "serif", is_bold, is_italic, style->small_caps); | |
| 1556 } | |
| 1557 } | |
| 1558 | |
| 1559 #ifdef DEBUG_CSS_SPLAY | |
| 1560 static void | |
| 1561 do_verify_splay(const fz_css_style_splay *x) | |
| 1562 { | |
| 1563 printf("%x<", x); | |
| 1564 if (x->lt) | |
| 1565 { | |
| 1566 assert(memcmp(&x->lt->style, &x->style, sizeof(x->style)) < 0); | |
| 1567 assert(x->lt->up == x); | |
| 1568 do_verify_splay(x->lt); | |
| 1569 } | |
| 1570 printf(","); | |
| 1571 if (x->gt) | |
| 1572 { | |
| 1573 assert(memcmp(&x->gt->style, &x->style, sizeof(x->style)) > 0); | |
| 1574 assert(x->gt->up == x); | |
| 1575 do_verify_splay(x->gt); | |
| 1576 } | |
| 1577 printf(">\n"); | |
| 1578 } | |
| 1579 | |
| 1580 static void | |
| 1581 verify_splay(const fz_css_style_splay *x) | |
| 1582 { | |
| 1583 if (x == NULL) | |
| 1584 return; | |
| 1585 assert(x->up == NULL); | |
| 1586 do_verify_splay(x); | |
| 1587 printf("-----\n"); | |
| 1588 } | |
| 1589 #endif | |
| 1590 | |
| 1591 const fz_css_style * | |
| 1592 fz_css_enlist(fz_context *ctx, const fz_css_style *style, fz_css_style_splay **tree, fz_pool *pool) | |
| 1593 { | |
| 1594 fz_css_style_splay **current = tree; | |
| 1595 fz_css_style_splay *x; | |
| 1596 fz_css_style_splay *y = NULL; | |
| 1597 | |
| 1598 /* Search for a match in the tree, if there is one, or for | |
| 1599 * the insertion point, if there is not. */ | |
| 1600 while (*current != NULL) | |
| 1601 { | |
| 1602 int cmp = memcmp(style, &(*current)->style, sizeof(*style)); | |
| 1603 if (cmp == 0) | |
| 1604 { | |
| 1605 /* We have a match - break out and do move to root. */ | |
| 1606 break; | |
| 1607 } | |
| 1608 y = (*current); | |
| 1609 if (cmp < 0) | |
| 1610 current = &y->lt; | |
| 1611 else | |
| 1612 current = &y->gt; | |
| 1613 } | |
| 1614 /* Create one if needed */ | |
| 1615 if (*current == NULL) | |
| 1616 { | |
| 1617 x = *current = fz_pool_alloc(ctx, pool, sizeof(*y)); | |
| 1618 x->style = *style; | |
| 1619 x->up = y; | |
| 1620 x->lt = NULL; | |
| 1621 x->gt = NULL; | |
| 1622 } | |
| 1623 else | |
| 1624 x = *current; | |
| 1625 /* Now move to root */ | |
| 1626 /* | |
| 1627 The splaying steps used: | |
| 1628 | |
| 1629 Case 1: |a) z x b) z x | |
| 1630 | y D => A y A y y D | |
| 1631 | x C B z B x => z C | |
| 1632 | A B C D C D A B | |
| 1633 | |
| 1634 Case 2: |a) z x b) z x | |
| 1635 | y D => y z A y => z y | |
| 1636 | A x A B C D x D A B C D | |
| 1637 | B C B C | |
| 1638 | |
| 1639 Case 3: |a) y x b) y x | |
| 1640 | x C => A y A x => y C | |
| 1641 | A B B C B C A B | |
| 1642 */ | |
| 1643 #ifdef DEBUG_CSS_SPLAY | |
| 1644 printf("BEFORE\n"); | |
| 1645 verify_splay(*tree); | |
| 1646 #endif | |
| 1647 while ((y = x->up) != NULL ) /* While we're not at the root */ | |
| 1648 { | |
| 1649 fz_css_style_splay *z = y->up; | |
| 1650 y->up = x; | |
| 1651 if (z == NULL) | |
| 1652 { | |
| 1653 if (y->lt == x) /* Case 3a */ | |
| 1654 { | |
| 1655 y->lt = x->gt; | |
| 1656 if (y->lt) | |
| 1657 y->lt->up = y; | |
| 1658 x->gt = y; | |
| 1659 } | |
| 1660 else /* Case 3b */ | |
| 1661 { | |
| 1662 y->gt = x->lt; | |
| 1663 if (y->gt) | |
| 1664 y->gt->up = y; | |
| 1665 x->lt = y; | |
| 1666 } | |
| 1667 x->up = NULL; | |
| 1668 break; | |
| 1669 } | |
| 1670 x->up = z->up; | |
| 1671 if (z->up) | |
| 1672 { | |
| 1673 if (z->up->lt == z) | |
| 1674 z->up->lt = x; | |
| 1675 else | |
| 1676 z->up->gt = x; | |
| 1677 } | |
| 1678 if (z->lt == y) | |
| 1679 { | |
| 1680 if (y->lt == x) /* Case 1a */ | |
| 1681 { | |
| 1682 z->lt = y->gt; | |
| 1683 if (z->lt) | |
| 1684 z->lt->up = z; | |
| 1685 y->lt = x->gt; | |
| 1686 if (y->lt) | |
| 1687 y->lt->up = y; | |
| 1688 y->gt = z; | |
| 1689 z->up = y; | |
| 1690 x->gt = y; | |
| 1691 } | |
| 1692 else /* Case 2a */ | |
| 1693 { | |
| 1694 y->gt = x->lt; | |
| 1695 if (y->gt) | |
| 1696 y->gt->up = y; | |
| 1697 z->lt = x->gt; | |
| 1698 if (z->lt) | |
| 1699 z->lt->up = z; | |
| 1700 x->lt = y; | |
| 1701 x->gt = z; | |
| 1702 z->up = x; | |
| 1703 } | |
| 1704 } | |
| 1705 else | |
| 1706 { | |
| 1707 if (y->gt == x) /* Case 1b */ | |
| 1708 { | |
| 1709 z->gt = y->lt; | |
| 1710 if (z->gt) | |
| 1711 z->gt->up = z; | |
| 1712 y->gt = x->lt; | |
| 1713 if (y->gt) | |
| 1714 y->gt->up = y; | |
| 1715 y->lt = z; | |
| 1716 z->up = y; | |
| 1717 x->lt = y; | |
| 1718 } | |
| 1719 else /* Case 2b */ | |
| 1720 { | |
| 1721 z->gt = x->lt; | |
| 1722 if (z->gt) | |
| 1723 z->gt->up = z; | |
| 1724 y->lt = x->gt; | |
| 1725 if (y->lt) | |
| 1726 y->lt->up = y; | |
| 1727 x->gt = y; | |
| 1728 x->lt = z; | |
| 1729 z->up = x; | |
| 1730 } | |
| 1731 } | |
| 1732 } | |
| 1733 | |
| 1734 *tree = x; | |
| 1735 #ifdef DEBUG_CSS_SPLAY | |
| 1736 printf("AFTER\n"); | |
| 1737 verify_splay(x); | |
| 1738 #endif | |
| 1739 | |
| 1740 return &x->style; | |
| 1741 } | |
| 1742 | |
| 1743 /* | |
| 1744 * Pretty printing | |
| 1745 */ | |
| 1746 | |
| 1747 static void print_value(fz_css_value *val) | |
| 1748 { | |
| 1749 printf("%s", val->data); | |
| 1750 if (val->args) | |
| 1751 { | |
| 1752 printf("("); | |
| 1753 print_value(val->args); | |
| 1754 printf(")"); | |
| 1755 } | |
| 1756 if (val->next) | |
| 1757 { | |
| 1758 printf(" "); | |
| 1759 print_value(val->next); | |
| 1760 } | |
| 1761 } | |
| 1762 | |
| 1763 static void print_property(fz_css_property *prop) | |
| 1764 { | |
| 1765 printf("\t%s: ", fz_css_property_name(prop->name)); | |
| 1766 print_value(prop->value); | |
| 1767 if (prop->important) | |
| 1768 printf(" !important"); | |
| 1769 printf(";\n"); | |
| 1770 } | |
| 1771 | |
| 1772 static void print_condition(fz_css_condition *cond) | |
| 1773 { | |
| 1774 if (cond->type == '=') | |
| 1775 printf("[%s=%s]", cond->key, cond->val); | |
| 1776 else if (cond->type == '[') | |
| 1777 printf("[%s]", cond->key); | |
| 1778 else | |
| 1779 printf("%c%s", cond->type, cond->val); | |
| 1780 if (cond->next) | |
| 1781 print_condition(cond->next); | |
| 1782 } | |
| 1783 | |
| 1784 static void print_selector(fz_css_selector *sel) | |
| 1785 { | |
| 1786 if (sel->combine) | |
| 1787 { | |
| 1788 print_selector(sel->left); | |
| 1789 if (sel->combine == ' ') | |
| 1790 printf(" "); | |
| 1791 else | |
| 1792 printf(" %c ", sel->combine); | |
| 1793 print_selector(sel->right); | |
| 1794 } | |
| 1795 else if (sel->name) | |
| 1796 printf("%s", sel->name); | |
| 1797 else | |
| 1798 printf("*"); | |
| 1799 if (sel->cond) | |
| 1800 { | |
| 1801 print_condition(sel->cond); | |
| 1802 } | |
| 1803 } | |
| 1804 | |
| 1805 static void print_rule(fz_css_rule *rule) | |
| 1806 { | |
| 1807 fz_css_selector *sel; | |
| 1808 fz_css_property *prop; | |
| 1809 | |
| 1810 for (sel = rule->selector; sel; sel = sel->next) | |
| 1811 { | |
| 1812 print_selector(sel); | |
| 1813 printf(" /* %d */", selector_specificity(sel, 0)); | |
| 1814 if (sel->next) | |
| 1815 printf(", "); | |
| 1816 } | |
| 1817 | |
| 1818 printf("\n{\n"); | |
| 1819 for (prop = rule->declaration; prop; prop = prop->next) | |
| 1820 { | |
| 1821 print_property(prop); | |
| 1822 } | |
| 1823 printf("}\n"); | |
| 1824 } | |
| 1825 | |
| 1826 void | |
| 1827 fz_debug_css(fz_context *ctx, fz_css *css) | |
| 1828 { | |
| 1829 fz_css_rule *rule = css->rule; | |
| 1830 while (rule) | |
| 1831 { | |
| 1832 print_rule(rule); | |
| 1833 rule = rule->next; | |
| 1834 } | |
| 1835 } |
