tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

font-editor.js (5129B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 "use strict";
      6 
      7 const {
      8  getStr,
      9 } = require("resource://devtools/client/inspector/fonts/utils/l10n.js");
     10 const {
     11  parseFontVariationAxes,
     12 } = require("resource://devtools/client/inspector/fonts/utils/font-utils.js");
     13 
     14 const {
     15  APPLY_FONT_VARIATION_INSTANCE,
     16  RESET_EDITOR,
     17  SET_FONT_EDITOR_DISABLED,
     18  UPDATE_AXIS_VALUE,
     19  UPDATE_EDITOR_STATE,
     20  UPDATE_PROPERTY_VALUE,
     21  UPDATE_WARNING_MESSAGE,
     22 } = require("resource://devtools/client/inspector/fonts/actions/index.js");
     23 
     24 const CUSTOM_INSTANCE_NAME = getStr("fontinspector.customInstanceName");
     25 
     26 const INITIAL_STATE = {
     27  // Variable font axes.
     28  axes: {},
     29  // Copy of the most recent axes values. Used to revert from a named instance.
     30  customInstanceValues: [],
     31  // When true, prevent users from interacting with inputs in the font editor.
     32  disabled: false,
     33  // Fonts used on the selected element.
     34  fonts: [],
     35  // Current selected font variation instance.
     36  instance: {
     37    name: CUSTOM_INSTANCE_NAME,
     38    values: [],
     39  },
     40  // CSS font properties defined on the selected rule.
     41  properties: {},
     42  // Unique identifier for the selected element.
     43  id: "",
     44  // Warning message with the reason why the font editor cannot be shown.
     45  warning: getStr("fontinspector.noFontsUsedOnCurrentElement"),
     46 };
     47 
     48 const reducers = {
     49  // Update font editor with the axes and values defined by a font variation instance.
     50  [APPLY_FONT_VARIATION_INSTANCE](state, { name, values }) {
     51    const newState = { ...state };
     52    newState.instance.name = name;
     53    newState.instance.values = values;
     54 
     55    if (Array.isArray(values) && values.length) {
     56      newState.axes = values.reduce((acc, value) => {
     57        acc[value.axis] = value.value;
     58        return acc;
     59      }, {});
     60    }
     61 
     62    return newState;
     63  },
     64 
     65  [RESET_EDITOR]() {
     66    return { ...INITIAL_STATE };
     67  },
     68 
     69  [UPDATE_AXIS_VALUE](state, { axis, value }) {
     70    const newState = { ...state };
     71    newState.axes[axis] = value;
     72 
     73    // Cache the latest axes and their values to restore them when switching back from
     74    // a named font variation instance to the custom font variation instance.
     75    newState.customInstanceValues = Object.keys(state.axes).map(axisName => {
     76      return { axis: [axisName], value: state.axes[axisName] };
     77    });
     78 
     79    // As soon as an axis value is manually updated, mark the custom font variation
     80    // instance as selected.
     81    newState.instance.name = CUSTOM_INSTANCE_NAME;
     82 
     83    return newState;
     84  },
     85 
     86  [SET_FONT_EDITOR_DISABLED](state, { disabled }) {
     87    return { ...state, disabled };
     88  },
     89 
     90  [UPDATE_EDITOR_STATE](state, { fonts, properties, id }) {
     91    const axes = parseFontVariationAxes(properties["font-variation-settings"]);
     92 
     93    // If not defined in font-variation-settings, setup "wght" axis with the value of
     94    // "font-weight" if it is numeric and not a keyword.
     95    const weight = properties["font-weight"];
     96    if (
     97      axes.wght === undefined &&
     98      parseFloat(weight).toString() === weight.toString()
     99    ) {
    100      axes.wght = parseFloat(weight);
    101    }
    102 
    103    // If not defined in font-variation-settings, setup "wdth" axis with the percentage
    104    // number from the value of "font-stretch" if it is not a keyword.
    105    const stretch = properties["font-stretch"];
    106    // Match the number part from values like: 10%, 10.55%, 0.2%
    107    // If there's a match, the number is the second item in the match array.
    108    const match = stretch.trim().match(/^(\d+(.\d+)?)%$/);
    109    if (axes.wdth === undefined && match && match[1]) {
    110      axes.wdth = parseFloat(match[1]);
    111    }
    112 
    113    // If not defined in font-variation-settings, setup "slnt" axis with the negative
    114    // of the "font-style: oblique" angle, if any.
    115    const style = properties["font-style"];
    116    const obliqueMatch = style.trim().match(/^oblique(?:\s*(\d+(.\d+)?)deg)?$/);
    117    if (axes.slnt === undefined && obliqueMatch) {
    118      if (obliqueMatch[1]) {
    119        // Negate the angle because CSS and OpenType measure in opposite directions.
    120        axes.slnt = -parseFloat(obliqueMatch[1]);
    121      } else {
    122        // Lack of an <angle> for "font-style: oblique" represents "14deg".
    123        axes.slnt = -14;
    124      }
    125    }
    126 
    127    // If not defined in font-variation-settings, setup "ital" axis with 0 for
    128    // "font-style: normal" or 1 for "font-style: italic".
    129    if (axes.ital === undefined) {
    130      if (style === "normal") {
    131        axes.ital = 0;
    132      } else if (style === "italic") {
    133        axes.ital = 1;
    134      }
    135    }
    136 
    137    return { ...state, axes, fonts, properties, id };
    138  },
    139 
    140  [UPDATE_PROPERTY_VALUE](state, { property, value }) {
    141    const newState = { ...state };
    142    newState.properties[property] = value;
    143    return newState;
    144  },
    145 
    146  [UPDATE_WARNING_MESSAGE](state, { warning }) {
    147    return { ...state, warning };
    148  },
    149 };
    150 
    151 module.exports = function (state = INITIAL_STATE, action) {
    152  const reducer = reducers[action.type];
    153  if (!reducer) {
    154    return state;
    155  }
    156  return reducer(state, action);
    157 };