Copy link to clipboard
Copied
I'm looking for a script that cuts part or parts of a text and pastes it into new text boxes below. trying to find a script code that I'm doing this, but I've had no success. Can someone help me. The objective is for this "sentence" text to reference the bold text above.
Copy link to clipboard
Copied
Hi @eusoujpg, can you show us what your raw text looks like? For example, does every second line have the secondary "sentences"? Or are there two separate stories and the paragraphs from one match the instances of bold text in the other? I really have no idea how your job is structured, so I couldn't say whether a script would be good for it, I'm afraid.
- Mark
Copy link to clipboard
Copied
The document is part of a material for schools. In the raw material, in DOC, the editor writes a sentence. Below, she will write some texts (quotes) that refer to the bold texts. Example:
The dog chases the cat.
...........singular
Obs: ( . = space )
Below the "dog" text, for example, there will be a dimension called "singular". And so on. Did you understand?
Copy link to clipboard
Copied
Hi @eusoujpg, I've written a script for this challenge!
Please try it out and let me know how it goes. Just select your text, or text frame and run script. If you have trouble getting it to work, please download the attached demo.indd and try script with that first.
- Mark
Here is the result using the same demo, but including styles for labels, tokens and sentences and also I've turned on the `removeLabelParagraphs` option (so my sentence paragraph style has enough space after to accommodate the label). It is a very good idea to use styles in cases like this. (The numbering and shading are just for demonstration of course.)
/**
* @file Connect Labels And Tokens.js
*
* Script will anchor "label" text to "token" text,
* where paragraphs alternate between having
* "tokens" and "labels".
*
* As configured by `settings` object:
* "Labels" are text delimited by runs of 4 or more spaces.
* "Tokens" are text in Bold font style.
* Object style is applied to labels (if available).
*
* @author m1b
* @discussion https://community.adobe.com/t5/indesign-discussions/cut-text-by-frame-and-paste/m-p/14457128
*/
function main() {
// user settings
var settings = {
// name of label object style (optional)
labelObjectStyleName: 'Label',
// name of token character style (optional)
tokenCharacterStyleName: 'Token',
// name of sentence paragraph style (optional)
sentenceParagraphStyleName: 'Sentence',
// tokens are bold text in sentences
tokenFindPreferences: { fontStyle: 'Bold' },
// labels are delimited by runs of 4 or more spaces
labelFindPreferences: { findWhat: '(?<=\\h{4})(\\S.*?)(?=\\h{4}|\\h*\\r)' },
removeLabelParagraphs: true,
};
var doc = app.activeDocument;
if (0 === doc.selection.length)
return alert('Please select a text frame or text and try again.');
// expand the settings object
settings.doc = doc;
settings.text = doc.selection[0];
settings.labelStyle = getThing(doc.allObjectStyles, 'name', settings.labelObjectStyleName);
settings.tokenStyle = getThing(doc.allCharacterStyles, 'name', settings.tokenCharacterStyleName);
settings.sentenceStyle = getThing(doc.allParagraphStyles, 'name', settings.sentenceParagraphStyleName);
// connect the labels
var labels = connectLabelsToTokens(settings);
alert((!labels || 0 === labels.length)
? 'No labels were connected.'
: 'Connected ' + labels.length + ' labels.'
);
};
app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, 'Connect Labels');
/**
* Connects "labels" to "tokens".
*
* Creates anchored text frames containing
* "label" text, positioned centrally beneath
* the "token" text.
*
* The `text` parameter must include paragraphs
* where a paragraph of "labels" is always preceded
* by a paragraph of 'tokens'.
*
* @author m1b
* @version 2024-03-03
* @param {Object} options
* @param {Document} options.doc - an Indesign Document.
* @param {Text|TextFrame|TextColumn} options.text - a DOM object with multiple paragraphs of alternating content.
* @param {Object} options.tokenFindPreferences - an object containing valid findGrepPreferences properties.
* @param {Object} options.labelFindPreferences - an object containing valid findGrepPreferences properties.
* @param {ObjectStyle} [options.labelStyle] - an object style to apply to each label text frame (default: none).
* @param {CharacterStyle} [options.tokenStyle] - a character style to apply to each token found (default: none).
* @returns {Array<{label: token:}>}
*/
function connectLabelsToTokens(options) {
options = options || {};
app.scriptPreferences.measurementUnit = MeasurementUnits.POINTS;
// extra width of label frames, in points
const LABEL_SIDE_MARGIN = 10;
var doc = options.doc,
paragraphs = getWholeParagraphs(options.text),
didAddCarriageReturn = false,
connections = [];
if (
undefined == paragraphs
|| 0 === paragraphs.length
)
return;
if ('\u000D' != paragraphs[paragraphs.length - 1].characters.lastItem().contents) {
// add a carriage return to last paragraph, to make things easier with findGrep
paragraphs[paragraphs.length - 1].characters.lastItem().contents += '\u000D';
// update the reference
paragraphs[paragraphs.length - 1] = paragraphs[paragraphs.length - 1].characters.firstItem().paragraphs[0];
didAddCarriageReturn = true;
}
paragraphLoop:
for (var i = paragraphs.length - 1, para, labels, sentence, tokens; i >= 0; i--) {
para = paragraphs[i];
if (!para.isValid)
continue paragraphLoop;
// find labels
setUpFindGrepPreferences(options.labelFindPreferences)
labels = para.findGrep();
if (labels.length == 0)
// no labels here
continue paragraphLoop;
// get the sentence paragraph for this label paragraph
sentence = labels[0]
.parentStory.characters
.previousItem(labels[0].paragraphs[0].characters[0])
.paragraphs[0];
if (!sentence.isValid)
continue paragraphLoop;
// find tokens in the sentence
setUpFindGrepPreferences(options.tokenFindPreferences)
tokens = sentence.findGrep();
if (tokens.length == 0)
// no tokens here
continue paragraphLoop;
if (options.sentenceStyle)
sentence.applyParagraphStyle(options.sentenceStyle, true);
tokenLoop:
for (var j = tokens.length - 1, token, label, ip, center, frame, halfLabel; j >= 0; j--) {
token = tokens[j];
label = labels[j];
if (!token || !label)
continue tokenLoop;
if (options.tokenStyle)
token.applyCharacterStyle(options.tokenStyle, false);
halfLabel = ((label.endHorizontalOffset - label.horizontalOffset) / 2) + LABEL_SIDE_MARGIN;
center = token.horizontalOffset + (token.endHorizontalOffset - token.horizontalOffset) / 2;
// this is where the label frame will be anchored
ip = token.insertionPoints.lastItem();
frame = doc.textFrames.add({
geometricBounds: [
/* top */ label.baseline - label.ascent,
/* left */ center - halfLabel,
/* bottom */ label.baseline + label.descent,
/* right */ center + halfLabel],
});
// put text in the new frame
labels.pop().duplicate(LocationOptions.AFTER, frame.insertionPoints[0]);
// anchor the frame
frame.anchoredObjectSettings.insertAnchoredObject(ip, AnchorPosition.ANCHORED);
// apply object style, if specified
if (options.labelStyle)
frame.applyObjectStyle(options.labelStyle, true);
// position the frame
frame.anchoredObjectSettings.horizontalReferencePoint = AnchoredRelativeTo.ANCHOR_LOCATION;
frame.anchoredObjectSettings.anchorXoffset = frame.parent.horizontalOffset - center - halfLabel;
connections.push({ label: frame, token: token });
}
if (options.removeLabelParagraphs)
para.remove();
else
para.contents = '\r';
}
if (didAddCarriageReturn)
options.text.parentStory.characters.lastItem().remove();
if (connections.length)
return connections;
};
/**
* Returns a thing with matching property.
* @param {Array|collection} things - the things to look through, eg. PageItems.
* @param {String} key - the property name, eg. 'name'.
* @param {*} value - the value to match.
* @returns {*} - the thing.
*/
function getThing(things, key, value) {
for (var i = 0; i < things.length; i++)
if (things[i][key] == value)
return things[i];
};
/**
* Sets up Indesign's findGrep using `props`.
* @param {Object} props - the properties to apply.
*/
function setUpFindGrepPreferences(props) {
app.findGrepPreferences = NothingEnum.NOTHING;
for (var key in props)
if (app.findGrepPreferences.hasOwnProperty(key))
app.findGrepPreferences[key] = props[key];
};
/**
* Returns array of whole paragraphs
* that each end in carriage return;
* @author m1b
* @version 2024-03-03
* @param {Text} text - Indesign DOM object with paragraphs.
* @returns {Array<Paragraph>}
*/
function getWholeParagraphs(text) {
if (undefined == text)
return;
if (text.hasOwnProperty('text'))
text = text.text;
if (
!text.hasOwnProperty('paragraphs')
|| text.paragraphs.length < 1
)
return;
return text.paragraphs.everyItem().getElements();
};
Edit 2024-03-03: improved getWholeParagraphs and fixed bug where if `text` was the last insertionPoint of a Story. Also added optional character style for "tokens" and better handling of object styling.
Edit 2024-03-03: added optional paragraph style for "sentences", option to remove "labels" paragraphs, and added gif showing use of styles.
Copy link to clipboard
Copied
I really appreciate your dedication for doing this. It will help me a lot with the material I'm doing. Furthermore, I will be able to study a lot of the code you created, bringing great ideas. I'm very happy.
Copy link to clipboard
Copied
Very glad it helped!
Copy link to clipboard
Copied
So you want for texts "sentence #" to stay in the same place - but as separate TextFrames?
Is there a Char / ParaStyle applied to the "sentence #" - because, how script should be able to find what you want to "extract"?
Copy link to clipboard
Copied
Yes, the code posted by M1b has already helped me a lot. Then I will read all the reflections and suggestions given here. It is very rich in information and will be very useful to me. thank you very much
Get ready! An upgraded Adobe Community experience is coming in January.
Learn more