Skip to content

Commit

Permalink
Merge pull request #6983 from plotly/font-shadow-striding-capitalize
Browse files Browse the repository at this point in the history
Add `shadow`, `lineposition` and `textcase` options to SVG fonts
  • Loading branch information
archmoj authored May 16, 2024
2 parents cfccbcc + e60123f commit c7b1003
Show file tree
Hide file tree
Showing 130 changed files with 12,464 additions and 174 deletions.
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,
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)
}));
}});

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'],
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.'
].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

0 comments on commit c7b1003

Please sign in to comment.