如何选择段落中所有带下划线的文本



我正在尝试创建一个谷歌应用程序脚本,该脚本将格式化段落的某些部分。例如,带下划线的文本也将变为粗体/斜体。

我尝试过的一个docs插件也有类似的功能:https://i.stack.imgur.com/3WIkp.jpg(这正是我想要实现的(

如何编写一个函数来选择特定类型的文本并设置其格式

**我设法写了一个脚本,遍历段落中的每一个字母,并检查它是否有下划线,但随着段落变长,它变得非常慢,所以我正在寻找一个更快的解决方案。

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);             
} 
}   
} 
}
}
}

使用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);
}
}
} 
}
}
}

参考:

  • getTextAttributeIndices((

根据动画gif中显示的示例,您的过程似乎需要

  • 处理选择
  • 如果所选区域具有某种格式(例如带下划线(,则设置属性
  • 如果所选区域不是某种格式(例如没有下划线(,则设置属性
  • 尽可能快地完成

并且您的示例代码实现了所有这些目标,除了最后一个目标。

问题是在每个索引位置调用text.set...()函数。每次调用都是同步的,并阻塞代码,直到文档更新,因此您的运行时间会随着选择中的每个字符线性增长。

我的建议是从选择范围建立一个子范围集合,然后对每个子范围使用text.set...(subrange.start, subrange.end)来应用格式。现在,运行时将依赖于字符块,而不是单个字符。也就是说,只有当格式从带下划线切换到不带下划线时,您才会进行更新。

下面是一些实现这个子范围思想的示例代码。我将特定的谓词函数(text.isUnderline(和特定的格式化效果分离为它们自己的函数,以便将一般思想与具体实现分离。

// 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);
});
}

最新更新