| 1 5 6 package org.w3c.tidy; 7 8 33 34 69 70 public class Clean { 71 72 private int classNum = 1; 73 74 private TagTable tt; 75 76 public Clean(TagTable tt) 77 { 78 this.tt = tt; 79 } 80 81 private StyleProp insertProperty(StyleProp props, String name, 82 String value) 83 { 84 StyleProp first, prev, prop; 85 int cmp; 86 87 prev = null; 88 first = props; 89 90 while (props != null) 91 { 92 cmp = props.name.compareTo(name); 93 94 if (cmp == 0) 95 { 96 97 return first; 98 } 99 100 if (cmp > 0) { 102 103 104 prop = new StyleProp(name, value, props); 105 106 if (prev != null) 107 prev.next = prop; 108 else 109 first = prop; 110 111 return first; 112 } 113 114 prev = props; 115 props = props.next; 116 } 117 118 prop = new StyleProp(name, value); 119 120 if (prev != null) 121 prev.next = prop; 122 else 123 first = prop; 124 125 return first; 126 } 127 128 135 private StyleProp createProps(StyleProp prop, String style) 136 { 137 int name_end; 138 int value_end; 139 int value_start = 0; 140 int name_start = 0; 141 boolean more; 142 143 name_start = 0; 144 while (name_start < style.length()) 145 { 146 while (name_start < style.length() && 147 style.charAt(name_start) == ' ') 148 ++name_start; 149 150 name_end = name_start; 151 152 while (name_end < style.length()) 153 { 154 if (style.charAt(name_end) == ':') 155 { 156 value_start = name_end + 1; 157 break; 158 } 159 160 ++name_end; 161 } 162 163 if (name_end >= style.length() || style.charAt(name_end) != ':') 164 break; 165 166 while (value_start < style.length() && 167 style.charAt(value_start) == ' ') 168 ++value_start; 169 170 value_end = value_start; 171 more = false; 172 173 while (value_end < style.length()) 174 { 175 if (style.charAt(value_end) == ';') 176 { 177 more = true; 178 break; 179 } 180 181 ++value_end; 182 } 183 184 prop = insertProperty(prop, 185 style.substring(name_start, name_end), 186 style.substring(value_start, value_end)); 187 188 if (more) 189 { 190 name_start = value_end + 1; 191 continue; 192 } 193 194 break; 195 } 196 197 return prop; 198 } 199 200 private String createPropString(StyleProp props) 201 { 202 String style = ""; 203 int len; 204 StyleProp prop; 205 206 207 208 for (len = 0, prop = props; prop != null; prop = prop.next) 209 { 210 len += prop.name.length() + 2; 211 len += prop.value.length() + 2; 212 } 213 214 for (prop = props; prop != null; prop = prop.next) 215 { 216 style = style.concat(prop.name); 217 style = style.concat(": "); 218 219 style = style.concat(prop.value); 220 221 if (prop.next == null) 222 break; 223 224 style = style.concat("; "); 225 } 226 227 return style; 228 } 229 230 233 private String addProperty(String style, String property) 234 { 235 StyleProp prop; 236 237 prop = createProps(null, style); 238 prop = createProps(prop, property); 239 style = createPropString(prop); 240 return style; 241 } 242 243 private String gensymClass(String tag) 244 { 245 String str; 246 247 str = "c" + classNum; 248 classNum++; 249 return str; 250 } 251 252 private String findStyle(Lexer lexer, String tag, String properties) 253 { 254 Style style; 255 256 for (style = lexer.styles; style != null; style=style.next) 257 { 258 if (style.tag.equals(tag) && 259 style.properties.equals(properties)) 260 return style.tagClass; 261 } 262 263 style = new Style(tag, gensymClass(tag), properties, lexer.styles); 264 lexer.styles = style; 265 return style.tagClass; 266 } 267 268 276 private void style2Rule(Lexer lexer, Node node) 277 { 278 AttVal styleattr, classattr; 279 String classname; 280 281 styleattr = node.getAttrByName("style"); 282 283 if (styleattr != null) 284 { 285 classname = findStyle(lexer, node.element, styleattr.value); 286 classattr = node.getAttrByName("class"); 287 288 292 if (classattr != null) 293 { 294 classattr.value = classattr.value + " " + classname; 295 node.removeAttribute(styleattr); 296 } 297 else 298 { 299 styleattr.attribute = "class"; 300 styleattr.value = classname; 301 } 302 } 303 } 304 305 private void addColorRule(Lexer lexer, String selector, String color) 306 { 307 if (color != null) 308 { 309 lexer.addStringLiteral(selector); 310 lexer.addStringLiteral(" { color: "); 311 lexer.addStringLiteral(color); 312 lexer.addStringLiteral(" }\n"); 313 } 314 } 315 316 326 private void cleanBodyAttrs(Lexer lexer, Node body) 327 { 328 AttVal attr; 329 String bgurl = null; 330 String bgcolor = null; 331 String color = null; 332 333 attr = body.getAttrByName("background"); 334 335 if (attr != null) 336 { 337 bgurl = attr.value; 338 attr.value = null; 339 body.removeAttribute(attr); 340 } 341 342 attr = body.getAttrByName("bgcolor"); 343 344 if (attr != null) 345 { 346 bgcolor = attr.value; 347 attr.value = null; 348 body.removeAttribute(attr); 349 } 350 351 attr = body.getAttrByName("text"); 352 353 if (attr != null) 354 { 355 color = attr.value; 356 attr.value = null; 357 body.removeAttribute(attr); 358 } 359 360 if (bgurl != null || bgcolor != null || color != null) 361 { 362 lexer.addStringLiteral(" body {\n"); 363 364 if (bgurl != null) 365 { 366 lexer.addStringLiteral(" background-image: url("); 367 lexer.addStringLiteral(bgurl); 368 lexer.addStringLiteral(");\n"); 369 } 370 371 if (bgcolor != null) 372 { 373 lexer.addStringLiteral(" background-color: "); 374 lexer.addStringLiteral(bgcolor); 375 lexer.addStringLiteral(";\n"); 376 } 377 378 if (color != null) 379 { 380 lexer.addStringLiteral(" color: "); 381 lexer.addStringLiteral(color); 382 lexer.addStringLiteral(";\n"); 383 } 384 385 lexer.addStringLiteral(" }\n"); 386 } 387 388 attr = body.getAttrByName("link"); 389 390 if (attr != null) 391 { 392 addColorRule(lexer, " :link", attr.value); 393 body.removeAttribute(attr); 394 } 395 396 attr = body.getAttrByName("vlink"); 397 398 if (attr != null) 399 { 400 addColorRule(lexer, " :visited", attr.value); 401 body.removeAttribute(attr); 402 } 403 404 attr = body.getAttrByName("alink"); 405 406 if (attr != null) 407 { 408 addColorRule(lexer, " :active", attr.value); 409 body.removeAttribute(attr); 410 } 411 } 412 413 private boolean niceBody(Lexer lexer, Node doc) 414 { 415 Node body = doc.findBody(lexer.configuration.tt); 416 417 if (body != null) 418 { 419 if ( 420 body.getAttrByName("background") != null || 421 body.getAttrByName("bgcolor") != null || 422 body.getAttrByName("text") != null || 423 body.getAttrByName("link") != null || 424 body.getAttrByName("vlink") != null || 425 body.getAttrByName("alink") != null 426 ) 427 { 428 lexer.badLayout |= Report.USING_BODY; 429 return false; 430 } 431 } 432 433 return true; 434 } 435 436 437 private void createStyleElement(Lexer lexer, Node doc) 438 { 439 Node node, head, body; 440 Style style; 441 AttVal av; 442 443 if (lexer.styles == null && niceBody(lexer, doc)) 444 return; 445 446 node = lexer.newNode(Node.StartTag, null, 0, 0, "style"); 447 node.implicit = true; 448 449 450 av = new AttVal(null, null, '"', "type", "text/css"); 451 av.dict = AttributeTable.getDefaultAttributeTable().findAttribute(av); 452 node.attributes = av; 453 454 body = doc.findBody(lexer.configuration.tt); 455 456 lexer.txtstart = lexer.lexsize; 457 458 if (body != null) 459 cleanBodyAttrs(lexer, body); 460 461 for (style = lexer.styles; style != null; style = style.next) 462 { 463 lexer.addCharToLexer(' '); 464 lexer.addStringLiteral(style.tag); 465 lexer.addCharToLexer('.'); 466 lexer.addStringLiteral(style.tagClass); 467 lexer.addCharToLexer(' '); 468 lexer.addCharToLexer('{'); 469 lexer.addStringLiteral(style.properties); 470 lexer.addCharToLexer('}'); 471 lexer.addCharToLexer('\n'); 472 } 473 474 lexer.txtend = lexer.lexsize; 475 476 Node.insertNodeAtEnd(node, 477 lexer.newNode(Node.TextNode, 478 lexer.lexbuf, 479 lexer.txtstart, 480 lexer.txtend)); 481 482 488 489 head = doc.findHEAD(lexer.configuration.tt); 490 491 if (head != null) 492 Node.insertNodeAtEnd(head, node); 493 } 494 495 496 private void fixNodeLinks(Node node) 497 { 498 Node child; 499 500 if (node.prev != null) 501 node.prev.next = node; 502 else 503 node.parent.content = node; 504 505 if (node.next != null) 506 node.next.prev = node; 507 else 508 node.parent.last = node; 509 510 for (child = node.content; child != null; child = child.next) 511 child.parent = node; 512 } 513 514 518 private void stripOnlyChild(Node node) 519 { 520 Node child; 521 522 child = node.content; 523 node.content = child.content; 524 node.last = child.last; 525 child.content = null; 526 527 for (child = node.content; child != null; child = child.next) 528 child.parent = node; 529 } 530 531 532 private void discardContainer(Node element, MutableObject pnode) 533 { 534 Node node; 535 Node parent = element.parent; 536 537 if (element.content != null) 538 { 539 element.last.next = element.next; 540 541 if (element.next != null) 542 { 543 element.next.prev = element.last; 544 element.last.next = element.next; 545 } 546 else 547 parent.last = element.last; 548 549 if (element.prev != null) 550 { 551 element.content.prev = element.prev; 552 element.prev.next = element.content; 553 } 554 else 555 parent.content = element.content; 556 557 for (node = element.content; node != null; node = node.next) 558 node.parent = parent; 559 560 pnode.setObject(element.content); 561 } 562 else 563 { 564 if (element.next != null) 565 element.next.prev = element.prev; 566 else 567 parent.last = element.prev; 568 569 if (element.prev != null) 570 element.prev.next = element.next; 571 else 572 parent.content = element.next; 573 574 pnode.setObject(element.next); 575 } 576 577 element.next = null; 578 element.content = null; 579 } 580 581 585 private void addStyleProperty(Node node, String property) 586 { 587 AttVal av; 588 589 for (av = node.attributes; av != null; av = av.next) 590 { 591 if (av.attribute.equals("style")) 592 break; 593 } 594 595 596 597 if (av != null) 598 { 599 String s; 600 601 s = addProperty(av.value, property); 602 av.value = s; 603 } 604 else 605 { 606 av = new AttVal(node.attributes, null, '"', "style", property); 607 av.dict = AttributeTable.getDefaultAttributeTable().findAttribute(av); 608 node.attributes = av; 609 } 610 } 611 612 621 private String mergeProperties(String s1, String s2) 622 { 623 String s; 624 StyleProp prop; 625 626 prop = createProps(null, s1); 627 prop = createProps(prop, s2); 628 s = createPropString(prop); 629 return s; 630 } 631 632 private void mergeStyles(Node node, Node child) 633 { 634 AttVal av; 635 String s1, s2, style; 636 637 for (s2 = null, av = child.attributes; av != null; av = av.next) 638 { 639 if (av.attribute.equals("style")) 640 { 641 s2 = av.value; 642 break; 643 } 644 } 645 646 for (s1 = null, av = node.attributes; av != null; av = av.next) 647 { 648 if (av.attribute.equals("style")) 649 { 650 s1 = av.value; 651 break; 652 } 653 } 654 655 if (s1 != null) 656 { 657 if (s2 != null) 658 { 659 style = mergeProperties(s1, s2); 660 av.value = style; 661 } 662 } 663 else if (s2 != null) 664 { 665 av = new AttVal(node.attributes, null, '"', "style", s2); 666 av.dict = AttributeTable.getDefaultAttributeTable().findAttribute(av); 667 node.attributes = av; 668 } 669 } 670 671 private String fontSize2Name(String size) 672 { 673 685 686 String [] sizes = 687 { 688 "60%", 689 "70%", 690 "80%", 691 null, 692 "120%", 693 "150%", 694 "200%" 695 }; 696 String buf; 697 698 if (size.length() > 0 && 699 '0' <= size.charAt(0) && size.charAt(0) <= '6') 700 { 701 int n = size.charAt(0) - '0'; 702 return sizes[n]; 703 } 704 705 if (size.length() > 0 && size.charAt(0) == '-') 706 { 707 if (size.length() > 1 && 708 '0' <= size.charAt(1) && size.charAt(1) <= '6') 709 { 710 int n = size.charAt(1) - '0'; 711 double x; 712 713 for (x = 1.0; n > 0; --n) 714 x *= 0.8; 715 716 x *= 100.0; 717 buf = "" + (int)x + "%"; 718 719 return buf; 720 } 721 722 return "smaller"; 723 } 724 725 if (size.length() > 1 && 726 '0' <= size.charAt(1) && size.charAt(1) <= '6') 727 { 728 int n = size.charAt(1) - '0'; 729 double x; 730 731 for (x = 1.0; n > 0; --n) 732 x *= 1.2; 733 734 x *= 100.0; 735 buf = "" + (int)x + "%"; 736 737 return buf; 738 } 739 740 return "larger"; 741 } 742 743 private void addFontFace(Node node, String face) 744 { 745 addStyleProperty(node, "font-family: " + face); 746 } 747 748 private void addFontSize(Node node, String size) 749 { 750 String value; 751 752 if (size.equals("6") && node.tag == tt.tagP) 753 { 754 node.element = "h1"; 755 tt.findTag(node); 756 return; 757 } 758 759 if (size.equals("5") && node.tag == tt.tagP) 760 { 761 node.element = "h2"; 762 tt.findTag(node); 763 return; 764 } 765 766 if (size.equals("4") && node.tag == tt.tagP) 767 { 768 node.element = "h3"; 769 tt.findTag(node); 770 return; 771 } 772 773 value = fontSize2Name(size); 774 775 if (value != null) 776 { 777 addStyleProperty(node, "font-size: " + value); 778 } 779 } 780 781 private void addFontColor(Node node, String color) 782 { 783 addStyleProperty(node, "color: " + color); 784 } 785 786 private void addAlign(Node node, String align) 787 { 788 789 addStyleProperty(node, "text-align: " + align.toLowerCase()); 790 } 791 792 796 private void addFontStyles(Node node, AttVal av) 797 { 798 while (av != null) 799 { 800 if (av.attribute.equals("face")) 801 addFontFace(node, av.value); 802 else if (av.attribute.equals("size")) 803 addFontSize(node, av.value); 804 else if (av.attribute.equals("color")) 805 addFontColor(node, av.value); 806 807 av = av.next; 808 } 809 } 810 811 815 private void textAlign(Lexer lexer, Node node) 816 { 817 AttVal av, prev; 818 819 prev = null; 820 821 for (av = node.attributes; av != null; av = av.next) 822 { 823 if (av.attribute.equals("align")) 824 { 825 if (prev != null) 826 prev.next = av.next; 827 else 828 node.attributes = av.next; 829 830 if (av.value != null) 831 { 832 addAlign(node, av.value); 833 } 834 835 break; 836 } 837 838 prev = av; 839 } 840 } 841 842 846 847 851 852 private boolean dir2Div(Lexer lexer, Node node, MutableObject pnode) 853 { 854 Node child; 855 856 if (node.tag == tt.tagDir || 857 node.tag == tt.tagUl || 858 node.tag == tt.tagOl) 859 { 860 child = node.content; 861 862 if (child == null) 863 return false; 864 865 866 867 if (child.next != null) 868 return false; 869 870 if (child.tag != tt.tagLi) 871 return false; 872 873 if (!child.implicit) 874 return false; 875 876 877 878 node.tag = tt.tagDiv; 879 node.element = "div"; 880 addStyleProperty(node, "margin-left: 2em"); 881 stripOnlyChild(node); 882 return true; 883 884 891 892 893 899 900 901 909 911 912 } 916 917 return false; 918 } 919 920 924 925 private boolean center2Div(Lexer lexer, Node node, MutableObject pnode) 926 { 927 if (node.tag == tt.tagCenter) 928 { 929 if (lexer.configuration.DropFontTags) 930 { 931 if (node.content != null) 932 { 933 Node last = node.last; 934 Node parent = node.parent; 935 936 discardContainer(node, pnode); 937 938 node = lexer.inferredTag("br"); 939 940 if (last.next != null) 941 last.next.prev = node; 942 943 node.next = last.next; 944 last.next = node; 945 node.prev = last; 946 947 if (parent.last == last) 948 parent.last = node; 949 950 node.parent = parent; 951 } 952 else 953 |