2

I have the following scenario Word:

  • text document with some content controls (also text)
  • content controls are always adjacent to normal text (without a space between the text and the content control)
  • content controls have the same style as the adjacent text when they are created (by editing their font.color, font.name, font.size etc. which is read from the selection in which the content control is being inserted)
  • when content controls are updated (their content is updated), their style is reset and I need to apply the document style again (not an issue, works fine)

The issue is, when content controls are updated, the current document selection can be anywhere but I need to be sure they re-apply the same style as the text adjacent to them.

Is there a way inside Office.js to get the range adjacent to a content control? That way I could read it's style and apply it to the content control.

Cindy Meister
  • 25,071
  • 21
  • 34
  • 43
dnmh
  • 2,045
  • 2
  • 34
  • 49
  • Try this. If it works, I'll make it an answer: Use ContentControl.getRange() to get the range of the control. Then use Range.getNextTextRange (or Range.getNextTextRangeOrNullObject) to get the range that follows the control. Then read the style of the range and reapply it to the control. – Rick Kirkham Aug 23 '18 at 17:14

1 Answers1

2

My assumption is that you want the text immediately preceding the content control. Unfortunately, the object model doesn't provide an equivalent to getNextTextRange for going "backwards" (getPreviousTextRange).

It can be done, but it's a bit round-about. Following is my Script Lab sample code:

async function
    getAdjacentRange() {
    await Word.run(async (context) => {

        var ccs = context.document.contentControls;
        ccs.load("items");
        await context.sync();
        console.log("Nr cc: " + ccs.items.length);
        let cc = ccs.items[0];

        //The starting point of the content control so that 
        //the range can be extended backwards          
        let ccRange = cc.getRange("Start");
        let ccParas = ccRange.paragraphs; cc.load("text,range,paragraphs");
        let ccPara = ccParas.getFirst();
        ccParas.load("items")

    //The content control must be in a paragraph: get that paragraph's
    //starting point so that the range can be extended in that direction
        let paraRange = ccPara.getRange("Start");
        let rng = paraRange.expandTo(ccRange);
   //Get the words in the range of the start of the paragraph to the 
   //start of the content control in order to get the last one before the content control
        let em = [" "];
        let words = rng.getTextRanges(em, true);
        words.load("items");
        await context.sync();
        let nrWords = words.items.length;

      //minus 2 to get second to last word since last word is directly adjacent to content control (no space)
        let lastWord = words.items[nrWords - 2];

        //Now get the content from the end of the second to last word to
        //the start of the content control, which will be the last word
        let word2BeforeCC = lastWord.getRange("End");
        let wordBeforeCC = word2BeforeCC.expandTo(ccRange);
        wordBeforeCC.load("text");
        await context.sync();
        console.log(cc.text + "/ Word before the content control: " + wordBeforeCC.text + " / nr Words: " + nrWords);
})
    }

Code for the case when there is a space between the last word and the content control:

async function
    getAdjacentRange() {
    await Word.run(async (context) => {
        var ccs = context.document.contentControls;
        ccs.load("items");
        await context.sync();

        let cc = ccs.getFirstOrNullObject();

        //The starting point of the content control so that 
        //the range can be extended backwards
        let ccRange = cc.getRange("Start");

        //The content control must be in a paragraph, so get that paragraph's
        //starting point so that the range can be extended in that direction
        let ccParas = ccRange.paragraphs; cc.load("text,range,paragraphs");
        let ccPara = ccParas.getFirst();
        ccParas.load("items")
        let paraRange = ccPara.getRange("Start");
        let rng = paraRange.expandTo(ccRange);

        //Now it's possible to get the last word in the extended range
        let em = [" "];
        console.log(em.length.toString());
        let words = rng.getTextRanges(em, true);
        words.load("items");
        await context.sync();
        let nrWords = words.items.length;
        //returns a Range object, from which you can get the style info
        let lastWord = words.items[nrWords - 1];

        await context.sync();
        console.log(cc.text + "/" + "nr Words: " + nrWords + "last word: " + lastWord.text);
})
    }
Cindy Meister
  • 25,071
  • 21
  • 34
  • 43
  • Yes, I want the text preceding the CC. I am trying out the code and I have 1 issues: `lastWord` prints the last word + the first word of the adj. CC. There is no space between them, I should have written that explicitly. Because of that, I can get the `font`, but `font.color` is empty because there are two different colors in the range. The font itself is in my example the same both in the paragraph and in the CC so if I print `font.name` I do get `Calibri`. Is there a way to get just the last word before the CC without it being concatenated to the content from the CC? – dnmh Aug 24 '18 at 11:24
  • Yes, @dnmh, although that's even more convoluted (and took quite some thinking). I've edited my answer to include that approach, as well. Please edit your question to include the additional information so that my answer makes sense to others who are interested :-) – Cindy Meister Aug 24 '18 at 12:49