summaryrefslogtreecommitdiff
path: root/appl/ebook/stylesheet.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/ebook/stylesheet.b')
-rw-r--r--appl/ebook/stylesheet.b156
1 files changed, 156 insertions, 0 deletions
diff --git a/appl/ebook/stylesheet.b b/appl/ebook/stylesheet.b
new file mode 100644
index 00000000..f142c121
--- /dev/null
+++ b/appl/ebook/stylesheet.b
@@ -0,0 +1,156 @@
+implement Stylesheet;
+
+include "sys.m";
+ sys: Sys;
+include "stylesheet.m";
+include "strmap.m";
+ strmap: Strmap;
+ Map: import strmap;
+include "cssparser.m";
+ Decl: import CSSparser;
+
+stylemap: ref Map;
+numstyles: int;
+
+RULEHASH: con 23;
+
+# specificity:
+# bits 0-26 declaration order
+# bit 27 class count (0 or 1)
+# bit 28 tagname count (0 or 1)
+# bit 28 id count (0 or 1) (inline style only)
+# bit 29-30 origin (0, 1 or 2 - default, reader, author)
+# bit 31 importance
+
+# order of these as implied by CSS1 ยง3.2
+TAG,
+CLASS,
+ID: con 1 << (iota + 26);
+ORIGINSHIFT: con 29;
+IMPORTANCE: con 1<<30;
+
+init(a: array of string)
+{
+ sys = load Sys Sys->PATH;
+ strmap = load Strmap Strmap->PATH;
+ if (strmap == nil) {
+ sys->fprint(sys->fildes(2), "stylesheet: cannot load %s: %r\n", Strmap->PATH);
+ raise "fail:bad module";
+ }
+ stylemap = Map.new(a);
+ numstyles = len a;
+}
+
+Sheet.new(): ref Sheet
+{
+ return ref Sheet(array[RULEHASH] of list of Rule, 0);
+}
+
+Sheet.addrules(sheet: self ref Sheet, rules: list of (string, list of Decl), origin: int)
+{
+ origin <<= ORIGINSHIFT;
+ for (; rules != nil; rules = tl rules) {
+ (sel, decls) := hd rules;
+ (tag, class) := selector(sel);
+ (key, sub) := (tag, "");
+ specificity := sheet.ruleid++;
+ if (tag != nil)
+ specificity |= TAG;
+ if (class != nil) {
+ specificity |= CLASS;
+ (key, sub) = ("." + class, tag);
+ }
+ specificity |= origin;
+
+ attrs: list of (int, int, string);
+ for (; decls != nil; decls = tl decls) {
+ d := mkdecl(hd decls, specificity);
+ if (d.attrid != -1)
+ attrs = d :: attrs;
+ }
+
+ n := hashfn(key, RULEHASH);
+ sheet.rules[n] = (key, sub, attrs) :: sheet.rules[n];
+ }
+}
+
+# assume selector is well-formed, having been previously parsed.
+selector(s: string): (string, string)
+{
+ for (i := 0; i < len s; i++)
+ if (s[i] == '.')
+ break;
+ if (i == len s)
+ return (s, nil);
+ return (s[0:i], s[i + 1:]);
+}
+
+
+Sheet.newstyle(sheet: self ref Sheet): ref Style
+{
+ return ref Style(sheet, array[numstyles] of string, array[numstyles] of {* => 0});
+}
+
+adddecl(style: ref Style, d: Ldecl)
+{
+ if (d.specificity > style.spec[d.attrid]) {
+ style.attrs[d.attrid] = d.val;
+ style.spec[d.attrid] = d.specificity;
+ }
+}
+
+Style.add(style: self ref Style, tag, class: string)
+{
+ rules := style.sheet.rules;
+ if (class != nil) {
+ key := "." + class;
+ v := hashfn(key, RULEHASH);
+ for (r := rules[v]; r != nil; r = tl r)
+ if ((hd r).key == key && ((hd r).sub == nil || (hd r).sub == tag))
+ for (decls := (hd r).decls; decls != nil; decls = tl decls)
+ adddecl(style, hd decls);
+ }
+ v := hashfn(tag, RULEHASH);
+ for (r := rules[v]; r != nil; r = tl r)
+ if ((hd r).key == tag)
+ for (decls := (hd r).decls; decls != nil; decls = tl decls)
+ adddecl(style, hd decls);
+}
+
+# add a specific set of attributes to a style;
+# attrs is list of (attrname, important, val).
+Style.adddecls(style: self ref Style, decls: list of Decl)
+{
+ specificity := ID | (AUTHOR << ORIGINSHIFT);
+ for (; decls != nil; decls = tl decls) {
+ d := mkdecl(hd decls, specificity);
+ if (d.attrid != -1)
+ adddecl(style, d);
+ }
+}
+
+Style.addone(style: self ref Style, attrid: int, origin: int, val: string)
+{
+ # XXX specificity is probably wrong here.
+ adddecl(style, (attrid, origin << ORIGINSHIFT, val));
+}
+
+# convert a declaration from extern (attrname, important, val) form
+# to intern (attrid, specificity, val) form.
+# XXX could warn for unknown attribute here...
+mkdecl(d: Decl, specificity: int): Ldecl
+{
+ if (d.important)
+ specificity |= IMPORTANCE;
+ return (stylemap.i(d.name), specificity, d.val);
+}
+
+hashfn(s: string, n: int): int
+{
+ h := 0;
+ m := len s;
+ for(i:=0; i<m; i++){
+ h = 65599*h+s[i];
+ }
+ return (h & 16r7fffffff) % n;
+}