Skip to content

Commit

Permalink
Initial support for dot patterns in tags.
Browse files Browse the repository at this point in the history
  • Loading branch information
kohler committed Sep 3, 2024
1 parent 925fcce commit 7e3ae68
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 45 deletions.
69 changes: 45 additions & 24 deletions lib/tagger.php
Original file line number Diff line number Diff line change
Expand Up @@ -377,29 +377,21 @@ class TagStyle {
const BG = 8;
const TEXT = 16;
const STYLE = 24; // BG | TEXT
const PATTERN = 32;

// see also style.css
const KNOWN_COLORS = " red:ffd8d8 orange:fdebcc yellow:fdffcb green:d8ffd8 blue:d8d8ff purple:f2d8f8 gray:e2e2e2 white:ffffff";

/** @param string $s
* @return ?string */
static function dynamic_style($s) {
if (str_starts_with($s, "rgb-")
&& (strlen($s) === 7 || strlen($s) === 10)
&& ctype_xdigit(substr($s, 4))) {
if (strlen($s) === 7) {
return "rgb-{$s[4]}{$s[4]}{$s[5]}{$s[5]}{$s[6]}{$s[6]}";
} else {
return $s;
}
} else if (str_starts_with($s, "text-rgb-")
&& (strlen($s) === 12 || strlen($s) === 15)
&& ctype_xdigit(substr($s, 9))) {
if (strlen($s) === 12) {
return "text-rgb-{$s[9]}{$s[9]}{$s[10]}{$s[10]}{$s[11]}{$s[11]}";
} else {
return $s;
}
$len = strlen($s);
if (str_starts_with($s, "rgb-")) {
return self::expand_color($s, 4, false);
} else if (str_starts_with($s, "text-rgb-")) {
return self::expand_color($s, 9, false);
} else if (str_starts_with($s, "dot-")) {
return self::expand_color($s, 4, true);
} else if (str_starts_with($s, "font-")
|| str_starts_with($s, "weight-")) {
return $s;
Expand All @@ -408,6 +400,33 @@ static function dynamic_style($s) {
}
}

/** @param string $color
* @param int $pfxlen
* @param bool $allow_name
* @return string */
static function expand_color($color, $pfxlen, $allow_name) {
if ($allow_name && substr($color, $pfxlen, 4) === "rgb-") {
$allow_name = false;
$pfxlen += 4;
}
if ($allow_name) {
$srch = " " . substr($color, $pfxlen) . ":";
if (($p = strpos(self::KNOWN_COLORS, $srch)) !== false) {
return substr($color, 0, $pfxlen) . "rgb-" . substr(self::KNOWN_COLORS, $p + strlen($srch), 6);
}
} else if (ctype_xdigit(substr($color, $pfxlen))) {
if (strlen($color) === $pfxlen + 3) {
$r = $color[$pfxlen];
$g = $color[$pfxlen + 1];
$b = $color[$pfxlen + 2];
return substr($color, 0, $pfxlen) . "{$r}{$r}{$g}{$g}{$b}{$b}";
} else if (strlen($color) === $pfxlen + 6) {
return $color;
}
}
return null;
}

/** @param string $text */
function __construct($text) {
// $text format: [name=]style[^][@][#][-][*]
Expand Down Expand Up @@ -444,8 +463,13 @@ function __construct($text) {
&& ($dstyle = self::dynamic_style($this->style)) !== null) {
$this->style = $dstyle;
$sclass |= self::DYNAMIC;
if ($dstyle[0] === "r") { // `rgb-`
$sclass |= self::BG | self::BADGE;
$first = $dstyle[0];
if ($first === "r") { // `rgb-`
$sclass |= self::BG;
} else if ($first === "d") { // `dot-`
$sclass |= self::BG | self::PATTERN;
} else if ($first === "b") { // `badge-`
$sclass |= self::BADGE;
} else {
$sclass |= self::TEXT;
}
Expand All @@ -455,7 +479,7 @@ function __construct($text) {
}
$this->sclass = $sclass;
if ($this->dark === null
&& (($sclass & self::DYNAMIC) === 0 || ($sclass & self::BG) === 0)) {
&& ($sclass & (self::DYNAMIC | self::BG | self::PATTERN)) !== (self::DYNAMIC | self::BG)) {
$this->dark = false;
}
}
Expand Down Expand Up @@ -609,10 +633,7 @@ function find($tag) {
&& $ltag !== ""
&& (($ltag[0] === ":" && $this->check_emoji_code($ltag))
|| isset($this->style_lmap[$ltag])
|| (str_starts_with($ltag, "rgb-") && ctype_xdigit(substr($ltag, 4)))
|| (str_starts_with($ltag, "text-rgb-") && ctype_xdigit(substr($ltag, 9)))
|| str_starts_with($ltag, "font-")
|| str_starts_with($ltag, "weight-"))) {
|| TagStyle::dynamic_style($ltag))) {
$ti = $this->ensure($tag);
}
if ($this->pattern_version > 0
Expand Down Expand Up @@ -890,7 +911,7 @@ function canonical_listed_styles($sclassmatch) {
private function color_regex() {
if (!$this->color_re) {
$rex = [
"{(?:\\A| )(?:(?:\\d*~|~~|)(font-[^\s#]+|weight-(?:[a-z]+|\d+)|(?:text-|)rgb-[0-9a-f]{3}(?:|[0-9a-f]{3})"
"{(?:\\A| )(?:(?:\\d*~|~~|)(font-[^\s#]+|weight-(?:[a-z]+|\d+)|dot-[-0-9a-z]+|(?:text-|)rgb-[0-9a-f]{3}(?:|[0-9a-f]{3})"
];
foreach ($this->style_lmap as $style => $ks) {
if (($ks->sclass & TagStyle::STYLE) !== 0)
Expand Down
67 changes: 46 additions & 21 deletions scripts/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -11547,6 +11547,18 @@ function make_color(k, r, g, b, a) {
type: null
};
}
function color_compare(a, b) {
if (a.s < 0.1 && b.s >= 0.1)
return a.l >= 0.9 ? -1 : 1;
else if (b.s < 0.1 && a.s >= 0.1)
return b.l >= 0.9 ? 1 : -1;
else if (a.h != b.h)
return a.h < b.h ? -1 : 1;
else if (a.l != b.l)
return a.l < b.l ? 1 : -1;
else
return a.s < b.s ? 1 : (a.s == b.s ? 0 : -1);
}
const class_analyses = {tagbg: null, dark: null, badge: null};
function store_class_analysis(k, anal) {
class_analyses[k] = anal;
Expand All @@ -11573,6 +11585,10 @@ function analyze_class(k) {
ensure_stylesheet().insertRule(".".concat(k, " { color: rgb(", c.r, ", ", c.g, ", ", c.b, "); }"), 0);
return set("text", c);
}
if (k.startsWith("tag-dot-rgb-")
&& (m = k.match(/^tag-dot-rgb-([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/))) {
return set("dot", make_color(k, parseInt(m[1], 16), parseInt(m[2], 16), parseInt(m[3], 16), 1.0));
}
if (k.startsWith("tag-font-")) {
m = k.substring(9).replace(/_/g, " ");
const ff = /^(?:serif|sans-serif|monospace|cursive|fantasy|system-ui|ui-(?:serif|sans-serif|monospace|rounded)|math|emoji|fangsong)$/.test(m) ? "" : "\"";
Expand Down Expand Up @@ -11645,17 +11661,20 @@ function gfillcolor(color) {
return color.gfill;
}
function fillcolor(color) {
var gf = gfillcolor(color);
const gf = gfillcolor(color);
return "rgba(".concat(gf[0], ", ", gf[1], ", ", gf[2], ", ", gf[3], ")");
}
function strokecolor(color) {
var gf = gfillcolor(color);
let gf = gfillcolor(color);
if (color.l > 0.75) {
var f = 0.75 / color.l;
const f = 0.75 / color.l;
gf = [gf[0] * f, gf[1] * f, gf[2] * f, gf[3]];
}
return "rgba(".concat(gf[0], ", ", gf[1], ", ", gf[2], ", 0.8)");
}
function paramcolor(param, color) {
return (param.type === 1 ? fillcolor : bgcolor)(color);
}
function ensure_graph_rules(color) {
if (!color.has_graph) {
var stylesheet = ensure_stylesheet(), k = color.k;
Expand All @@ -11674,46 +11693,52 @@ return function (classes, type) {
if (index in fmap)
return fmap[index];
// canonicalize classes, sort by color and luminance
const xtags = classes.split(/\s+/), colors = [];
const xtags = classes.split(/\s+/), colors = [], dots = [];
for (const k of xtags) {
const ka = analyze_class(k);
if (ka && ka.type === "bg" && colors.indexOf(ka) < 0) {
colors.push(ka);
param.type === 1 && ensure_graph_rules(ka);
} else if (ka && ka.type === "dot" && dots.indexOf(ka) < 0) {
dots.push(ka);
}
}
colors.sort(function (a, b) {
if (a.s < 0.1 && b.s >= 0.1)
return a.l >= 0.9 ? -1 : 1;
else if (b.s < 0.1 && a.s >= 0.1)
return b.l >= 0.9 ? 1 : -1;
else if (a.h != b.h)
return a.h < b.h ? -1 : 1;
else if (a.l != b.l)
return a.l < b.l ? 1 : -1;
else
return a.s < b.s ? 1 : (a.s == b.s ? 0 : -1);
});
colors.sort(color_compare);
dots.sort(color_compare);
// check on classes in canonical order
const tags = [];
for (const c of colors) {
tags.push(c.k);
}
for (const c of dots) {
tags.push(c.k);
}
const cindex = param.prefix + tags.join(" ");
if (cindex in fmap || tags.length < 2) {
if (cindex in fmap || (tags.length < 2 && dots.length === 0)) {
fmap[index] = fmap[cindex] || null;
return fmap[index];
}
// create pattern
const id = "svgpat__" + cindex.replace(/\s+/g, "__"),
size = param.size + Math.max(0, tags.length - 2) * param.incr,
sw = size / tags.length,
size = param.size + Math.max(0, colors.length - 2) * param.incr,
sw = size / colors.length,
svgns = "http://www.w3.org/2000/svg",
pathsfx = " 0l".concat(-size, " ", size, "l", sw, " 0l", size, " ", -size, "z"),
dxs = [];
for (let i = 0; i !== colors.length; ++i) {
const k = param.type === 1 ? fillcolor(colors[i]) : bgcolor(colors[i]);
dxs.push("M".concat(sw * i, pathsfx), k, "M".concat(sw * i + size, pathsfx), k);
dxs.push("M".concat(sw * i, pathsfx, "M", sw * i + size, pathsfx), paramcolor(param, colors[i]));
}
if (dots.length > 0) {
const d = size * 0.125, r = d * 0.75, pds = [], nd = dots.length / 8,
offs = [1, 5, 5, 1, 7, 3, 7, 3, 1, 5, 1, 5, 7, 3, 3, 7];
let pd = "";
for (let i = 0; i < 8; ++i) {
const ci = Math.floor(i * nd);
pds[ci] = (pds[ci] || "") + `M${d*offs[i]},${d*offs[8+i]-r}a${r},${r} 0,0,1 ${r},${r} ${r},${r} 0,1,1 ${-r},${-r}z`;
}
for (let i = 0; i < pds.length; ++i) {
dxs.push(pds[i], paramcolor(param, dots[i]));
}
}
if (param.type === 1) {
if (svgdef === null) {
Expand Down

0 comments on commit 7e3ae68

Please sign in to comment.