Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add shadow, lineposition and textcase options to SVG fonts #6983

Merged
merged 62 commits into from
May 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
5142be6
add text-shadow, text-decoration-line & text-transform in drawing.font
archmoj Apr 24, 2024
10d5558
declare new text font styling attributes
archmoj Apr 24, 2024
0a1f7a8
implementation
archmoj Apr 24, 2024
1b67344
adjust toSVG to skip none text style values
archmoj Apr 24, 2024
fe7a30a
set auto shadow for sankey, parcats and parcoords that has text-shado…
archmoj Apr 24, 2024
368cd94
update sankey, parcats & parcoords baselines - revise contrast src
archmoj Apr 24, 2024
1ce1719
fix shadow attr def
archmoj Apr 24, 2024
811ec9a
update schema
archmoj Apr 24, 2024
614ef31
adjust jasmine tests
archmoj Apr 24, 2024
35bce6c
test axes_custom-ticks_log-date
archmoj Apr 24, 2024
c6549e2
test axes_labels
archmoj Apr 24, 2024
86dff1b
test bar_annotation_max_range_eq_category
archmoj Apr 24, 2024
3a04219
test bar_attrs_group_norm
archmoj Apr 24, 2024
aceb679
test bar-colorscale-colorbar
archmoj Apr 24, 2024
ab55061
test bar_gantt-chart
archmoj Apr 24, 2024
6e6f04f
test benchmarks
archmoj Apr 24, 2024
1f8ed20
test canada_geo_projections
archmoj Apr 24, 2024
e8b91ae
test cliponaxis_false-dates-log
archmoj Apr 24, 2024
e9b2efd
test container-legend
archmoj Apr 24, 2024
698b010
test contour_constraints_equal_boundary_minmax
archmoj Apr 24, 2024
faae542
test font-variant-bar
archmoj Apr 24, 2024
5bd8a2d
test funnel_axis_textangle_outside
archmoj Apr 24, 2024
628b0aa
test funnel_horizontal_group_basic
archmoj Apr 24, 2024
a9479f5
test geo_africa-insets
archmoj Apr 24, 2024
625c3c1
test geo_canadian-cities
archmoj Apr 24, 2024
20cace9
test geo_country-names-text-chart
archmoj Apr 24, 2024
034f446
test geo_text_chart_arrays
archmoj Apr 24, 2024
1f044fe
test geo_text_chart_arrays custom title shadow
archmoj Apr 24, 2024
0a7e182
test heatmap_brick_padding
archmoj Apr 24, 2024
974a30e
test heatmap-text-color-on-missing-data
archmoj Apr 24, 2024
cff115c
test indicator_attrs
archmoj Apr 24, 2024
5b2e77a
test indicator_scatter
archmoj Apr 24, 2024
5709630
test insiderange
archmoj Apr 24, 2024
0044abf
test pie_legend_line_color_array
archmoj Apr 24, 2024
1fc4a5a
test polar_bar-overlay
archmoj Apr 24, 2024
70fec4b
test sankey_messy
archmoj Apr 24, 2024
67bbbb1
test text_on_shapes_basic
archmoj Apr 24, 2024
65586cd
test texttemplate
archmoj Apr 24, 2024
b8b8d0a
test texttemplate_scatter
archmoj Apr 24, 2024
647895a
test ticklabelposition-5
archmoj Apr 24, 2024
d39fe8b
test treemap_multi-line_headers
archmoj Apr 24, 2024
58cfeb3
Merge remote-tracking branch 'origin/master' into font-shadow-stridin…
archmoj Apr 25, 2024
150f0c8
introduce overrideDflt option for coerceFont
archmoj May 3, 2024
e79ce86
refactor src/components/fx/hover.js
archmoj May 3, 2024
d7c6f8f
fix colorbar.title.font default
archmoj May 3, 2024
55f27fc
capitalize none > normal
archmoj May 3, 2024
0c91191
lowercase > lower, uppercase > upper
archmoj May 3, 2024
b0977dd
replace case options with object
archmoj May 3, 2024
581b2fc
striding > decorline
archmoj May 3, 2024
c8fe2fc
Merge remote-tracking branch 'origin/master' into font-shadow-stridin…
archmoj May 8, 2024
a3ad2e8
rename keys
archmoj May 9, 2024
47ebc09
correct schema
archmoj May 10, 2024
042b2ad
capitalize > textcase
archmoj May 10, 2024
4bbe9a2
textcase: word > headline
archmoj May 10, 2024
70f3370
textcase: headline > caps - Capitalizing Every Word Is NOT headline case
archmoj May 10, 2024
6f20c25
use new options in trace_metatext mock
archmoj May 10, 2024
84bc75c
test new options
archmoj May 10, 2024
cff60fb
use Drawing.font to style titles
archmoj May 13, 2024
3ff255f
caps > word caps
archmoj May 14, 2024
a344464
decorline > lineposition
archmoj May 14, 2024
9416441
clear none text font styles instead of setting to none
archmoj May 15, 2024
e60123f
Merge remote-tracking branch 'origin/master' into font-shadow-stridin…
archmoj May 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/components/annotations/draw.js
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,10 @@ function drawRaw(gd, options, index, subplotId, xa, ya) {
fontColor: hoverFont.color,
fontWeight: hoverFont.weight,
fontStyle: hoverFont.style,
fontVariant: hoverFont.variant
fontVariant: hoverFont.variant,
fontShadow: hoverFont.fontShadow,
fontLineposition: hoverFont.fontLineposition,
fontTextcase: hoverFont.fontTextcase,
}, {
container: fullLayout._hoverlayer.node(),
outerContainer: fullLayout._paper.node(),
Expand Down
8 changes: 3 additions & 5 deletions src/components/colorbar/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,11 +124,9 @@ module.exports = function colorbarDefaults(containerIn, containerOut, layout) {
coerce('title.text', layout._dfltTitle.colorbar);

var tickFont = colorbarOut.showticklabels ? colorbarOut.tickfont : font;
var dfltTitleFont = Lib.extendFlat({}, tickFont, {
weight: font.weight,
style: font.style,
variant: font.variant,
color: font.color,

var dfltTitleFont = Lib.extendFlat({}, font, {
family: tickFont.family,
archmoj marked this conversation as resolved.
Show resolved Hide resolved
size: Lib.bigFont(tickFont.size)
});
Lib.coerceFont(coerce, 'title.font', dfltTitleFont);
Expand Down
36 changes: 36 additions & 0 deletions src/components/drawing/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ drawing.font = function(s, font) {
var color = font.color;
var size = font.size;
var family = font.family;
var shadow = font.shadow;
var lineposition = font.lineposition;
var textcase = font.textcase;

if(family) s.style('font-family', family);
if(size + 1) s.style('font-size', size + 'px');
Expand All @@ -42,8 +45,38 @@ drawing.font = function(s, font) {
if(weight) s.style('font-weight', weight);
if(style) s.style('font-style', style);
if(variant) s.style('font-variant', variant);

if(textcase) s.style('text-transform', dropNone(textcase2transform(textcase)));
if(shadow) s.style('text-shadow', shadow === 'auto' ? svgTextUtils.makeTextShadow(Color.contrast(color)) : dropNone(shadow));
if(lineposition) s.style('text-decoration-line', dropNone(lineposition2decorationLine(lineposition)));
};

function dropNone(a) {
return a === 'none' ? undefined : a;
}

var textcase2transformOptions = {
normal: 'none',
lower: 'lowercase',
upper: 'uppercase',
'word caps': 'capitalize'
};

function textcase2transform(textcase) {
return textcase2transformOptions[textcase];
}

function lineposition2decorationLine(lineposition) {
return (
lineposition
.replace('under', 'underline')
.replace('over', 'overline')
.replace('through', 'line-through')
.split('+')
.join(' ')
);
}

/*
* Positioning helpers
* Note: do not use `setPosition` with <text> nodes modified by
Expand Down Expand Up @@ -1136,6 +1169,9 @@ drawing.textPointStyle = function(s, trace, gd) {
weight: d.tw || trace.textfont.weight,
style: d.ty || trace.textfont.style,
variant: d.tv || trace.textfont.variant,
textcase: d.tC || trace.textfont.textcase,
lineposition: d.tE || trace.textfont.lineposition,
shadow: d.tS || trace.textfont.shadow,
size: fontSize,
color: fontColor
})
Expand Down
32 changes: 24 additions & 8 deletions src/components/fx/hover.js
Original file line number Diff line number Diff line change
Expand Up @@ -961,6 +961,9 @@ function createHoverText(hoverData, opts) {
var fontWeight = opts.fontWeight || fullLayout.font.weight;
var fontStyle = opts.fontStyle || fullLayout.font.style;
var fontVariant = opts.fontVariant || fullLayout.font.variant;
var fontTextcase = opts.fontTextcase || fullLayout.font.textcase;
var fontLineposition = opts.fontLineposition || fullLayout.font.lineposition;
var fontShadow = opts.fontShadow || fullLayout.font.shadow;

var c0 = hoverData[0];
var xa = c0.xa;
Expand Down Expand Up @@ -1041,13 +1044,17 @@ function createHoverText(hoverData, opts) {
var commonBgColor = commonLabelOpts.bgcolor || Color.defaultLine;
var commonStroke = commonLabelOpts.bordercolor || Color.contrast(commonBgColor);
var contrastColor = Color.contrast(commonBgColor);
var commonLabelOptsFont = commonLabelOpts.font;
var commonLabelFont = {
weight: commonLabelOpts.font.weight || fontWeight,
style: commonLabelOpts.font.style || fontStyle,
variant: commonLabelOpts.font.variant || fontVariant,
family: commonLabelOpts.font.family || fontFamily,
size: commonLabelOpts.font.size || fontSize,
color: commonLabelOpts.font.color || contrastColor
weight: commonLabelOptsFont.weight || fontWeight,
style: commonLabelOptsFont.style || fontStyle,
variant: commonLabelOptsFont.variant || fontVariant,
textcase: commonLabelOptsFont.textcase || fontTextcase,
lineposition: commonLabelOptsFont.lineposition || fontLineposition,
shadow: commonLabelOptsFont.shadow || fontShadow,
family: commonLabelOptsFont.family || fontFamily,
size: commonLabelOptsFont.size || fontSize,
color: commonLabelOptsFont.color || contrastColor
};

lpath.style({
Expand Down Expand Up @@ -1370,6 +1377,9 @@ function createHoverText(hoverData, opts) {
weight: fontWeight,
style: fontStyle,
variant: fontVariant,
textcase: fontTextcase,
lineposition: fontLineposition,
shadow: fontShadow,
family: fontFamily,
size: fontSize
});
Expand Down Expand Up @@ -1413,7 +1423,10 @@ function createHoverText(hoverData, opts) {
color: d.fontColor || contrastColor,
weight: d.fontWeight || fontWeight,
style: d.fontStyle || fontStyle,
variant: d.fontVariant || fontVariant
variant: d.fontVariant || fontVariant,
textcase: d.fontTextcase || fontTextcase,
lineposition: d.fontLineposition || fontLineposition,
shadow: d.fontShadow || fontShadow,
})
.text(text)
.attr('data-notex', 1)
Expand All @@ -1432,7 +1445,10 @@ function createHoverText(hoverData, opts) {
color: nameColor,
weight: d.fontWeight || fontWeight,
style: d.fontStyle || fontStyle,
variant: d.fontVariant || fontVariant
variant: d.fontVariant || fontVariant,
textcase: d.fontTextcase || fontTextcase,
lineposition: d.fontLineposition || fontLineposition,
shadow: d.fontShadow || fontShadow,
}).text(name)
.attr('data-notex', 1)
.call(svgTextUtils.positionText, 0, 0)
Expand Down
4 changes: 2 additions & 2 deletions src/components/legend/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ function groupDefaults(legendId, layoutIn, layoutOut, fullData) {
};

var globalFont = layoutOut.font || {};
var grouptitlefont = Lib.coerceFont(coerce, 'grouptitlefont', Lib.extendFlat({}, globalFont, {
var grouptitlefont = Lib.coerceFont(coerce, 'grouptitlefont', globalFont, { overrideDflt: {
size: Math.round(globalFont.size * 1.1)
archmoj marked this conversation as resolved.
Show resolved Hide resolved
}));
}});

var legendTraceCount = 0;
var legendReallyHasATrace = false;
Expand Down
3 changes: 3 additions & 0 deletions src/components/legend/style.js
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,9 @@ module.exports = function style(s, gd, legend) {
dEdit.tw = boundVal('textfont.weight', pickFirst);
dEdit.ty = boundVal('textfont.style', pickFirst);
dEdit.tv = boundVal('textfont.variant', pickFirst);
dEdit.tC = boundVal('textfont.textcase', pickFirst);
dEdit.tE = boundVal('textfont.lineposition', pickFirst);
dEdit.tS = boundVal('textfont.shadow', pickFirst);
}

dMod = [Lib.minExtend(d0, dEdit)];
Expand Down
22 changes: 14 additions & 8 deletions src/components/titles/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ function draw(gd, titleClass, options) {
var fontWeight = font.weight;
var fontStyle = font.style;
var fontVariant = font.variant;
var fontTextcase = font.textcase;
var fontLineposition = font.lineposition;
var fontShadow = font.shadow;

// only make this title editable if we positively identify its property
// as one that has editing enabled.
Expand Down Expand Up @@ -144,14 +147,17 @@ function draw(gd, titleClass, options) {

titleEl.attr('transform', transformVal);

titleEl.style({
'font-family': fontFamily,
'font-size': d3.round(fontSize, 2) + 'px',
fill: Color.rgb(fontColor),
opacity: opacity * Color.opacity(fontColor),
'font-weight': fontWeight,
'font-style': fontStyle,
'font-variant': fontVariant
titleEl.style('opacity', opacity * Color.opacity(fontColor))
.call(Drawing.font, {
color: Color.rgb(fontColor),
size: d3.round(fontSize, 2),
family: fontFamily,
weight: fontWeight,
style: fontStyle,
variant: fontVariant,
textcase: fontTextcase,
shadow: fontShadow,
lineposition: fontLineposition,
})
.attr(attributes)
.call(svgTextUtils.convertToTspans, gd);
Expand Down
29 changes: 20 additions & 9 deletions src/lib/coerce.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
var isNumeric = require('fast-isnumeric');
var tinycolor = require('tinycolor2');

var extendFlat = require('./extend').extendFlat;

var baseTraceAttrs = require('../plots/attributes');
var colorscales = require('../components/colorscale/scales');
var Color = require('../components/color');
Expand Down Expand Up @@ -470,18 +472,27 @@ exports.coerce2 = function(containerIn, containerOut, attributes, attribute, dfl
*/
exports.coerceFont = function(coerce, attr, dfltObj, opts) {
if(!opts) opts = {};
dfltObj = extendFlat({}, dfltObj);
dfltObj = extendFlat(dfltObj, opts.overrideDflt || {});

var out = {};

dfltObj = dfltObj || {};

out.family = coerce(attr + '.family', dfltObj.family);
out.size = coerce(attr + '.size', dfltObj.size);
out.color = coerce(attr + '.color', dfltObj.color);
var out = {
family: coerce(attr + '.family', dfltObj.family),
size: coerce(attr + '.size', dfltObj.size),
color: coerce(attr + '.color', dfltObj.color),
weight: coerce(attr + '.weight', dfltObj.weight),
style: coerce(attr + '.style', dfltObj.style),
};

out.weight = coerce(attr + '.weight', dfltObj.weight);
out.style = coerce(attr + '.style', dfltObj.style);
if(!opts.noFontVariant) out.variant = coerce(attr + '.variant', dfltObj.variant);
if(!opts.noFontLineposition) out.lineposition = coerce(attr + '.lineposition', dfltObj.lineposition);
if(!opts.noFontTextcase) out.textcase = coerce(attr + '.textcase', dfltObj.textcase);
if(!opts.noFontShadow) {
var dfltShadow = dfltObj.shadow;
if(dfltShadow === 'none' && opts.autoShadowDflt) {
dfltShadow = 'auto';
}
out.shadow = coerce(attr + '.shadow', dfltShadow);
}

return out;
};
Expand Down
8 changes: 7 additions & 1 deletion src/plots/cartesian/axes.js
Original file line number Diff line number Diff line change
Expand Up @@ -1740,6 +1740,9 @@ function tickTextObj(ax, x, text) {
fontWeight: tf.weight,
fontStyle: tf.style,
fontVariant: tf.variant,
fontTextcase: tf.textcase,
fontLineposition: tf.lineposition,
fontShadow: tf.shadow,
fontColor: tf.color
};
}
Expand Down Expand Up @@ -3507,7 +3510,10 @@ axes.drawLabels = function(gd, ax, opts) {
color: d.fontColor,
weight: d.fontWeight,
style: d.fontStyle,
variant: d.fontVariant
variant: d.fontVariant,
textcase: d.fontTextcase,
lineposition: d.fontLineposition,
shadow: d.fontShadow,
})
.text(d.text)
.call(svgTextUtils.convertToTspans, gd);
Expand Down
8 changes: 2 additions & 6 deletions src/plots/cartesian/axis_defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,14 +111,10 @@ module.exports = function handleAxisDefaults(containerIn, containerOut, coerce,
if(!visible) return containerOut;

coerce('title.text', dfltTitle);
Lib.coerceFont(coerce, 'title.font', {
family: font.family,
weight: font.weight,
style: font.style,
variant: font.variant,
Lib.coerceFont(coerce, 'title.font', font, { overrideDflt: {
size: Lib.bigFont(font.size),
color: dfltFontColor
});
}});

// major ticks
handleTickValueDefaults(containerIn, containerOut, coerce, axType);
Expand Down
9 changes: 2 additions & 7 deletions src/plots/cartesian/tick_label_defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,9 @@ module.exports = function handleTickLabelDefaults(containerIn, containerOut, coe
(contColor && contColor !== layoutAttributes.color.dflt) ?
contColor : font.color;

Lib.coerceFont(coerce, 'tickfont', {
family: font.family,
weight: font.weight,
style: font.style,
variant: font.variant,
size: font.size,
Lib.coerceFont(coerce, 'tickfont', font, { overrideDflt: {
color: dfltFontColor
});
}});

if(
!options.noTicklabelstep &&
Expand Down
49 changes: 48 additions & 1 deletion src/plots/font_attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,42 @@ module.exports = function(opts) {
].join(' ')
},

textcase: opts.noFontTextcase ? undefined : {
editType: editType,
valType: 'enumerated',
values: ['normal', 'word caps', 'upper', 'lower'],
archmoj marked this conversation as resolved.
Show resolved Hide resolved
dflt: 'normal',
description: [
'Sets capitalization of text.',
'It can be used to make text appear in all-uppercase or all-lowercase,',
'or with each word capitalized.'
].join(' ')
},

lineposition: opts.noFontLineposition ? undefined : {
editType: editType,
valType: 'flaglist',
flags: ['under', 'over', 'through'],
extras: ['none'],
dflt: 'none',
description: [
'Sets the kind of decoration line(s) with text,',
'such as an *under*, *over* or *through*',
'as well as combinations e.g. *under+over*, etc.'
].join(' ')
},

shadow: opts.noFontShadow ? undefined : {
editType: editType,
valType: 'string',
dflt: opts.autoShadowDflt ? 'auto' : 'none',
description: [
'Sets the shape and color of the shadow behind text.',
'*auto* places minimal shadow and applies contrast text font color.',
'See https://developer.mozilla.org/en-US/docs/Web/CSS/text-shadow for additional options.'
archmoj marked this conversation as resolved.
Show resolved Hide resolved
].join(' ')
},

editType: editType,
// blank strings so compress_attributes can remove
// TODO - that's uber hacky... better solution?
Expand All @@ -112,7 +148,18 @@ module.exports = function(opts) {
attrs.family.arrayOk = true;
attrs.weight.arrayOk = true;
attrs.style.arrayOk = true;
attrs.variant.arrayOk = true;
if(!opts.noFontVariant) {
attrs.variant.arrayOk = true;
}
if(!opts.noFontTextcase) {
attrs.textcase.arrayOk = true;
}
if(!opts.noFontLineposition) {
attrs.lineposition.arrayOk = true;
}
if(!opts.noFontShadow) {
attrs.shadow.arrayOk = true;
}
attrs.size.arrayOk = true;
attrs.color.arrayOk = true;
}
Expand Down
3 changes: 3 additions & 0 deletions src/plots/mapbox/layout_attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ var constants = require('./constants');

var fontAttr = fontAttrs({
noFontVariant: true,
noFontShadow: true,
noFontLineposition: true,
noFontTextcase: true,
description: [
'Sets the icon text font (color=mapbox.layer.paint.text-color, size=mapbox.layer.layout.text-size).',
'Has an effect only when `type` is set to *symbol*.'
Expand Down
5 changes: 4 additions & 1 deletion src/plots/mapbox/layout_defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,10 @@ function handleLayerDefaults(layerIn, layerOut) {

coerce('symbol.text');
Lib.coerceFont(coerce, 'symbol.textfont', undefined, {
noFontVariant: true
noFontVariant: true,
noFontShadow: true,
noFontLineposition: true,
noFontTextcase: true,
});
coerce('symbol.textposition');
coerce('symbol.placement');
Expand Down
Loading