Как выделить весь подчеркнутый текст в абзаце - PullRequest
1 голос
/ 10 июля 2020

Я пытаюсь создать скрипт приложений Google, который будет форматировать определенные части абзаца. Например, подчеркнутый текст будет также выделен жирным шрифтом / курсивом.

Одна надстройка документации, которую я пробовал, имеет аналогичную функцию: https://imgur.com/a/5Cw6Irn (это именно то, что я ' м пытаюсь достичь)

Как мне написать функцию, которая будет выбирать определенный тип текста и форматировать его?

** Мне удалось написать сценарий, который перебирает каждую букву в абзаце и проверяет, подчеркнута ли она, но по мере удлинения абзаца это становится очень медленным, поэтому я ищу более быстрое решение.

function textUnderline() {
  var selectedText = DocumentApp.getActiveDocument().getSelection();  
  if(selectedText) {
    var elements = selectedText.getRangeElements();
    for (var index = 0; index < elements.length; index++) {
      var element = elements[index];
      if(element.getElement().editAsText) {        
        var text = element.getElement().editAsText();
        var textLength = text.getText().length;        
        //For every single character, check if it's underlined and then format it
        for (var i = 0; i < textLength; i++) {        
          if(text.isUnderline(i)) {          
            text.setBold(i, i, true);
            text.setBackgroundColor(i,i,'#ffff00');               
          } else {            
            text.setFontSize(i, i, 8);             
          } 
        }   
      } 
    }
  }
}

Ответы [ 2 ]

2 голосов
/ 13 июля 2020

Используйте getTextAttributeIndices:

Нет необходимости проверять каждый символ в выделении. Вы можете использовать getTextAttributeIndices () , чтобы получить индексы, в которых изменяется форматирование текста. Этот метод:

Извлекает набор текстовых индексов, которые соответствуют началу отдельных прогонов форматирования текста.

Вам просто нужно выполнить итерацию по этим индексам (то есть проверьте индексы, в которых изменяется форматирование текста), которые составляют небольшую часть всех индексов символов. Это значительно повысит эффективность.

Пример кода:

function textUnderline() {
  var selectedText = DocumentApp.getActiveDocument().getSelection();  
  if(selectedText) {
    var elements = selectedText.getRangeElements();
    for (var index = 0; index < elements.length; index++) {
      var element = elements[index];
      if(element.getElement().editAsText) {        
        var text = element.getElement().editAsText();
        var textRunIndices = text.getTextAttributeIndices();
        var textLength = text.getText().length;
        for (let i = 0; i < textRunIndices.length; i++) {
          const startOffset = textRunIndices[i];
          const endOffset = i + 1 < textRunIndices.length ? textRunIndices[i + 1] - 1 : textLength - 1;
          if (text.isUnderline(textRunIndices[i])) {
            text.setBold(startOffset, endOffset, true);
            text.setBackgroundColor(startOffset, endOffset,'#ffff00');  
          } else {
            text.setFontSize(startOffset, endOffset, 8);
          }
        }
      } 
    }
  }
}

Ссылка:

1 голос
/ 11 июля 2020

На основе примера, показанного в анимированном gif, кажется, что ваша процедура должна

  • обрабатывать выделение
  • задавать свойства, если выбранная область имеет некоторый формат (например, подчеркнутый )
  • установить свойства, если выбранная область НЕ имеет какой-либо формат (например, не подчеркнута)
  • fini sh как можно быстрее

и ваш пример кода достигает всех этих целей, кроме последней.

Проблема в том, что вы вызываете функции text.set...() в каждой позиции индекса. Каждый вызов является синхронным и блокирует код до тех пор, пока документ не будет обновлен, поэтому время выполнения увеличивается линейно с каждым символом в выделенном фрагменте.

Я предлагаю создать коллекцию поддиапазонов из диапазона выбора, а затем для в каждом поддиапазоне используется text.set...(subrange.start, subrange.end) для применения форматирования. Теперь время выполнения будет зависеть от фрагментов символов, а не от отдельных символов. то есть, вы обновляете только тогда, когда форматирование переключается с подчеркнутого на неподчеркнутый в вашем примере.

Вот пример кода, который реализует эту идею поддиапазона. Я разделил определенную c функцию предиката (text.isUnderline) и определенные c эффекты форматирования на их собственные функции, чтобы отделить общую идею от конкретной реализации c.

// run this function with selection
function transformUnderlinedToBoldAndYellow() {
  transformSelection("isUnderline", boldYellowOrSmall);
}

function transformSelection(stylePredicateKey, stylingFunction) {
  const selectedText = DocumentApp.getActiveDocument().getSelection();
  if (!selectedText) return;
  const getStyledSubRanges = makeStyledSubRangeReducer(stylePredicateKey);
  selectedText.getRangeElements()
    .reduce(getStyledSubRanges, [])
    .forEach(stylingFunction);
}

function makeStyledSubRangeReducer(stylePredicateKey) {
  return function(ranges, rangeElement) {
    const {text, start, end} = unwrapRangeElement(rangeElement);
    if (start >= end) return ranges; // filter out empty selections
    const range = {
      text, start, end,
      styled: [], notStyled: [] // we will extend our range with subranges
    };
    const getKey = (isStyled) => isStyled ? "styled" : "notStyled";
    let currentKey = getKey(text[stylePredicateKey](start));
    range[currentKey].unshift({start: start});
    for (let index = start + 1; index <= end; ++index) {
      const isStyled = text[stylePredicateKey](index);
      if (getKey(isStyled) !== currentKey) { // we are switching styles
        range[currentKey][0].end = index - 1; // note end of this style
        currentKey = getKey(isStyled);
        range[currentKey].unshift({start: index}); // start new style range
      }
    }
    ranges.push(range);
    return ranges;
  }
}

// a helper function to unwrap a range selection, deals with isPartial,
// maps RangeElement => {text, start, end}
function unwrapRangeElement(rangeElement) {
  const isPartial = rangeElement.isPartial();
  const text = rangeElement.getElement().asText();
  return {
    text: text,
    start: isPartial
      ? rangeElement.getStartOffset()
      : 0,
    end: isPartial
      ? rangeElement.getEndOffsetInclusive()
      : text.getText().length - 1
  };
}

// apply specific formatting to satisfy the example
function boldYellowOrSmall(range) {
  const {text, start, end, styled, notStyled} = range;
  styled.forEach(function setTextBoldAndYellow(range) {
    text.setBold(range.start, range.end || end, true);
    text.setBackgroundColor(range.start, range.end || end, '#ffff00');
  });
   notStyled.forEach(function setTextSmall(range) {
    text.setFontSize(range.start, range.end || end, 8);
  });
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...