Text Attachments
The cells of outline items can contain non-textual data such as images, or audio files. The following examples show how to add images to new rows using either image import or image data methods.
File Attachment
The fetch() method of the URL class is used to import an image from a file into a row. In this example, the data from a chosen image file is loaded using the fetch() method and then used to create a new instance of the FileWrapper class. The created filewrapper is then passed into the makeFileAttachement() method of the Text class to create an new text attachment object. Once created the attachment is assigned to a specified column in a newly created outline row.
Import Text Attachment Image | ||
01 | imageURL = URL.choose(['public.image']) | |
02 | if (imageURL === null){throw new Error('user cancelled')} | |
03 | urlString = imageURL.string | |
04 | var imageFileName = urlString.substr(urlString.lastIndexOf('/') + 1) | |
05 | imageFileName = decodeURIComponent(imageFileName) | |
06 | imageURL.fetch(function(data){ | |
07 | wrapper = FileWrapper.withContents(imageFileName, data) | |
08 | textObj = Text.makeFileAttachment(wrapper, document.outline.baseStyle) | |
09 | rootItem.addChild( | |
10 | null, | |
11 | function(item){ | |
12 | item.topic = "This is an image named: " + imageFileName | |
13 | item.setValueForColumn(textObj,columns.byTitle('Image')) | |
14 | } | |
15 | ) | |
16 | }) |
Import Images to New Rows Plug-In
Donec id elit non mi porta gravida at eget metus. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Donec sed odio dui. Donec sed odio dui. Integer posuere erat a ante venenatis dapibus posuere velit aliquet. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.
You can download the example document and a folder of employee headshots for use with this plug-in.
New Rows for Imported Images | ||
01 | /*{ | |
02 | "type": "action", | |
03 | "targets": ["omnioutliner"], | |
04 | "author": "Otto Automator", | |
05 | "identifier": "com.omni-automation.oo.pics-to-rows", | |
06 | "version": "1.0", | |
07 | "description": "Create a new row for each chosen image file. Requires column named: “Image”", | |
08 | "label": "Add Rows with Pictures", | |
09 | "shortLabel": "Pics to Rows" | |
10 | }*/ | |
11 | var _ = function(){ | |
12 | var action = new PlugIn.Action(function(selection, sender){ | |
13 | ||
14 | // CHECK FOR IMAGE COLUMN | |
15 | var colTitle = 'Image' | |
16 | columnTitles = columns.map(function(col){ | |
17 | return col.title | |
18 | }) | |
19 | if(columnTitles.indexOf(colTitle) === -1){ | |
20 | alertMsg = 'There is no column titled “' + colTitle + '” in this outline.' | |
21 | var alert = new Alert('Missing Column', alertMsg) | |
22 | alert.addOption('Cancel') | |
23 | alert.show(()=>{}) | |
24 | } else { | |
25 | // FILEPICKER INSTANCE CREATED AND PROPERTIES SET | |
26 | var picker = new FilePicker() | |
27 | picker.folders = false | |
28 | picker.multiple = true | |
29 | picker.types = [FileType.image] | |
30 | ||
31 | // PICKER PROMISE IS CREATED AND STORED WHEN PICKER IS DISPLAYED | |
32 | pickerPromise = picker.show() | |
33 | ||
34 | // PROMISE FUNCTION CALLED UPON PICKER APPROVAL | |
35 | pickerPromise.then(function(urlsArray){ | |
36 | var bStyle = document.outline.baseStyle | |
37 | var imageColumn = columns.byTitle(colTitle) | |
38 | urlsArray.forEach(url => { | |
39 | urlStr = url.string | |
40 | // GET FILE NAME | |
41 | var filename = urlStr.substring(urlStr.lastIndexOf('/')+1) | |
42 | // REMOVE FILE EXTENSION AND ENCODING | |
43 | var baseName = filename.substring(0,filename.lastIndexOf('.')) | |
44 | baseName = decodeURIComponent(baseName) | |
45 | // IMPORT IMAGES | |
46 | url.fetch(function(data){ | |
47 | wrapper = FileWrapper.withContents(filename, data) | |
48 | textObj = Text.makeFileAttachment(wrapper, bStyle) | |
49 | rootItem.addChild( | |
50 | null, | |
51 | function(item){ | |
52 | item.topic = baseName | |
53 | item.setValueForColumn(textObj, imageColumn) | |
54 | } | |
55 | ) | |
56 | }) | |
57 | }) | |
58 | }) | |
59 | ||
60 | // PROMISE FUNCTION CALLED UPON PICKER CANCELLATION | |
61 | pickerPromise.catch(function(err){ | |
62 | console.log("form cancelled", err.message) | |
63 | }) | |
64 | } | |
65 | }); | |
66 | ||
67 | action.validate = function(selection, sender){ | |
68 | return true | |
69 | }; | |
70 | ||
71 | return action; | |
72 | }(); | |
73 | _; |
Image from Data
If small images are often used as text attachments, they can be stored in the script code as a base64 string. This base64 data can then be used to generate an image attachement.
Here’s a useful script for extracting the data of a chosen image file as base64. It uses the toBase64() method of the Data class to convert image data into a base64 format. Run it in the console, and the chosen image data will be logged:
Image File as base64 Data | ||
01 | imageURL = URL.choose(['public.image']) | |
02 | if (imageURL === null){throw new Error('user cancelled')} | |
03 | imageURL.fetch(function(data){ | |
04 | console.log(data.toBase64()) | |
05 | } |
And here’s an example script that has stored a small image of a star in a circle:
Image Attachment from Data | ||
01 | img64 = "iVBORw0KGgoAAAANSUhEUgAAABMAAAASCAYAAAC5DOVpAAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAgtpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDUuNC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iPgogICAgICAgICA8dGlmZjpSZXNvbHV0aW9uVW5pdD4yPC90aWZmOlJlc29sdXRpb25Vbml0PgogICAgICAgICA8dGlmZjpDb21wcmVzc2lvbj41PC90aWZmOkNvbXByZXNzaW9uPgogICAgICAgICA8dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPgogICAgICAgICA8dGlmZjpQaG90b21ldHJpY0ludGVycHJldGF0aW9uPjI8L3RpZmY6UGhvdG9tZXRyaWNJbnRlcnByZXRhdGlvbj4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24" | |
02 | ||
03 | imgData = Data.fromBase64(img64) | |
04 | wrapper = FileWrapper.withContents('star-circle.png',imgData) | |
05 | textObj = Text.makeFileAttachment(wrapper, document.outline.baseStyle) | |
06 | rootItem.addChild( | |
07 | null, | |
08 | function(item){ | |
09 | item.topic = "This is a star in a circle:" | |
10 | item.setValueForColumn(textObj,columns.byTitle('Image')) | |
11 | } | |
12 | ) |
Exporting Audio Attachments
One of the best features of OmniOutliner is the ability to record audio clips that are automatically embedded into the document.
With Omni Automation, you can use the following plug-in to export all of the audio clips from the selected outline rows to MPEG audio files (m4a):
Export Audio Clips from Selected Rows | ||
01 | /*{ | |
02 | "type": "action", | |
03 | "targets": ["omnioutliner"], | |
04 | "author": "Otto Automator", | |
05 | "identifier": "com.omni-automation.oo.export-audio-from-selected-rows", | |
06 | "version": "1.5", | |
07 | "description": "This plug-in will export all of the audio clips attached to the text columns of the selected rows.", | |
08 | "label": "Export Audio Clips from Selected Rows", | |
09 | "shortLabel": "Export Audio" | |
10 | }*/ | |
11 | (() => { | |
12 | var action = new PlugIn.Action(function(selection, sender){ | |
13 | // action code | |
14 | // selection options: columns, document, editor, items, nodes, outline, styles | |
15 | ||
16 | try { | |
17 | // GET ARRAY OF VISIBLE TEXT COLUMNS (OMIT “NOTE BUTTON” COLUMN) | |
18 | var editor = document.editors[0] | |
19 | var textColumns = [noteColumn] | |
20 | columns.forEach(col => { | |
21 | if (editor.visibilityOfColumn(col)){ | |
22 | if (col.type === Column.Type.Text){ | |
23 | if (!textColumns.includes(col)){ | |
24 | textColumns.push(col) | |
25 | } | |
26 | } | |
27 | } | |
28 | }) | |
29 | if(textColumns.length === 0){throw new Error("This document has no text columns.")} | |
30 | ||
31 | // GET ARRAY OF FILEWRAPPERS FOR ATTACHED AUDIO CLIPS | |
32 | var exportWrappers = [] | |
33 | selection.items.forEach((row,index) => { | |
34 | textColumns.forEach(col => { | |
35 | if(row.valueForColumn(col)){ | |
36 | var atts = row.valueForColumn(col).attachments | |
37 | if(atts){ | |
38 | atts.forEach(att => { | |
39 | var wrapper = att.fileWrapper | |
40 | if(wrapper.type === FileWrapper.Type.File && wrapper.preferredFilename.endsWith('.m4a')){ | |
41 | exportWrappers.push(wrapper) | |
42 | } | |
43 | }) | |
44 | } | |
45 | } | |
46 | }) | |
47 | }) | |
48 | ||
49 | // EXPORT THE ATTACHED AUDIO CLIPS | |
50 | if(exportWrappers.length > 0){ | |
51 | // Create and Show File Saver | |
52 | var filesaver = new FileSaver() | |
53 | var folderType = new FileType("public.folder") | |
54 | filesaver.types = [folderType] | |
55 | var fldrwrapper = FileWrapper.withChildren("Audio Clips", exportWrappers) | |
56 | var fileSaverPromise = filesaver.show(fldrwrapper) | |
57 | ||
58 | // Process File Saver Result | |
59 | fileSaverPromise.then(function(urlObj){ | |
60 | console.log(urlObj.string) | |
61 | urlObj.open() | |
62 | }) | |
63 | ||
64 | fileSaverPromise.catch(function(err){ | |
65 | console.log(err.message) | |
66 | }) | |
67 | } | |
68 | } | |
69 | catch(err){ | |
70 | console.error(err) | |
71 | } | |
72 | }); | |
73 | ||
74 | action.validate = function(selection, sender){ | |
75 | // validation code | |
76 | // selection options: columns, document, editor, items, nodes, outline, styles | |
77 | return (selection.items.length > 0) | |
78 | }; | |
79 | ||
80 | return action; | |
81 | })(); |
This webpage is in the process of being developed. Any content may change and may not be accurate or complete at this time.