namespace XFESystem.Browser;
internal static class BrowserCssImages
{
internal static IEnumerable<string> Sources(string value)
{
for (int offset = 0; offset < value.Length;)
{
int begin = value.IndexOf("url(", offset, StringComparison.OrdinalIgnoreCase);
if (begin < 0) yield break;
int cursor = begin + 4;
while (cursor < value.Length && char.IsWhiteSpace(value[cursor])) cursor++;
char quote = cursor < value.Length && value[cursor] is '\'' or '"' ? value[cursor++] : '\0';
var source = new System.Text.StringBuilder();
while (cursor < value.Length && (quote == '\0' ? value[cursor] != ')' : value[cursor] != quote))
{
char character = value[cursor++];
if (character == '\\' && cursor < value.Length) character = value[cursor++];
source.Append(character);
}
if (quote != '\0' && cursor < value.Length) cursor++;
while (cursor < value.Length && char.IsWhiteSpace(value[cursor])) cursor++;
if (cursor >= value.Length || value[cursor] != ')') yield break;
if (source.Length != 0) yield return source.ToString().Trim();
offset = cursor + 1;
}
}
internal static BrowserRect Size(BrowserRect area, BrowserCanvasSurface? image, CssComputedStyle style)
{
if (image is null) return area;
double width = image.Width, height = image.Height;
string size = style.Get("background-size", "auto").Split(',')[0].Trim();
if (size is "cover" or "contain")
{
double scale = size == "cover" ? Math.Max(area.Width / width, area.Height / height) : Math.Min(area.Width / width, area.Height / height);
width *= scale; height *= scale;
}
else
{
var tokens = size.Split(' ', StringSplitOptions.RemoveEmptyEntries);
double? w = tokens.Length > 0 ? CssValue.Length(tokens[0], area.Width, style.Font.Size, style.Viewport, 16) : null;
double? h = tokens.Length > 1 ? CssValue.Length(tokens[1], area.Height, style.Font.Size, style.Viewport, 16) : null;
if (w is not null || h is not null)
{ double ratio = width / height; width = w ?? h!.Value * ratio; height = h ?? w!.Value / ratio; }
}
string[] positions = style.Get("background-position", "0% 0%").Split(' ', StringSplitOptions.RemoveEmptyEntries);
string px = positions.ElementAtOrDefault(0) ?? "0%", py = positions.ElementAtOrDefault(1) ?? "center";
double Position(string token, double spare, double basis) => token switch
{ "left" or "top" => 0, "center" => spare / 2, "right" or "bottom" => spare, _ => token.EndsWith('%') ? CssValue.Number(token[..^1], 0) * spare / 100 : CssValue.Length(token, basis, style.Font.Size, style.Viewport, 16) ?? 0 };
return new(area.X + Position(px, area.Width - width, area.Width), area.Y + Position(py, area.Height - height, area.Height), width, height);
}
}
namespace XFESystem.Browser;
internal static class BrowserCssImages
{
internal static IEnumerable<string> Sources(string value)
{
for (int offset = 0; offset < value.Length;)
{
int begin = value.IndexOf("url(", offset, StringComparison.OrdinalIgnoreCase);
if (begin < 0) yield break;
int cursor = begin + 4;
while (cursor < value.Length && char.IsWhiteSpace(value[cursor])) cursor++;
char quote = cursor < value.Length && value[cursor] is '\'' or '"' ? value[cursor++] : '\0';
var source = new System.Text.StringBuilder();
while (cursor < value.Length && (quote == '\0' ? value[cursor] != ')' : value[cursor] != quote))
{
char character = value[cursor++];
if (character == '\\' && cursor < value.Length) character = value[cursor++];
source.Append(character);
}
if (quote != '\0' && cursor < value.Length) cursor++;
while (cursor < value.Length && char.IsWhiteSpace(value[cursor])) cursor++;
if (cursor >= value.Length || value[cursor] != ')') yield break;
if (source.Length != 0) yield return source.ToString().Trim();
offset = cursor + 1;
}
}
internal static BrowserRect Size(BrowserRect area, BrowserCanvasSurface? image, CssComputedStyle style)
{
if (image is null) return area;
double width = image.Width, height = image.Height;
string size = style.Get("background-size", "auto").Split(',')[0].Trim();
if (size is "cover" or "contain")
{
double scale = size == "cover" ? Math.Max(area.Width / width, area.Height / height) : Math.Min(area.Width / width, area.Height / height);
width *= scale; height *= scale;
}
else
{
var tokens = size.Split(' ', StringSplitOptions.RemoveEmptyEntries);
double? w = tokens.Length > 0 ? CssValue.Length(tokens[0], area.Width, style.Font.Size, style.Viewport, 16) : null;
double? h = tokens.Length > 1 ? CssValue.Length(tokens[1], area.Height, style.Font.Size, style.Viewport, 16) : null;
if (w is not null || h is not null)
{ double ratio = width / height; width = w ?? h!.Value * ratio; height = h ?? w!.Value / ratio; }
}
string[] positions = style.Get("background-position", "0% 0%").Split(' ', StringSplitOptions.RemoveEmptyEntries);
string px = positions.ElementAtOrDefault(0) ?? "0%", py = positions.ElementAtOrDefault(1) ?? "center";
double Position(string token, double spare, double basis) => token switch
{ "left" or "top" => 0, "center" => spare / 2, "right" or "bottom" => spare, _ => token.EndsWith('%') ? CssValue.Number(token[..^1], 0) * spare / 100 : CssValue.Length(token, basis, style.Font.Size, style.Viewport, 16) ?? 0 };
return new(area.X + Position(px, area.Width - width, area.Width), area.Y + Position(py, area.Height - height, area.Height), width, height);
}
}