Plug-In: Paragraph Attributes
A plug-in for setting and restoring the paragraph attributes of the note of the selected project or task.
(NOTE: This plug-in requires OmniFocus 4.x)
(above) Each of the specified paragraph attributes has its own menu of settings and options. By default, the initial setting for each property is: “No Change”
All but the alignment property menus offer these two options:
- No Change • When this option is chosen, no changes will be made to the paragraph attribute.
- Default • When this option is chosen, the default value for the paragraph attribute will be applied to the note text.
NOTE: Immediately following each attribute title, placed within parens, is the current value of that attribute in the note.
IMPORTANT: Attribute changes are applied to all of the note text, not just a selection.
Here is a description of each of the paragraph attributes:
- Alignment (TextAlignment) • The text alignment of the paragraph. Natural text alignment is realized as left or right alignment depending on the line sweep direction of the first script contained in the paragraph.
- First Line Head Indent (Number) • The indentation of the first line of the paragraph. A paragraph’s “head” is the side from which you read. This property contains the distance (in points) from the leading margin of a text container to the beginning of the paragraph’s first line. This value is always nonnegative.
- Head Indent (Number) • The indentation of the paragraph’s lines other than the first. A paragraph’s “head” is the side from which you read. This property contains the distance (in points) from the leading margin of a text container to the beginning of lines other than the first. This value is always nonnegative.
- Tail Indent (Number) • If positive, this value is the distance from the leading margin (for example, the left margin in left-to-right text). If 0 or negative, it’s the distance from the trailing margin.
- Line Height Multiple (Number) • The natural line height of the paragraph is multiplied by this factor (if positive) before being constrained by minimum and maximum line height. The default value of this property is 0.0.
- Line Spacing (Number) • The distance in points between the bottom of one line fragment and the top of the next. This value is always nonnegative.
- Spacing (Number) • The space after the end of the paragraph. This property contains the space (measured in points) added at the end of the paragraph to separate it from the following paragraph. This value is always nonnegative. The space between paragraphs is determined by adding the previous paragraph’s Paragraph Spacing and the current paragraph’s Paragraph Spacing Before.
- Spacing Before (Number) • The distance between the paragraph’s top and the beginning of its text content. This property contains the space (measured in points) between the paragraph’s top and the beginning of its text content. The default value of this property is 0.0.
Return to: OmniFocus Plug-In Collection
Paragraph Attributes
/*{
"type": "action",
"targets": ["omnifocus"],
"author": "Otto Automator",
"identifier": "com.omni-automation.of.paragraph-attributes",
"version": "1.4",
"description": "This plug-in will display and apply the paragraph attribute settings of the note of the selected project or task.",
"label": "Note Style Paragraph Attributes",
"shortLabel": "Paragraph Attributes",
"paletteLabel": "Paragraph Attributes",
"image": "parkingsign.circle.fill"
}*/
(() => {
var action = new PlugIn.Action(async function(selection, sender){
try {
var shouldLog = false
if(shouldLog){console.clear()}
var item = selection.databaseObjects[0]
// CREATE VIRTUAL TEXT OBJECT CONTAING CURRENT NOTE
var noteObj = new Text("", baseStyle)
noteObj.replace(noteObj.range, item.noteText)
var nStyle = noteObj.style
// GET ATTRIBUTE RUN RANGES
var runRanges = noteObj.ranges(TextComponent.AttributeRuns)
// TEXT OBJECTS AND STYLE
var runRanges = noteObj.ranges(TextComponent.AttributeRuns)
// CREATE FORM AND FORM ELEMENTS
var txtAlgnCurVal = nStyle.get(Style.Attribute.ParagraphAlignment)
var txtAlgnDsplyVal
switch(String(txtAlgnCurVal)) {
case "[object TextAlignment: Left]":
txtAlgnDsplyVal = 'Left'
break
case "[object TextAlignment: Right]":
txtAlgnDsplyVal = 'Right'
break
case "[object TextAlignment: Center]":
txtAlgnDsplyVal = 'Center'
break
case "[object TextAlignment: Natural]":
txtAlgnDsplyVal = 'Natural'
break
case "[object TextAlignment: Justified]":
txtAlgnDsplyVal = 'Justified'
break
}
paragraphAlignmentMenu = new Form.Field.Option(
"paragraphAlignment",
`Alignment (${txtAlgnDsplyVal})`,
TextAlignment.all,
null,
null
)
paragraphAlignmentMenu.allowsNull = true
paragraphAlignmentMenu.nullOptionTitle = "No Change"
spcBfrCurVal = nStyle.get(Style.Attribute.ParagraphSpacingBefore)
spcBfrValues = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
spcBfrValueStrings = ["Default", "0", "4", "6", "8", "10", "12", "18", "24", "36"]
spaceBeforeMenu = new Form.Field.Option(
"spaceBefore",
`Space Before (${spcBfrCurVal})`,
spcBfrValues,
spcBfrValueStrings,
null
)
spaceBeforeMenu.allowsNull = true
spaceBeforeMenu.nullOptionTitle = "No Change"
spcAftCurVal = nStyle.get(Style.Attribute.ParagraphSpacing)
spcAftValues = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
spcAftValueStrings = ["Default", "0", "4", "6", "8", "10", "12", "18", "24", "36"]
spaceAfterMenu = new Form.Field.Option(
"spaceAfter",
`Space After (${spcAftCurVal})`,
spcAftValues,
spcAftValueStrings,
null
)
spaceAfterMenu.allowsNull = true
spaceAfterMenu.nullOptionTitle = "No Change"
frstLnCurVal = nStyle.get(Style.Attribute.ParagraphFirstLineHeadIndent)
frstLnValues = [0, 1, 2, 3, 4, 5, 6, 7]
frstLnValueStrings = ["Default", "12", "18", "24", "36", "48", "64", "72"]
firstLineIndentMenu = new Form.Field.Option(
"firstLineIndent",
`1st Line Indent (${frstLnCurVal})`,
frstLnValues,
frstLnValueStrings,
null
)
firstLineIndentMenu.allowsNull = true
firstLineIndentMenu.nullOptionTitle = "No Change"
headCurVal = nStyle.get(Style.Attribute.ParagraphHeadIndent)
headValues = [0, 1, 2, 3, 4, 5, 6, 7]
headValueStrings = ["Default", "0", "12", "24", "36", "48", "64", "72"]
headIndentMenu = new Form.Field.Option(
"headIndent",
`Head Indent (${headCurVal})`,
headValues,
headValueStrings,
null
)
headIndentMenu.allowsNull = true
headIndentMenu.nullOptionTitle = "No Change"
tailCurVal = nStyle.get(Style.Attribute.ParagraphTailIndent)
tailValues = [0, 1, 2, 3, 4, 5, 6, 7]
tailValueStrings = ["Default", "0", "12", "24", "36", "48", "64", "72"]
tailIndentMenu = new Form.Field.Option(
"tailIndent",
`Tail Indent (${tailCurVal})`,
tailValues,
tailValueStrings,
null
)
tailIndentMenu.allowsNull = true
tailIndentMenu.nullOptionTitle = "No Change"
lneHgtFctrCurVal = nStyle.get(Style.Attribute.ParagraphLineHeightMultiple)
//console.log(lneHgtFctrCurVal)
lneHgtFctrValues = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
lneHgtFctrValueStrings = ["Default", "1.0", "1.1", "1.2", "1.3", "1.4", "1.5", "1.6", "1.7", "1.8", "1.9", "2.0"]
lineHeightFactorMenu = new Form.Field.Option(
"lineHeightFactor",
`Line Height Multiple (${lneHgtFctrCurVal})`,
lneHgtFctrValues,
lneHgtFctrValueStrings,
null
)
lineHeightFactorMenu.allowsNull = true
lineHeightFactorMenu.nullOptionTitle = "No Change"
lneSpcCurVal = nStyle.get(Style.Attribute.ParagraphLineSpacing)
//console.log(lneSpcCurVal)
lneSpcValues = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
lneSpcValueStrings = ["Default", "0", "1", "2", "3", "4", "6", "8", "10", "12"]
lineSpacingMenu = new Form.Field.Option(
"lineSpacing",
`Line Spacing (${lneSpcCurVal})`,
lneSpcValues,
lneSpcValueStrings,
null
)
lineSpacingMenu.allowsNull = true
lineSpacingMenu.nullOptionTitle = "No Change"
inputForm = new Form()
inputForm.addField(paragraphAlignmentMenu)
inputForm.addField(spaceBeforeMenu)
inputForm.addField(spaceAfterMenu)
inputForm.addField(firstLineIndentMenu)
inputForm.addField(headIndentMenu)
inputForm.addField(tailIndentMenu)
inputForm.addField(lineHeightFactorMenu)
inputForm.addField(lineSpacingMenu)
formPrompt = "Note Paragraph Attributes:"
buttonTitle = "Continue"
formObject = await inputForm.show(formPrompt, buttonTitle)
// retrieve attribute settings
if(shouldLog){console.log(">>> RETRIEVING VALUES <<<")}
paragraphAlignment = formObject.values["paragraphAlignment"]
if(shouldLog){console.log("paragraphAlignment", paragraphAlignment)}
spcBfrIndicator = formObject.values["spaceBefore"]
spcBfrValue = spcBfrValueStrings[spcBfrIndicator]
if(shouldLog){console.log("spcBfrValue", spcBfrValue)}
spcAftIndicator = formObject.values["spaceAfter"]
spcAftValue = spcAftValueStrings[spcAftIndicator]
if(shouldLog){console.log("spcAftValue", spcAftValue)}
firstLineIndentIndicator = formObject.values["firstLineIndent"]
firstLineIndentValue = frstLnValueStrings[firstLineIndentIndicator]
if(shouldLog){console.log("firstLineIndentValue", firstLineIndentValue)}
headIndentIndicator = formObject.values["headIndent"]
headIndentValue = headValueStrings[headIndentIndicator]
if(shouldLog){console.log("headIndentValue", headIndentValue)}
tailIndentIndicator = formObject.values["tailIndent"]
tailIndentValue = tailValueStrings[tailIndentIndicator]
if(shouldLog){console.log("tailIndentValue", tailIndentValue)}
lineHeightMultipleIndicator = formObject.values["lineHeightFactor"]
lineHeightMultiple = lneHgtFctrValueStrings[lineHeightMultipleIndicator]
if(shouldLog){console.log("lineHeightMultiple", lineHeightMultiple)}
lineSpacingIndicator = formObject.values["lineSpacing"]
lineSpacingValue = lneSpcValueStrings[lineSpacingIndicator]
if(shouldLog){console.log("lineSpacingValue", lineSpacingValue)}
// SET ATTRIBUTE VALUES
if(shouldLog){console.log(">>> APPLYING VALUES <<<")}
var attsToModify = []
var valuesToApply = []
if(paragraphAlignment){
if(shouldLog){console.log("paragraphAlignment", paragraphAlignment)}
if(paragraphAlignment === "Default"){
var valueToSet = Style.Attribute.ParagraphAlignment.defaultValue
} else {
var valueToSet = paragraphAlignment
}
attsToModify.push(Style.Attribute.ParagraphAlignment)
valuesToApply.push(valueToSet)
}
if(spcBfrValue){
if(shouldLog){console.log("spcBfrValue", spcBfrValue)}
if(spcBfrValue === "Default"){
var valueToSet = Style.Attribute.ParagraphSpacingBefore.defaultValue
} else {
var valueToSet = parseInt(spcBfrValue)
}
attsToModify.push(Style.Attribute.ParagraphSpacingBefore)
valuesToApply.push(valueToSet)
}
if(spcAftValue){
if(shouldLog){console.log("spcAftValue", spcAftValue)}
if(spcAftValue === "Default"){
var valueToSet = Style.Attribute.ParagraphSpacing.defaultValue
} else {
var valueToSet = parseInt(spcAftValue)
}
attsToModify.push(Style.Attribute.ParagraphSpacing)
valuesToApply.push(valueToSet)
}
if(firstLineIndentValue){
if(shouldLog){console.log("firstLineIndentValue", firstLineIndentValue)}
if(firstLineIndentValue === "Default"){
var valueToSet = Style.Attribute.ParagraphFirstLineHeadIndent.defaultValue
} else {
var valueToSet = parseInt(firstLineIndentValue)
}
attsToModify.push(Style.Attribute.ParagraphFirstLineHeadIndent)
valuesToApply.push(valueToSet)
}
if(headIndentValue){
if(shouldLog){console.log("headIndentValue", headIndentValue)}
if(headIndentValue === "Default"){
var valueToSet = Style.Attribute.ParagraphHeadIndent.defaultValue
} else {
var valueToSet = parseInt(headIndentValue)
}
attsToModify.push(Style.Attribute.ParagraphHeadIndent)
valuesToApply.push(valueToSet)
}
if(tailIndentValue){
if(shouldLog){console.log("tailIndentValue", tailIndentValue)}
if(tailIndentValue === "Default"){
var valueToSet = Style.Attribute.ParagraphTailIndent.defaultValue
} else {
var valueToSet = parseInt(tailIndentValue)
}
attsToModify.push(Style.Attribute.ParagraphTailIndent)
valuesToApply.push(valueToSet)
}
if(lineHeightMultiple){
if(shouldLog){console.log("lineHeightMultiple", lineHeightMultiple)}
if(lineHeightMultiple === "Default"){
var valueToSet = Style.Attribute.ParagraphLineHeightMultiple.defaultValue
} else {
var valueToSet = Number(lineHeightMultiple)
}
attsToModify.push(Style.Attribute.ParagraphLineHeightMultiple)
valuesToApply.push(valueToSet)
}
if(lineSpacingValue){
if(shouldLog){console.log("lineSpacingValue", lineSpacingValue)}
if(lineSpacingValue === "Default"){
var valueToSet = Style.Attribute.ParagraphLineSpacing.defaultValue
} else {
var valueToSet = Number(lineSpacingValue)
}
attsToModify.push(Style.Attribute.ParagraphLineSpacing)
valuesToApply.push(valueToSet)
}
// APPLY THE SPECIFIED ATTRIBUTE SETTINGS
runRanges.forEach(range => {
runStyle = noteObj.styleForRange(range)
attsToModify.forEach((att, index) => {
valueToSet = valuesToApply[index]
runStyle.set(att, valueToSet)
})
})
// REPLACE NOTE WITH VIRTUAL OBJECT
if(shouldLog){console.log(">>> REPLACING TEXT WITH NOTEOBJ <<<")}
item.noteText.replace(item.noteText.range, noteObj)
// REVEAL NOTE
if(shouldLog){console.log("SHOW NOTE")}
tree = document.windows[0].content
tree.nodeForObject(item).expandNote(false)
}
catch(err){
if(!err.causedByUserCancelling){
console.log(err.name, err.message)
}
}
});
action.validate = function(selection, sender){
// validation code
// selection options: tasks, projects, folders, tags, allObjects
return (selection.tasks.length === 1 || selection.projects.length === 1 )
};
return action;
})();