-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathusage.html
1373 lines (1281 loc) · 83.1 KB
/
usage.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="copyright" content="Carlos Amengual">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="usage-e.css" rel="stylesheet" type="text/css" />
<title>CSS4J Quick Start User Guide</title>
<script src="js/usage-d.js" charset="utf-8"></script>
</head>
<body>
<div class="layout">
<div id="hdr01"></div>
<a id="mylinkhome" href="/"><span>CSS4J</span></a>
</div>
<div class="container">
<div class="menu">
<ul class="menulist">
<li><a id="mnuindice" href="/"><span>Home</span></a></li>
<li><div id="mnuusage-sel"><span>Usage</span></div></li>
<li class="menulvl2"><a id="mnuembedsvg" href="embed-svg.html"><span>Embed SVG</span></a></li>
<li class="menulvl2"><a id="mnuresolver" href="resolver.html"><span>Resolver</span></a></li>
<li><a id="mnuapi2" href="api/latest/"><span>Latest API</span></a></li>
<li><a id="mnufaq" href="faq.html"><span>FAQ</span></a></li>
<li><a id="mnubenchmarks" href="benchmarks.html"><span>Benchmarks</span></a></li>
<li><a id="mnugithub" href="https://github.com/css4j"><span>Github</span></a></li>
</ul>
</div>
<div class="beforemain"></div>
<div class="main">
<div id="presentacion_top" class="textheader"><span>Usage</span></div>
<div class="cos">
<h1>CSS4J User Guide</h1>
<p>This project implements, in the Java™ language, APIs that are partly based on W3C's <a
href="https://www.w3.org/TR/cssom-1/" target="_blank">CSS Object Model</a> and WHATWG/W3C's <a
href="https://dom.spec.whatwg.org/" target="_blank">DOM</a> APIs. (Initially, the project
implemented the <a href="https://www.w3.org/TR/2000/REC-DOM-Level-2-Style-20001113/"
target="_blank">DOM Level 2 Style API</a>.)</p>
<p>It targets several different use cases, with functionalities ranging from style sheet error
detection to style computation. A specialized module also adds CSS support to the <a
href="https://dom4j.github.io/" target="_blank">DOM4J</a> package.</p>
<p>Java 8 and later versions are supported (the library is fully modular).</p>
<div class="tema" id="overview">
<h2>Overview</h2>
<p>This library provides a number of classes to deal with different CSS Object Model abstractions. For example:</p>
<ul>
<li><a class="codeitem" href="api/latest/io.sf.carte.css4j/io/sf/carte/doc/style/css/om/AbstractCSSStyleSheet.html">AbstractCSSStyleSheet</a>
for style sheets.</li>
<li><a class="codeitem" href="api/latest/io.sf.carte.css4j/io/sf/carte/doc/style/css/om/StyleRule.html">StyleRule</a>
for style rules.</li>
<li><a class="codeitem" href="api/latest/io.sf.carte.css4j/io/sf/carte/doc/style/css/property/StyleValue.html">StyleValue</a>
for values like <code>rgb(12% 0% 67%)</code> or <code>var(--customProperty)</code>.</li>
<li><a class="codeitem" href="api/latest/io.sf.carte.css4j/io/sf/carte/doc/style/css/nsac/LexicalUnit.html">LexicalUnit</a>
for low-level CSS syntax.</li>
<li><a class="codeitem" href="api/latest/io.sf.carte.css4j/io/sf/carte/doc/style/css/nsac/Selector.html">Selector</a>
and <a class="codeitem" href="api/latest/io.sf.carte.css4j/io/sf/carte/doc/style/css/nsac/SelectorList.html">SelectorList</a>
for <a href="https://www.w3.org/TR/selectors-4/" target="_blank">CSS Selectors</a>.</li>
</ul>
<p>You can use those classes as stand-alone objects or have them all together in a document, one of the implementations
of the <a href="https://dom.spec.whatwg.org/" target="_blank">DOM</a>-based <a class="codeitem"
href="api/latest/io.sf.carte.css4j/io/sf/carte/doc/style/css/CSSDocument.html">CSSDocument</a> interface:</p>
<ul>
<li><a class="codeitem" href="api/latest/io.sf.carte.css4j/io/sf/carte/doc/dom/HTMLDocument.html">HTMLDocument</a> and
<a class="codeitem" href="api/latest/io.sf.carte.css4j/io/sf/carte/doc/dom/DOMDocument.html">DOMDocument</a>
of css4j's native DOM, available from the <a href="api/latest/io.sf.carte.css4j/module-summary.html">core css4j module</a>.
Native DOM is recommended for new projects.</li>
<li>The <a class="codeitem" href="api/latest/io.sf.carte.css4j/io/sf/carte/doc/style/css/om/StylableDocumentWrapper.html">StylableDocumentWrapper</a>
can wrap any valid DOM document, thus adding (read-only) style capabilities to a pre-existing document in your favourite
DOM implementation. The documentation often refers to this approach as the "DOM Wrapper", and is also available from the
<a href="api/latest/io.sf.carte.css4j/module-summary.html">core css4j module</a>.</li>
<li><a class="codeitem" href="api/latest/io.sf.carte.css4j.dom4j/io/sf/carte/doc/dom4j/XHTMLDocument.html">XHTMLDocument</a>
is a DOM4J subclass which comes with the <a href="api/latest/io.sf.carte.css4j.dom4j/module-summary.html">css4j-dom4j
module</a>. Recommended only for dealing with legacy DOM4J code.</li>
</ul>
<p>An important advantage of using <a href="http://www.w3.org/DOM/" target="_blank">DOM</a>-based interfaces is that you
can use the same or very similar methods in your web browser's Javascript (with the exception of the <a class="codeitem"
href="api/latest/io.sf.carte.css4j/io/sf/carte/doc/style/css/CSSValue.html">CSSValue</a> API which
is based on an old W3C standard instead of the more recent Houdini's Typed OM).</p>
<h3 class="subtema" id="choosing-dom">Choosing a DOM implementation</h3>
<p>Once you have chosen which DOM you prefer to use, the first step is to obtain an instance of a compatible
<a class="codeitem" href="api/latest/io.sf.carte.css4j/io/sf/carte/doc/style/css/CSSStyleSheetFactory.html">CSSStyleSheetFactory</a>.
<p>There are three implementations of that interface:</p>
<ul>
<li><a class="codeitem" href="api/latest/io.sf.carte.css4j/io/sf/carte/doc/dom/CSSDOMImplementation.html">CSSDOMImplementation</a>, the native DOM implementation.</li>
<li><a class="codeitem" href="api/latest/io.sf.carte.css4j/io/sf/carte/doc/style/css/om/DOMCSSStyleSheetFactory.html">DOMCSSStyleSheetFactory</a> for the DOM Wrapper.</li>
<li>If you use DOM4J, get it through <a class="codeitem"
href="api/latest/io.sf.carte.css4j.dom4j/io/sf/carte/doc/dom4j/XHTMLDocumentFactory.html#getStyleSheetFactory()">XHTMLDocumentFactory.getInstance().getStyleSheetFactory()</a>.</li>
</ul>
<p>Even if you want to use only stand-alone style sheets, you should use one of the above factories to create them with the
<a class="codeitem" href="api/latest/io.sf.carte.css4j/io/sf/carte/doc/style/css/om/AbstractCSSStyleSheetFactory.html#createStyleSheet(java.lang.String,io.sf.carte.doc.style.css.MediaQueryList)">createStyleSheet("<i>title</i>",
"<i>media</i>")</a> method.</p>
<p>The style sheet objects created by that method are empty, but you can load the style sheet rules with the
<a class="codeitem" href="api/latest/io.sf.carte.css4j/io/sf/carte/doc/style/css/om/AbstractCSSStyleSheet.html#parseStyleSheet(java.io.Reader,short)">AbstractCSSStyleSheet.parseStyleSheet(<i>reader</i>,
<i>short</i>)</a> method (see <a href="#sheetparsing">Parsing a Style Sheet</a>).</p>
</div>
<div class="tema" id="modules">
<h2>Modules</h2>
<p>The project is organized in several modules:</p>
<ul>
<li><a href="https://github.com/css4j/css4j"><code>css4j</code></a> core module (providing the <a href="api/latest/io.sf.carte.css4j/module-summary.html">io.sf.carte.css4j</a>
JPMS module), which depends on the next two (they are included in the Zip distribution):</li>
<li><a href="api/carte-util/3/"><code>carte-util</code></a>, a very small collection of interfaces and utility classes.</li>
<li><a href="https://github.com/css4j/tokenproducer"><code>tokenproducer</code></a>, the low-level parser at the core of css4j.</li>
<li><a href="https://github.com/css4j/css4j-agent"><code>css4j-agent</code></a>, an optional collection of agent-related
classes that makes the processing of remote documents a bit easier. JPMS module: <a
href="api/latest/io.sf.carte.css4j.agent.net/module-summary.html">io.sf.carte.css4j.agent.net</a>.</li>
<li><a href="https://github.com/css4j/css4j-awt"><code>css4j-awt</code></a>, a small set of <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/java/awt/package-summary.html">AWT</a>-related
utility classes (also optional). Module: <a href="api/latest/io.sf.carte.css4j.awt/module-summary.html">io.sf.carte.css4j.awt</a>.</li>
<li><a href="https://github.com/css4j/css4j-dom4j"><code>css4j-dom4j</code></a>: to use css4j together with dom4j, if you prefer that API.
The JPMS module name is <a href="api/latest/io.sf.carte.css4j.dom4j/module-summary.html">io.sf.carte.css4j.dom4j</a>.</li>
<li><a href="https://github.com/css4j/xml-dtd"><code>xml-dtd</code></a> is a small set of classes to aid in the processing of XML <a href="https://en.wikipedia.org/wiki/Document_type_definition"
target="_blank">DTD</a>s, including the useful <a class="codeitem" href="api/latest/io.sf.carte.xml.dtd/io/sf/carte/doc/xml/dtd/DefaultEntityResolver.html">DefaultEntityResolver</a>.
It is not needed if you do not process XML. The JPMS module is <a
href="api/latest/io.sf.carte.xml.dtd/module-summary.html">io.sf.carte.xml.dtd</a>.</li>
</ul>
<p>In general, you'll need to have at least <code>css4j</code>, <code>carte-util</code> and <code>tokenproducer</code> in your classpath/modulepath.
If you use <a href="https://gradle.org/" target="_blank">Gradle</a> or <a href="https://maven.apache.org/" target="_blank">Apache Maven</a>, you may want to look at each module's
<code>build.gradle</code> files (or even better, the <a href="https://css4j.github.io/maven/io/sf/carte/css4j-dom4j/4.2/css4j-dom4j-4.2.pom"><code>.pom</code></a>
or the <a href="https://css4j.github.io/maven/io/sf/carte/css4j-dom4j/4.2/css4j-dom4j-4.2.module"><code>.module</code> files at the repository</a>) to check for the specific module dependencies.</p>
<p>The latest <code>css4j-*</code> artifacts are generally compatible with <code>css4j</code> releases
that have the same major version number and the same or larger minor version. For example
<code>css4j-awt</code> 4.0 is compatible with <code>css4j</code> 4.0.1 and also with 4.2.2.</p>
<h3 class="subtema" id="gradle">Usage from a Gradle project</h3>
<p>
If your Gradle project depends on css4j, you can use this project's own Maven repository in a <code>repositories</code> section of
your build file:
</p>
<pre class="code"><code class="language-groovy">repositories {
maven {
url "https://css4j.github.io/maven/"
mavenContent {
releasesOnly()
}
content {
includeGroup 'io.sf.carte'
includeGroup 'io.sf.jclf'
includeGroup 'xmlpull'
includeGroup 'xpp3'
}
}
}
</code></pre>
<p>
please use this repository <b>only</b> for the artifact groups listed in the <code>includeGroup</code> statements.
</p>
<p>
Then, in your <code>build.gradle</code> file:
</p>
<pre class="code"><code class="language-groovy">dependencies {
implementation "io.sf.carte:css4j:${css4jVersion}"
}
</code></pre>
<p>
where <code>css4jVersion</code> could be defined in a <code>gradle.properties</code> file.
</p>
<h3 class="subtema" id="maven">Usage from a Maven build</h3>
<p>
If you build your project (that depends on css4j) with Apache Maven, please note that neither css4j nor some of its
dependencies are in Maven Central:
</p>
<ul>
<li><a href="https://sourceforge.net/projects/jclf/">JCLF</a>.</li>
<li><a href="https://github.com/xmlpull-xpp3/xmlpull-xpp3">XMLPull-XPP3</a> (v1.2, dependency of the DOM4J module only).</li>
</ul>
<p>
So you may want to add css4j's Maven repository to your POM file:
</p>
<pre class="code"><code class="language-xml"><repositories>
<repository>
<id>css4j</id>
<name>CSS4J repository</name>
<url>https://css4j.github.io/maven/</url>
</repository>
</repositories>
</code></pre>
<p>
Alternatively, you can also install the artifacts manually into your local Maven repository,
which can be done easily with the:
</p>
<ul>
<li><a href="https://raw.githubusercontent.com/css4j/css4j-dist/master/maven/install-css4j.sh"><code>install-css4j.sh</code></a></li>
<li><a href="https://raw.githubusercontent.com/css4j/css4j-dist/master/maven/install-jclf.sh"><code>install-jclf.sh</code></a></li>
<li><a href="https://raw.githubusercontent.com/css4j/css4j-dist/master/maven/install-xpp3.sh"><code>install-xpp3.sh</code></a></li>
</ul>
<p>
scripts. And then, add the following to the <code><dependencies></code> section of your <code>pom.xml</code>:
</p>
<pre class="code"><code class="language-xml"><!-- This artifact is not in Maven Central -->
<dependency>
<groupId>io.sf.carte</groupId>
<artifactId>css4j</artifactId>
<version>${css4j.version}</version>
</dependency>
</code></pre>
</div>
<div class="tema" id="nativedom">
<h2>Using css4j's native DOM implementation</h2>
<p>You can create a DOM document from scratch and use the related DOM methods to programmatically build a document: just use the provided DOM implementation
(<code>io.sf.carte.doc.dom.CSSDOMImplementation</code>). But if you want to parse an existing document, the procedure depends on the type of document: to parse
an XML document (including XHTML documents), you can use this library's <code>XMLDocumentBuilder</code> with the SAX parser provided by the JDK, while to parse
an HTML one (or an XHTML document that does not use namespace prefixes) you can use the <a href="https://about.validator.nu/htmlparser/" target="_blank">validator.nu
HTML5 parser</a> (be sure to use the latest version from the <a href="https://mvnrepository.com/artifact/nu.validator/htmlparser"><code>nu.validator</code>
Maven group id</a>).</p>
<p>An example with that parser follows:</p>
<pre class="code"><code class="language-java">import java.io.Reader;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import io.sf.carte.doc.dom.CSSDOMImplementation;
import io.sf.carte.doc.dom.DOMElement;
import io.sf.carte.doc.dom.HTMLDocument;
import io.sf.carte.doc.dom.XMLDocumentBuilder;
import io.sf.carte.doc.style.css.CSSComputedProperties;
import io.sf.carte.doc.style.css.CSSTypedValue;
import io.sf.carte.doc.style.css.RGBAColor;
import io.sf.carte.doc.xml.dtd.DefaultEntityResolver;
import nu.validator.htmlparser.common.XmlViolationPolicy;
import nu.validator.htmlparser.sax.HtmlParser;
[...]
// Instantiate DOM implementation (with default settings: no IE hacks accepted)
// and configure it
CSSDOMImplementation impl = new CSSDOMImplementation();
// Alternatively, impl = new CSSDOMImplementation(flags);
// Now load default HTML user agent sheets
impl.setDefaultHTMLUserAgentSheet();
// Prepare parser
HtmlParser parser = new HtmlParser(XmlViolationPolicy.ALTER_INFOSET);
parser.setCommentPolicy(XmlViolationPolicy.ALLOW);
parser.setXmlnsPolicy(XmlViolationPolicy.ALLOW);
// Prepare builder
XMLDocumentBuilder builder = new XMLDocumentBuilder(impl);
builder.setHTMLProcessing(true);
builder.setXMLReader(parser);
// Read the document to parse, and prepare source object
Reader re = ... [reader for HTML document]
InputSource source = new InputSource(re);
// Parse. If the document is not HTML, you want to use DOMDocument instead
HTMLDocument document = (HTMLDocument) builder.parse(source);
re.close();
// Set document URI
document.setDocumentURI("http://www.example.com/mydocument.html");
</code></pre>
<p>Now, you have a CSS-enabled document.</p>
<p>You can see the above code example (and others in this guide) in the <a href="https://github.com/css4j/css4j.github.io/blob/master/src/examples/java/io/sf/carte/example/Css4jTest.java">Css4jTest.java</a>
example file.</p>
<h3 class="subtema" id="computing-styles">Computing styles</h3>
<p>To compute styles, use <a class="codeitem" href="api/latest/io.sf.carte.css4j/io/sf/carte/doc/style/css/CSSElement.html#getComputedStyle(java.lang.String)">getComputedStyle</a>:</p>
<pre class="code"><code class="language-java">DOMElement element = document.getElementById("someId");
CSSComputedProperties style = element.getComputedStyle(null);
// Next line could be 'String display = style.getDisplay();'
String display = style.getPropertyValue("display");
// If you use a factory that has been set to setLenientSystemValues(false), next
// line may throw an exception if the 'color' property was not specified.
// The default value for lenientSystemValues is TRUE.
RGBAColor color = ((CSSTypedValue) style.getPropertyCSSValue("color")).toRGBColor();
// Suppose that the linked style sheet located at 'css/sheet.css' declares:
// background-image: url('foo.png');
String image_css = style.getPropertyValue("background-image");
String image_uri = ((CSSTypedValue) style.getPropertyCSSValue("background-image")).getStringValue();
// Then, because we already set the document URI to "http://www.example.com/mydocument.html",
// image_css will be set to "url('http://www.example.com/css/foo.png')",
// and image_uri to "http://www.example.com/css/foo.png"
</code></pre>
<p>The <a class="codeitem" href="api/latest/io.sf.carte.css4j/io/sf/carte/doc/style/css/CSSComputedProperties.html">CSSComputedProperties</a>
interface extends W3C's <a href="https://www.w3.org/TR/cssom-1/#the-cssstyledeclaration-interface" target="_blank"><code>CSSStyleDeclaration</code></a>
for computed styles, adding methods like <code>getComputedFontSize()</code> or <code>getComputedLineHeight()</code>.</p>
<p>If you are computing styles for a specific medium, tell the document about it (see "<a href="#mediahandling">Media Handling</a>"):</p>
<pre class="code"><code class="language-java">document.setTargetMedium("print");
</code></pre>
<h3 class="subtema" id="conformance">Conformance with the DOM specification</h3>
<p>This library's native DOM implementation has some minor behavior differences with what is written in the <a href="https://www.w3.org/TR/DOM-Level-3-Core/" target="_blank">DOM
Level 3 Core Specification</a>. For example, on elements and attributes the <code>Node.getLocalName()</code> method returns the tag name instead of
<code>null</code> when the node was created with a DOM Level 1 method such as <code>Document.createElement()</code>. Read the
<a class="codeitem" href="api/latest/io.sf.carte.css4j/io/sf/carte/doc/dom/package-summary.html">io.sf.carte.doc.dom</a> package
description for additional information.</p>
</div>
<div class="tema" id="dom4j">
<h2>Examples with DOM4J</h2>
<p>To use the library with dom4j (using dom4j-like documents, elements and factory) you need the
<a href="https://github.com/css4j/css4j-dom4j">css4j-dom4j module</a> in addition to the core
module. The -dom4j module optionally depends on the <a href="https://github.com/css4j/css4j-agent">-agent module</a>,
but you do not need the latter unless you plan to use the DOM4J agent.</p>
<h3 class="subtema" id="dom4j-gradle">Gradle setup</h3>
<p>For example, to set up css4j-dom4j (and the generally useful xml-dtd) with Gradle, put the following in your
build file:</p>
<pre class="code"><code class="language-groovy">dependencies {
implementation "io.sf.carte:css4j-dom4j:${css4jDom4jVersion}"
implementation "io.sf.carte:xml-dtd:4.3"
}
</code></pre>
<p>But please remember that you have to set up css4j's repository, if you haven't already:</p>
<pre class="code"><code class="language-groovy">repositories {
maven {
url "https://css4j.github.io/maven/"
mavenContent {
releasesOnly()
}
content {
includeGroup 'io.sf.carte'
includeGroup 'io.sf.jclf'
includeGroup 'xmlpull'
includeGroup 'xpp3'
}
}
}
</code></pre>
<h3 class="subtema" id="dom4j-maven">Maven setup</h3>
<p>Same with Apache Maven:</p>
<pre class="code"><code class="language-xml"><!-- This artifact requires the css4j Maven repository -->
<dependency>
<groupId>io.sf.carte</groupId>
<artifactId>css4j-dom4j</artifactId>
<version>${css4jDom4j.version}</version>
</dependency>
<!-- This artifact requires the css4j Maven repository -->
<dependency>
<groupId>io.sf.carte</groupId>
<artifactId>xml-dtd</artifactId>
<version>4.3</version>
</dependency>
</code></pre>
<p>Again, please keep in mind adding css4j's Maven repository to your POM file:
</p>
<pre class="code"><code class="language-xml"><repositories>
<repository>
<id>css4j</id>
<name>CSS4J repository</name>
<url>https://css4j.github.io/maven/</url>
</repository>
</repositories>
</code></pre>
<h3 class="subtema" id="dom4j-depend-compat">Dependencies and compatibility</h3>
<p>The <code>css4j-dom4j</code> artifact transitively implies the <code>css4j</code> core module, but since both artifacts have
different release cycles you may want to add an explicit dependency for the core module, with the latest version.</p>
<p>As explained in the <a href="#modules">modules section</a>, the latest <code>css4j-dom4j</code>
version is supposed to be compatible with <code>css4j</code> artifacts that have the same major
version number and equal or greater minor version. Which in practice means that you should be always
using the latest <code>css4j-dom4j</code> combined with the latest <code>css4j</code> version.</p>
<p>If your project is modular you will need this:</p>
<pre class="code"><code class="language-java">requires io.sf.carte.css4j.dom4j;
requires io.sf.carte.xml.dtd; // Only if you use DefaultEntityResolver
</code></pre>
<h3 class="subtema" id="dom4j-parsing">Loading and parsing an XML document</h3>
<p>This is the easiest way to parse an XML document to use this package with DOM4J, using that library's <code>SAXReader</code>:</p>
<pre class="code"><code class="language-java">SAXReader reader = new SAXReader(XHTMLDocumentFactory.getInstance());
reader.setEntityResolver(new DefaultEntityResolver());
Reader re = ... [reader for HTML document]
XHTMLDocument document = (XHTMLDocument) reader.read(re);
</code></pre>
<p>As you see, a document factory called <a class="codeitem" href="api/latest/io.sf.carte.css4j.dom4j/io/sf/carte/doc/dom4j/XHTMLDocumentFactory.html">XHTMLDocumentFactory</a>
is used. And as mentioned previously, the <a class="codeitem" href=
"api/latest/io.sf.carte.xml.dtd/io/sf/carte/doc/xml/dtd/DefaultEntityResolver.html">DefaultEntityResolver</a>
class is provided by the <a href="https://github.com/css4j/xml-dtd"><code>xml-dtd</code></a>
project.</p>
<p>Once you got the element you want the computed style for (see, for example, the
<a href="https://github.com/dom4j/dom4j/wiki/Quick-Start-Guide" target="_blank">DOM4J Quick Start Guide</a>), just get it with a procedure analogous to the
<a href="https://www.w3.org/TR/2000/REC-DOM-Level-2-Style-20001113/css.html#CSS-ViewCSS" target="_blank"><code>ViewCSS</code></a>
interface:</p>
<pre class="code"><code class="language-java">CSSComputedProperties style = ((CSSStylableElement) element).getComputedStyle(null);
String display = style.getPropertyValue("display");
</code></pre>
<p>Be careful to have the <code>XHTMLDocumentFactory</code> loaded with the desired defaults, like the user agent style sheet
which was loaded by default in 1.0 but not in 2.0 and later.</p>
<h3 class="subtema" id="dom4j-html">Parsing HTML5 into DOM4J</h3>
<p>It is also possible to parse an HTML5 document into a css4j-dom4j tree, with the aforementioned
validator.nu HTML5 parser:</p>
<pre class="code"><code class="language-java">XHTMLDocumentFactory factory = XHTMLDocumentFactory.getInstance();
// Next line is optional: default is TRUE, and is probably what you want
// factory.getStyleSheetFactory().setLenientSystemValues(false);
HtmlParser parser = new HtmlParser(XmlViolationPolicy.ALTER_INFOSET);
parser.setCommentPolicy(XmlViolationPolicy.ALLOW);
parser.setXmlnsPolicy(XmlViolationPolicy.ALLOW);
// Configure the SAXReader with the HtmlParser
SAXReader builder = new SAXReader(factory);
builder.setXMLReader(parser);
// Provide an error handler to avoid exceptions
ErrorHandler errorHandler = [...]
builder.setErrorHandler(errorHandler);
// We do not set the EntityResolver, the HtmlParser does not need it
Reader re = ... [reader for HTML document]
XHTMLDocument document = (XHTMLDocument) builder.read(re);
</code></pre>
<p>Or parse the document with the css4j's builder,
<a href="api/latest/io.sf.carte.css4j/io/sf/carte/doc/dom/XMLDocumentBuilder.html"><code>XMLDocumentBuilder</code></a> (which can account for things like implied HTML parent elements),
instead of dom4j's <a href="https://dom4j.github.io/javadoc/2.1.1/org/dom4j/io/SAXReader.html"><code>SAXReader</code></a>:</p>
<pre class="code"><code class="language-java">XHTMLDocumentFactory factory = XHTMLDocumentFactory.getInstance();
// Next line is optional: default is TRUE, and is probably what you want
// factory.getStyleSheetFactory().setLenientSystemValues(false);
HtmlParser parser = new HtmlParser(XmlViolationPolicy.ALTER_INFOSET);
parser.setCommentPolicy(XmlViolationPolicy.ALLOW);
parser.setXmlnsPolicy(XmlViolationPolicy.ALLOW);
XMLDocumentBuilder builder = new XMLDocumentBuilder(factory);
builder.setHTMLProcessing(true);
builder.setXMLReader(parser);
Reader re = ... [reader for XML document]
InputSource source = new InputSource(re);
XHTMLDocument document = (XHTMLDocument) builder.parse(source);
re.close();
</code></pre>
<h3 class="subtema" id="html-caveats">Caveats about DOM4J and HTML</h3>
<p>A plain HTML document may contain <code>STYLE</code> elements with selectors like <code>div>p</code>,
and css4j's native DOM handles that correctly. However, XML-oriented DOM implementations are likely to serialize
them as "<code>div&gt;p</code>", breaking the CSS. For example, if you serialize a DOM4J document
with <code>asXML()</code> or code like the following:</p>
<pre class="code"><code class="language-java">Writer wri = ... [put your writer here]
OutputFormat format = OutputFormat.createPrettyPrint();
format.setXHTML(true);
HTMLWriter writer = new HTMLWriter(wri, format);
writer.write(document);
</code></pre>
<p>The result will be the "<code>div&gt;p</code>". As explained below, with other DOM implementations
it is possible to work around this problem if one serializes to XHTML with a <a href="#serial-xml-html">properly
configured <code>Transformer</code></a>, but not with DOM4J where you are forced to serialize to plain HTML instead.</p>
</div>
<div class="tema" id="serial-xml-html">
<h2>Serializing HTML with an XML DOM</h2>
<p>As was previously mentioned, if you serialize a plain HTML document
(which does not contain <code>CDATA</code> sections) with an XML-oriented DOM to obtain XHTML,
you can avoid serializing <code>div>p</code> as "<code>div&gt;p</code>" if you use a
<code>Transformer</code> which is configured for the task.</p>
<h3 class="subtema" id="xml-serial-xhtml">Serializing to XHTML</h3>
<p>The code would be like the following:</p>
<pre class="code"><code class="language-java">// Serialize to XHTML
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.setOutputProperty(OutputKeys.METHOD, "xml");
// Put other Transformer configurations here
// ...
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
// Put the embedded style sheets inside CDATA sections
transformer.setOutputProperty(OutputKeys.CDATA_SECTION_ELEMENTS, "script style");
Writer wri = ... [put your writer here]
transformer.transform(new DOMSource(document), new StreamResult(wri));
</code></pre>
<p>which —as the comment says— puts the embedded style sheets inside <code>CDATA</code> sections.
Note, however, that:</p>
<ol>
<li>In HTML, the
<a href="https://html.spec.whatwg.org/multipage/syntax.html#cdata-sections">CDATA sections can only be used in foreign content</a>
—otherwise being considered as "bogus comments"— so you are now in XHTML instead of HTML.</li>
<li>If you use the above code with css4j-dom4j (3.6.1 or later is required), it will not produce
the desired result, so you have to resort to plain HTML serialization by setting <code>OutputKeys.METHOD</code>
to <code>"html"</code> (see next example).</li>
</ol>
<h3 class="subtema" id="xml-serial-plain-html">Serializing to plain HTML (no <code>CDATA</code> sections)</h3>
<pre class="code"><code class="language-java">// Serialize to plain HTML
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.setOutputProperty(OutputKeys.METHOD, "html");
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
Writer wri = ... [put your writer here]
transformer.transform(new DOMSource(document), new StreamResult(wri));
</code></pre>
<p>Unfortunately this procedure adds a <code>META</code> element with an <code>http-equiv</code> attribute,
which is something that you probably did not want.</p>
<p>To handle HTML, the <a
href="https://css4j.github.io/api/latest/io.sf.carte.css4j/io/sf/carte/doc/dom/package-summary.html">native
DOM</a> is recommended over any XML DOM.</p>
</div>
<div class="tema" id="domwrapper">
<h2>Usage with the DOM wrapper</h2>
<p>If you choose to build your document with your favorite DOM implementation instead of the CSS4J
one or the DOM4J back-end, you can use the <a class="codeitem" href=
"api/latest/io.sf.carte.css4j/io/sf/carte/doc/style/css/om/DOMCSSStyleSheetFactory.html#createCSSDocument(org.w3c.dom.Document)">DOMCSSStyleSheetFactory.createCSSDocument(document)</a>
method to wrap a pre-existing DOM Document. Example:</p>
<pre class="code"><code class="language-java">DOMCSSStyleSheetFactory cssFactory = new DOMCSSStyleSheetFactory();
CSSDocument document = cssFactory.createCSSDocument(otherDOMdocument);
</code></pre>
<p>Unlike the native DOM or the DOM4J back-end, the DOM resulting from the DOM wrapper is read-only, although you can change the
values of some nodes.</p>
</div>
<div id="dom-impls-diff" class="tema">
<h2>Consistency of different DOM implementations</h2>
<p>Beware that the computed styles found by each method (native DOM, DOM4J back-end or DOM wrapper) may not be completely identical, due to differences in the
underlying document DOM implementations:</p>
<ul>
<li>XML-oriented DOM implementations do not implement the case sensitivity subtleties of HTML (although this library attempts to emulate the basics).</li>
<li>The <code>:dir()</code> pseudo-class is not fully supported on DOM4J, due to limitations in its DOM support.</li>
</ul>
<p>If you find a difference in styles computed from different back-ends that you believe to be a bug, please report it.</p>
</div>
<div class="tema" id="declared-styles">
<h2>Accessing the declared style sheets</h2>
<h3 class="subtema" id="cssom-indexed-access">CSSOM indexed access</h3>
<p>To access the styles declared in style sheets, we can use the relevant
<a href="https://www.w3.org/TR/cssom-1/" target="_blank">CSS Object Model</a> methods.
For example, suppose that the first style sheet of the document contains only the following rule:</p>
<pre class="code"><code class="language-css">div > .myClass {
font-size: 14pt;
color: var(--myColor, #46f);
}
</code></pre>
<p>then, to access that style sheet and that rule we could use something like this:</p>
<pre class="code"><code class="language-java">/*
* Access style sheet(s) and its rule(s)
*/
// Obtain the list of CSS style sheets
StyleSheetList sheets = document.getStyleSheets();
// Access the first sheet
AbstractCSSStyleSheet sheet = sheets.item(0);
// The first rule in the sheet
AbstractCSSRule rule = sheet.getCssRules().item(0);
// What kind of rule is it?
int ruleType = rule.getType();
// It is a style rule (CSSRule.STYLE_RULE)
</code></pre>
<p>Now we can cast to a <a class="codeitem" href="api/latest/io.sf.carte.css4j/io/sf/carte/doc/style/css/om/StyleRule.html">StyleRule</a>
which gives us access to the selector list and the declared style.</p>
<h3 class="subtema" id="rule-iterators">Rule iterators</h3>
<p>The same as above, but with a rule iterator:</p>
<pre class="code"><code class="language-java">/*
* It is possible to use iterators to retrieve the rules.
*/
sheet = sheets.item(0);
Iterator<AbstractCSSRule> it = sheet.getCssRules().iterator();
// it.hasNext() returns true
// Access the first rule in the sheet
rule = it.next();
// What kind of rule is it?
ruleType = rule.getType();
// It is a style rule (CSSRule.STYLE_RULE)
</code></pre>
<h3 class="subtema" id="sheet-visitors">Sheet Visitors</h3>
<p>You can also use the <a class="codeitem" href="api/latest/io.sf.carte.util/io/sf/carte/util/Visitor.html">Visitor</a>
API to perform many of the tasks that can be achieved with the standard CSSOM, for example:</p>
<ul>
<li><a class="codeitem" href="api/latest/io.sf.carte.css4j/io/sf/carte/doc/style/css/om/AbstractCSSStyleSheet.html#acceptStyleRuleVisitor(io.sf.carte.util.Visitor)">acceptStyleRuleVisitor</a>
to visit style rules in a sheet.</li>
<li><a class="codeitem" href="api/latest/io.sf.carte.css4j/io/sf/carte/doc/style/css/om/AbstractCSSStyleSheet.html#acceptDeclarationRuleVisitor(io.sf.carte.util.Visitor)">acceptDeclarationRuleVisitor</a>
to visit declaration rules in a sheet. In the terminology of this library, a declaration rule is a rule that
contains any kind of declaration, be it property or descriptor declarations. For example, style, font-face and
keyframe rules are all "declaration rules".</li>
<li><a class="codeitem" href="api/latest/io.sf.carte.css4j/io/sf/carte/doc/style/css/om/AbstractCSSStyleSheet.html#acceptDescriptorRuleVisitor(io.sf.carte.util.Visitor)">acceptDescriptorRuleVisitor</a>
to visit declaration rules with descriptors within a sheet.</li>
</ul>
<p>Those methods also apply to sheet lists:</p>
<ul>
<li><a class="codeitem" href="api/latest/io.sf.carte.css4j/io/sf/carte/doc/style/css/CSSStyleSheetList.html#acceptStyleRuleVisitor(io.sf.carte.util.Visitor)">acceptStyleRuleVisitor</a>
to visit style rules in a list of sheets.</li>
<li><a class="codeitem" href="api/latest/io.sf.carte.css4j/io/sf/carte/doc/style/css/CSSStyleSheetList.html#acceptDeclarationRuleVisitor(io.sf.carte.util.Visitor)">acceptDeclarationRuleVisitor</a>
to visit declaration rules in a list of sheets.</li>
<li><a class="codeitem" href="api/latest/io.sf.carte.css4j/io/sf/carte/doc/style/css/CSSStyleSheetList.html#acceptDescriptorRuleVisitor(io.sf.carte.util.Visitor)">acceptDescriptorRuleVisitor</a>
to visit declaration rules with descriptors within a list of sheets.</li>
</ul>
<p>With Visitors, you do not need to navigate the different rules yourself. For an example of its use, see <a
href="embed-svg.html">Embedding SVG in HTML documents</a>.</p>
<h3 class="subtema" id="selectors">Dealing with selectors</h3>
<p>If your use case requires inspecting the selectors, you will need to look at the NSAC api, starting with the
<a class="codeitem" href="api/latest/io.sf.carte.css4j/io/sf/carte/doc/style/css/nsac/Selector.html">Selector</a> class.</p>
<pre class="code"><code class="language-java">// Cast
StyleRule styleRule = (StyleRule) rule;
// Obtain the selector list
SelectorList selist = styleRule.getSelectorList();
// First (and only) selector
Selector selector = selist.item(0);
// Selector type (which we can use to cast the selector)
SelectorType selType = selector.getSelectorType();
// For "div > .myClass" selType would be SelectorType.CHILD
// Cast to CombinatorSelector (see CHILD javadoc)
CombinatorSelector comb = (CombinatorSelector) selector;
// Obtain the two selectors that are combined by ">"
Selector firstSel = comb.getSelector();
SimpleSelector secondSel = comb.getSecondSelector();
// Examine the first selector
SelectorType firstSelType = firstSel.getSelectorType();
// Type is SelectorType.ELEMENT (div)
// Cast to ElementSelector (see ELEMENT javadoc)
ElementSelector elemSel = (ElementSelector) firstSel;
String elemName = elemSel.getLocalName();
// It is "div"
// Now the second selector
SelectorType secondSelType = secondSel.getSelectorType();
// Type is SelectorType.CONDITIONAL (.myClass)
// Cast to ConditionalSelector
ConditionalSelector condSel = (ConditionalSelector) secondSel;
Condition condition = condSel.getCondition();
ConditionType condType = condition.getConditionType();
// The condition type is ConditionType.CLASS
// Cast condition to AttributeCondition (as suggested by the CLASS javadoc)
AttributeCondition attrCond = (AttributeCondition) condition;
// Retrieve the condition (class) name
String cssClassName = attrCond.getValue();
// It is "myClass"
</code></pre>
<h4 class="subtema4" id="nsac-selectors">Low-level selector parsing</h4>
<p>
Sometimes one has to work with stand-alone selectors, for which you can use a NSAC parser:
</p>
<pre class="code"><code class="language-java">CSSParser parser = new CSSParser();
SelectorList selist = parser.parseSelectors("a:not([href]):not([tabindex]):focus,pre,.myClass span");
</code></pre>
<p><em>
Note: you should avoid stand-alone parsing namespaced selectors with <a class="codeitem"
href="api/latest/io.sf.carte.css4j/io/sf/carte/doc/style/css/parser/CSSParser.html#parseSelectors(java.io.Reader)">parseSelectors</a>
and equivalent single-argument methods in <code>CSSParser</code>, as the namespace prefixes
won't map to any namespace URI. See <a class="codeitem"
href="api/latest/io.sf.carte.css4j/io/sf/carte/doc/style/css/parser/CSSParser.html#parseSelectors(java.lang.String,io.sf.carte.doc.style.css.nsac.Parser.NamespaceMap)"
>parseSelectors(String,Parser.NamespaceMap)</a> for a solution to the issue.</em></p>
<h3 class="subtema" id="use-declared-styles">Using the declared styles</h3>
<p>In the previous example we got the style rule in a <code>styleRule</code> variable.
Now we use it to obtain the declared style with <a class="codeitem"
href="api/latest/io.sf.carte.css4j/io/sf/carte/doc/style/css/CSSDeclarationRule.html#getStyle()">getStyle()</a>
and examine the declared properties:</p>
<pre class="code"><code class="language-java">// Obtain the declared style from the rule
AbstractCSSStyleDeclaration style = styleRule.getStyle();
// Obtain the number of properties
int pCount = style.getLength();
// Two properties
/*
* The first property
*/
// Retrieve the name of the first property
String pname0 = style.item(0);
// pname0 is "font-size"
// Obtain the value of "font-size"
StyleValue fontSize = style.getPropertyCSSValue("font-size");
// Is it a length?
SyntaxParser synparser = new SyntaxParser();
CSSValueSyntax syntaxLength = synparser.parseSyntax("<length>");
Match match0 = fontSize.matches(syntaxLength);
// Match.TRUE: yes it is a length
// Let's check the primitive type
CSSValue.Type primiType0 = fontSize.getPrimitiveType();
// CSSValue.Type.NUMERIC: It is numeric
// Obtain the CSS unit and numeric value
NumberValue numericValue = (NumberValue) fontSize;
short unit = numericValue.getUnitType();
// Unit is typographic points (CSSUnit.CSS_PT)
float floatVal = numericValue.getFloatValue(unit);
// Value is 14 (14pt)
// Change the value to 23px (could use any length unit)
numericValue.setFloatValue(CSSUnit.CSS_PX, 23f);
/*
* The second property
*/
// Retrieve the name of the second property
String pname1 = style.item(1);
// pname1 is "color"
// Obtain the value of "color"
StyleValue color = style.getPropertyCSSValue("color");
// Let's check the global type
CSSValue.CssType type1 = color.getCssValueType();
// It is a CSSValue.CssType.PROXY, which means that it is either a
// custom property value (it is not) or a type not known in advance
// But may it be a color?
CSSValueSyntax syntaxColor = synparser.parseSyntax("<color>");
Match match1 = color.matches(syntaxColor);
// Gives Match.PENDING: yes it could be a color, but we do not know for sure
// until the value is computed
// Check the primitive type
CSSValue.Type primiType1 = color.getPrimitiveType();
// It is a LEXICAL value (var() or attr())
// Obtain the custom property name
LexicalValue varValue = (LexicalValue) color;
LexicalUnit lexUnit = varValue.getLexicalUnit();
// It is a VAR (var() function)
// Obtain function parameters
LexicalUnit param = lexUnit.getParameters();
// Let's see its unit type
LexicalType luType = param.getLexicalUnitType();
// It is IDENT (identifier)
String customPropertyName = param.getStringValue();
// Name is --myColor
// Continue with the lexical chain
param = param.getNextLexicalUnit();
// Let's see its unit type
luType = param.getLexicalUnitType();
// It is OPERATOR_COMMA
// In this case we have a fallback value (otherwise next line returns null)
param = param.getNextLexicalUnit();
// Let's see its unit type
luType = param.getLexicalUnitType();
// It is a RGBCOLOR
assertEquals(LexicalType.RGBCOLOR, luType);
// And the color is...
String strColor = param.getCssText();
// Is '#46f'
// Access the individual color components.
// RGB colors are accessed as a rgb() function, so we retrieve
// the function parameters as a doubly-linked list:
LexicalUnit firstComponent = param.getParameters();
// Let's see the lexical type to see how we access the value
LexicalType firstLexUnitType = firstComponent.getLexicalUnitType();
// It is a LexicalType.INTEGER (RGB components could be REAL and PERCENTAGE as well)
// Therefore we use getIntegerValue()
int color_R = firstComponent.getIntegerValue();
// Is 0x44 hex (68 decimal)
// Second component is the next in the linked list.
// Beware that if the color was specified with commas,
// like "rgb(68, 102, 255)", the next one could be a comma!
LexicalUnit secondComponent = firstComponent.getNextLexicalUnit();
int color_G = secondComponent.getIntegerValue();
// Is 0x66 (102 decimal)
// Third component
LexicalUnit thirdComponent = secondComponent.getNextLexicalUnit();
int color_B = thirdComponent.getIntegerValue();
// Is 0xff (255 decimal)
</code></pre>
<p>Now let's set another property using <a class="codeitem"
href="api/latest/io.sf.carte.css4j/io/sf/carte/doc/style/css/CSSStyleDeclaration.html#setProperty(java.lang.String,java.lang.String,java.lang.String)">setProperty</a>:</p>
<pre class="code"><code class="language-java">/*
* Set another property, with important priority
*/
style.setProperty("background-color", "#d0dfee7a", "important");
pCount = style.getLength();
// Now we have three properties
</code></pre>
<p>If you are updating any of the declared sheets —that is, the sheets returned by <code>document.getStyleSheets()</code>—
with <code>setProperty</code> while you are computing styles, the changes will not apply to the computed styles
until you update the cascade with a call to <a class="codeitem"
href="api/latest/io.sf.carte.css4j/io/sf/carte/doc/style/css/CSSDocument.html#rebuildCascade()">rebuildCascade()</a>.</p>
<h3 class="subtema" id="update-internal-sheets">Updating internal sheets</h3>
<p><b>IMPORTANT:</b> once we are done updating a style sheet, if the sheet is internal to the
document (that is, it is located inside a <code><style></code> element), the actual
contents of the <code><style></code> element will not be updated until you execute
<code>normalize()</code> on that element node:</p>
<pre class="code"><code class="language-java">// Obtain the owner node
org.w3c.dom.Node styleNode = sheet.getOwnerNode();
// Which type is it?
short nodeType = styleNode.getNodeType();
// It is a ELEMENT_NODE (could also be a PROCESSING_INSTRUCTION_NODE)
// Get the name of the element
String nodeName = styleNode.getNodeName();
// It is "style", as expected
// Get the element content (the initial serialized style sheet)
String preContent = styleNode.getTextContent();
// Serialize the current style sheet into the inner text node
styleNode.normalize();
// Get the normalized element content (the current serialization)
String afterContent = styleNode.getTextContent();
// preContent and afterContent are different
</code></pre>
</div>
<div class="tema" id="inline-styles">
<h2>Working with inline styles</h2>
<p>Now we are going to obtain the inline style of an element, as given by the <code>style</code> attribute.
In the next example, the inline style is supposed to contain the declaration of a custom property called
<code>--myColor</code> with a value <code>#54e</code>.</p>
<pre class="code"><code class="language-java">/*
* Access a style attribute (inline styles)
*/
// First obtain the desired element, for example:
CSSElement element = document.getElementById("someId");
// Now the inline style. It can be directly accessed
CSSStyleDeclaration inlineStyle = element.getStyle();
// How many properties are defined?
int inlinePCount = inlineStyle.getLength();
// Suppose that it is just one called --myColor
// Retrieve its name
String inlinePname0 = inlineStyle.item(0);
// It is "--myColor"
// Now get the value
CSSValue myColor = inlineStyle.getPropertyCSSValue(inlinePname0);
/*
* Handle the custom property in myColor
*/
// Get the general value type
CSSValue.CssType type = myColor.getCssValueType();
// Being a custom property, it must be a CssType.PROXY
// But is it (or could be) a color?
Match matchCP = myColor.matches(syntaxColor);
// Match.TRUE: Yes it is for sure!
// Let's see the primitive type, although...
CSSValue.Type primiType = myColor.getPrimitiveType();
// custom properties are *always* Type.LEXICAL values
LexicalValue lvColor = (LexicalValue) myColor;
// Can we know its final value type once it is processed?
Type finalType = lvColor.getFinalType();
// Yes, as suggested by the result of calling 'matches'
// ...it is a Type.COLOR
// Let's retrieve its value
LexicalUnit lexUnitCP = lvColor.getLexicalUnit();
// First see its unit type
LexicalType luTypeCP = lexUnitCP.getLexicalUnitType();
// It is a RGBCOLOR
// And the color is...
String strlexUnitCP = lexUnitCP.getCssText();
// Is '#54e'. We could also access its components
// Let's change the value of the custom property
// We want to set '#667'
inlineStyle.setProperty(inlinePname0, "#667", null);
// Check that we successfully assigned the new value:
String newInlineValue = inlineStyle.getPropertyValue(inlinePname0);
// Yes it is '#667' as we wanted
</code></pre>
<p>After changing an inline style you do not need to call any method (like how <code>normalize()</code> was used for
internal sheets) to modify the document. The serialization of (inline) style attributes is always up to date.</p>
<p>The small drawback is that the <code>style</code> attributes may not appear exactly as they were specified, but instead
according to the serialization given by this library (which can be tailored a bit, for example by customizing <a class="codeitem"
href="api/latest/io.sf.carte.css4j/io/sf/carte/doc/style/css/StyleFormattingContext.html#endInlinePropertyDeclaration(io.sf.carte.util.SimpleWriter)"
>endInlinePropertyDeclaration()</a> in your own <code>StyleFormattingContext</code>, see <a href="#styleformatting">CSS
style formatting</a> for details).</p>
</div>
<div class="tema" id="overridestyles">
<h2>Override styles</h2>
<p>Override styles, that come after the author style sheet in the cascade algorithm, are not part of the formal standard
but are supported by this library; they are like "hidden" inline styles.</p>
<p>To access the override style of an element, use <code>CSSElement</code>'s <a class="codeitem"
href="api/latest/io.sf.carte.css4j/io/sf/carte/doc/style/css/CSSElement.html#getOverrideStyle(io.sf.carte.doc.style.css.nsac.Condition)">getOverrideStyle</a>.
For example:</p>
<pre class="code"><code class="language-java">element.getOverrideStyle(null).setCssText("padding: 6pt;");
</code></pre>
<p>Override styles are defined at the <a href="https://www.w3.org/TR/2000/REC-DOM-Level-2-Style-20001113/css.html#CSS-DocumentCSS" target="_blank">DocumentCSS interface</a> description.</p>
</div>
<div id="cascade" class="tema">
<h2>Configuring the cascade</h2>
<p>Depending on your use case, you may need to set the <a href="https://www.w3.org/TR/css-cascade-3/#origin">user
agent (UA) style sheet</a> before starting to compute styles.
The library supports two different UA sheets, one for <code>STRICT</code> ('standards') mode and another for <code>QUIRKS</code>.
To set either of those sheets, first obtain an instance of the factory that you are using:
</p>
<pre class="code"><code class="language-java">// Instantiate the new factory or get it from an object that you are already using.
AbstractCSSStyleSheetFactory cssFactory = ...
</code></pre>
<p>If you are using the DOM4J classes, you may want to do:</p>
<pre class="code"><code class="language-java">AbstractCSSStyleSheetFactory cssFactory = XHTMLDocumentFactory.getInstance().getStyleSheetFactory();
</code></pre>
<p>If you are processing HTML, css4j's default HTML5 UA sheet (based on W3C/WHATWG recommendations) should be
appropriate for you:
</p>
<pre class="code"><code class="language-java">cssFactory.setDefaultHTMLUserAgentSheet();
</code></pre>
<p>But if you want to set your own UA sheet, first obtain a reference to the sheet:</p>
<pre class="code"><code class="language-java">BaseCSSStyleSheet sheet = cssFactory.getUserAgentStyleSheet(CSSDocument.ComplianceMode.STRICT);
</code></pre>
<p>This is assuming the <a class="codeitem" href="api/latest/io.sf.carte.css4j/io/sf/carte/doc/style/css/CSSDocument.ComplianceMode.html#STRICT">STRICT</a>
mode, <i>i.e.</i> that you use documents with a <code>DOCTYPE</code>, otherwise use <code>QUIRKS</code> (or you may want to set both UA sheets).
</p>
<p>If the UA sheet already contains rules (it is empty by default), clean it:</p>
<pre class="code"><code class="language-java">sheet.getCssRules().clear();
</code></pre>
<p>And now load the new sheet:</p>
<pre class="code"><code class="language-java">Reader reader = ... [reads the UA sheet]
sheet.parseStyleSheet(reader, CSSStyleSheet.COMMENTS_IGNORE);
reader.close();
</code></pre>
<p>As the UA sheet's comments are rarely of interest at the OM level, they were ignored during the parse
process (notice the <code>COMMENTS_IGNORE</code> flag).</p>
<p><em>Note: this implementation does not support <code>important</code> style declarations in the UA sheet.</em></p>
<h3 class="subtema" id="user-style-sheet">Setting the user style sheet</h3>
<p>There is also the possibility to set a user style sheet (with 'user' origin) via <a class="codeitem" href="api/latest/io.sf.carte.css4j/io/sf/carte/doc/style/css/CSSStyleSheetFactory.html#setUserStyleSheet(java.io.Reader)">setUserStyleSheet</a>:</p>
<pre class="code"><code class="language-java">Reader reader = ... [reads the user sheet]
cssFactory.setUserStyleSheet(reader);
reader.close();
</code></pre>
<p>or the URL variant:</p>
<pre class="code"><code class="language-java">String url = "https://www.example.com/user.css";
cssFactory.setUserStyleSheet(url, null);
</code></pre>
<p>Both <code>important</code> and normal declarations are supported in the <a class="codeitem" href="api/latest/io.sf.carte.css4j/io/sf/carte/doc/style/css/CSSStyleSheetFactory.html#ORIGIN_USER">user</a> style sheet.</p>
</div>
<div class="tema" id="mediahandling">
<h2>Media handling</h2>
<h3 class="subtema" id="setting-target-medium">Setting a target medium for computed styles</h3>
<p>By default, computed styles only take into account generic styles that are common to all media. If you want to target a more specific medium,
you have to use the <code>CSSDocument.setTargetMedium("<i>medium</i>")</code> method. For example, if your document has the following style sheets linked:</p>
<pre class="code"><code class="language-xml"><link href="http://www.example.com/css/sheet.css" rel="stylesheet" type="text/css" />
<link href="http://www.example.com/css/sheet_for_print.css" rel="stylesheet" media="print" type="text/css" />
</code></pre>
<p>Computed styles will initially take into account only the "sheet.css" style sheet. However, if you execute the following method:</p>
<pre class="code"><code class="language-java">document.setTargetMedium("print");
</code></pre>
<p>Then all subsequently computed styles will account for the merged style sheet from "sheet.css" and "sheet_for_print.css".</p>
<p>This way to tie a document with a medium is not totally standard, as the W3C APIs would probably expect a <code>DeviceFactory</code>-related object implementing the
<a href="https://www.w3.org/2003/01/dom2-javadoc/org/w3c/dom/css/ViewCSS.html" target="_blank"><code>ViewCSS</code></a> interface and referencing the document,
but this approach allows to isolate DOM logic inside DOM objects and keep the <code>DeviceFactory</code> for media-specific information only.</p>
<h3 class="subtema" id="media-queries">Media queries</h3>
<p>The library provides an object-oriented interface to access media queries. If you obtain a <a class="codeitem"
href="api/latest/io.sf.carte.css4j/io/sf/carte/doc/style/css/MediaQueryList.html">MediaQueryList</a> from a style
sheet or a factory object, you can handle it like in the following example, where we create a media query
and then produce a style sheet tied to it:</p>
<pre class="code"><code class="language-java">// We instantiate a factory based on CSSDOMImplementation,
// but could do the same with any of the other factories
AbstractCSSStyleSheetFactory cssFactory = new CSSDOMImplementation();
/*
* Parsing a Media Query
*/
MediaQueryList mql = cssFactory.createMediaQueryList("screen and (600px <= width < 1200px)",
null);
// How many queries we got?
int numQueries = mql.getLength();
// One
// Get the first and only query
MediaQuery mediaQ = mql.getMediaQuery(0);
// The media type
String medium = mediaQ.getMediaType();
// It is "screen"
/*
* Have a look at the associated boolean condition(s)
*/
BooleanCondition cond = mediaQ.getCondition();
// The condition type
BooleanCondition.Type condType = cond.getType();
// It is BooleanCondition.Type.AND
// As the AND javadoc suggests, call getSubConditions()
List<BooleanCondition> andConds = cond.getSubConditions();
// andConds.size() is 2
// The first operand of the AND
BooleanCondition cond1 = andConds.get(0);
// The condition type
BooleanCondition.Type cond1Type = cond1.getType();
// It is BooleanCondition.Type.PREDICATE
// Now, following the indication from the getCondition() javadoc
// we cast the predicate to a MediaQueryPredicate
MediaQueryPredicate predicate1 = (MediaQueryPredicate) cond1;
// predicate1.getPredicateType() is MediaQueryPredicate.MEDIA_TYPE
// predicate1.getName() is "screen"
// The second operand of the AND
BooleanCondition cond2 = andConds.get(1);
// The condition type
BooleanCondition.Type cond2Type = cond2.getType();
// It is BooleanCondition.Type.PREDICATE
// Again, following the indication from the getCondition() javadoc
// we cast the predicate to a MediaQueryPredicate
MediaQueryPredicate predicate2 = (MediaQueryPredicate) cond2;
// predicate2.getPredicateType() is MediaQueryPredicate.MEDIA_FEATURE
// predicate2.getName() is "width"
// Cast predicate2 to MediaFeature
MediaFeature feature = (MediaFeature) predicate2;
// feature.getRangeType() is MediaFeaturePredicate.FEATURE_LE_AND_LT
// Obtain the first value in the LE_AND_LT range