<commit_before>'use strict';

var FontFace = require('./FontFace');
var FontUtils = require('./FontUtils');

var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');

var Hypher = require('hypher'); // FIXME: Lazy load
var english = require('hyphenation.en-us'); // FIXME: l10n
var h = new Hypher(english);

var _fragmentWidthMemos = {};
var measureFragmentWidth = function (fragment, fontFace, fontSize) {
  var memoKey = fragment + fontFace.id + fontSize;
  var memoized = _fragmentWidthMemos[memoKey];
  if (memoized) { return memoized; }

  ctx.font = fontFace.attributes.style + ' normal ' +
             fontFace.attributes.weight + ' ' + fontSize + 'pt ' +
             fontFace.family;
  var fragmentWidth = ctx.measureText(fragment).width;

  _fragmentWidthMemos[memoKey] = fragmentWidth;
  return fragmentWidth;
};

var _hyphenationOpportunityMemos = {};
var getHyphenationOpportunities = function(text, hyphens) {
  var memoKey = hyphens + text;
  var memoized = _hyphenationOpportunityMemos[memoKey];
  if (memoized) { return memoized; }

  var words = text.split(/\s+/);
  if (hyphens === 'auto') {
    words = words.map(function(word) {
      return h.hyphenate(word);
    });
  } else {
    words = words.map(function(word) {
      return [word];
    });
  }

  _hyphenationOpportunityMemos[memoKey] = words;
  return words;
};

var _paragraphMetricsMemos = {};
var getParagraphMetrics = function(text, hyphens, fontFace, fontSize) {
  var memoKey = text + hyphens + fontFace.id + fontSize;
  var memoized = _paragraphMetricsMemos[memoKey];
  if (memoized) { return memoized; }

  var metrics = {};
  metrics.fragments = getHyphenationOpportunities(text, hyphens);
  metrics.fragmentWidths = metrics.fragments.map(function(word) {
    return word.map (function(morpheme) {
      return measureFragmentWidth(morpheme, fontFace, fontSize);
    });
  });
  metrics.spaceWidth = measureFragmentWidth(' ', fontFace, fontSize);
  metrics.hyphenWidth = measureFragmentWidth('-', fontFace, fontSize);
  
  _paragraphMetricsMemos[memoKey] = metrics;
  return metrics;
};

var firstFit = function(maxWidth, metrics) {
  function Word() {
    this.text = '';
    this.width = 0;
  }

  function Line() {
    this.whiteSpace = 0;
    this.width = 0;
    this.words = [ new Word() ];
  }

  var lines = [ new Line() ];

  Line.prototype.appendMorpheme = function(morpheme, advanceWidth) {
    var word = this.words[this.words.length - 1];
    word.text += morpheme;
    word.width += advanceWidth;
    this.width += advanceWidth;
  };

  Line.prototype.appendHyphen = function() {
    this.appendMorpheme('-', metrics.hyphenWidth);
  };

  Line.prototype.appendSpace = function() {
    this.whiteSpace += metrics.spaceWidth;
  };

  Line.prototype.newWord = function() {
    this.appendSpace();
    this.words.push( new Word() );
  };

  function push(morpheme, advanceWidth, initial, final) {
    var line = lines[lines.length - 1];
    // setting the first morpheme of a line always succeeds
    if (line.width === 0) {
      // good to go!
    // do we need to break the line?
    } else if
        // handle a middle syllable
        ((!initial && !final &&
          line.width + line.whiteSpace + advanceWidth + metrics.hyphenWidth > maxWidth) ||
        // handle an initial syllable
         (initial &&
          line.width + line.whiteSpace + metrics.spaceWidth + advanceWidth > maxWidth) ||
        // handle a final syllable.
         (final &&
          line.width + line.whiteSpace + advanceWidth > maxWidth)) {
      if (!initial) { line.appendHyphen(); }
      line = new Line();
      lines.push(line);
    } else if (initial) {
      line.newWord();
    }

    line.appendMorpheme(morpheme, advanceWidth);
  }

  metrics.fragments.forEach(function(word, wordIdx, words) {
    word.forEach(function(morpheme, morphemeIdx) {
      var advanceWidth = metrics.fragmentWidths[wordIdx][morphemeIdx];
      var initial = (morphemeIdx === 0);
      var final = (morphemeIdx === word.length - 1);
      push(morpheme, advanceWidth, initial, final);
    });
  });

  return lines;
};

// var _lineBreakMemos = {};
module.exports = function measureText(width, text, hyphens, fontFace, fontSize, lineHeight, breakingStrategy) {
  // Bail and return zero unless we're sure the font is ready.
  if (!FontUtils.isFontLoaded(fontFace)) {<commit_msg>single words are both initial and final morphemes<commit_after>'use strict';

var FontFace = require('./FontFace');
var FontUtils = require('./FontUtils');

var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');

var Hypher = require('hypher'); // FIXME: Lazy load
var english = require('hyphenation.en-us'); // FIXME: l10n
var h = new Hypher(english);

var _fragmentWidthMemos = {};
var measureFragmentWidth = function (fragment, fontFace, fontSize) {
  var memoKey = fragment + fontFace.id + fontSize;
  var memoized = _fragmentWidthMemos[memoKey];
  if (memoized) { return memoized; }

  ctx.font = fontFace.attributes.style + ' normal ' +
             fontFace.attributes.weight + ' ' + fontSize + 'pt ' +
             fontFace.family;
  var fragmentWidth = ctx.measureText(fragment).width;

  _fragmentWidthMemos[memoKey] = fragmentWidth;
  return fragmentWidth;
};

var _hyphenationOpportunityMemos = {};
var getHyphenationOpportunities = function(text, hyphens) {
  var memoKey = hyphens + text;
  var memoized = _hyphenationOpportunityMemos[memoKey];
  if (memoized) { return memoized; }

  var words = text.split(/\s+/);
  if (hyphens === 'auto') {
    words = words.map(function(word) {
      return h.hyphenate(word);
    });
  } else {
    words = words.map(function(word) {
      return [word];
    });
  }

  _hyphenationOpportunityMemos[memoKey] = words;
  return words;
};

var _paragraphMetricsMemos = {};
var getParagraphMetrics = function(text, hyphens, fontFace, fontSize) {
  var memoKey = text + hyphens + fontFace.id + fontSize;
  var memoized = _paragraphMetricsMemos[memoKey];
  if (memoized) { return memoized; }

  var metrics = {};
  metrics.fragments = getHyphenationOpportunities(text, hyphens);
  metrics.fragmentWidths = metrics.fragments.map(function(word) {
    return word.map (function(morpheme) {
      return measureFragmentWidth(morpheme, fontFace, fontSize);
    });
  });
  metrics.spaceWidth = measureFragmentWidth(' ', fontFace, fontSize);
  metrics.hyphenWidth = measureFragmentWidth('-', fontFace, fontSize);
  
  _paragraphMetricsMemos[memoKey] = metrics;
  return metrics;
};

var firstFit = function(maxWidth, metrics) {
  function Word() {
    this.text = '';
    this.width = 0;
  }

  function Line() {
    this.whiteSpace = 0;
    this.width = 0;
    this.words = [ new Word() ];
  }

  var lines = [ new Line() ];

  Line.prototype.appendMorpheme = function(morpheme, advanceWidth) {
    var word = this.words[this.words.length - 1];
    word.text += morpheme;
    word.width += advanceWidth;
    this.width += advanceWidth;
  };

  Line.prototype.appendHyphen = function() {
    this.appendMorpheme('-', metrics.hyphenWidth);
  };

  Line.prototype.appendSpace = function() {
    this.whiteSpace += metrics.spaceWidth;
  };

  Line.prototype.newWord = function() {
    this.appendSpace();
    this.words.push( new Word() );
  };

  function push(morpheme, advanceWidth, initial, final) {
    var line = lines[lines.length - 1];
    // setting the first morpheme of a line always succeeds
    if (line.width === 0) {
      // good to go!
    // do we need to break the line?
    } else if
        // handle a middle syllable
        ((!initial && !final &&
          line.width + line.whiteSpace + advanceWidth + metrics.hyphenWidth > maxWidth) ||
        // handle an initial syllable
         (initial &&
          line.width + line.whiteSpace + metrics.spaceWidth + advanceWidth > maxWidth) ||
        // handle a final syllable.
         (!initial && final &&
          line.width + line.whiteSpace + advanceWidth > maxWidth)) {
      if (!initial) { line.appendHyphen(); }
      line = new Line();
      lines.push(line);
    } else if (initial) {
      line.newWord();
    }

    line.appendMorpheme(morpheme, advanceWidth);
  }

  metrics.fragments.forEach(function(word, wordIdx, words) {
    word.forEach(function(morpheme, morphemeIdx) {
      var advanceWidth = metrics.fragmentWidths[wordIdx][morphemeIdx];
      var initial = (morphemeIdx === 0);
      var final = (morphemeIdx === word.length - 1);
      push(morpheme, advanceWidth, initial, final);
    });
  });

  return lines;
};

// var _lineBreakMemos = {};
module.exports = function measureText(width, text, hyphens, fontFace, fontSize, lineHeight, breakingStrategy) {
  // Bail and return zero unless we're sure the font is ready.
  if (!FontUtils.isFontLoaded(fontFace)) {
{'fraction_non_alphanumeric': 0.13278055429350297, 'fraction_numerical': 0.002498864152657883, 'mean_word_length': 3.341715976331361, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2596340', 'n_tokens_mistral': 2851, 'n_tokens_neox': 2724, 'n_words': 644}