Here is a script for printing TaskPaper 3 documents through Brett Terspstra’s excellent Marked 2 71
Marked 2 lets you choose from a menu of alternative CSS templates, and exports to a range of formats, as well as printing directly.
The script simply:
- Copies the active TaskPaper 3 document to the clipboard (in Markdown format), and,
- if Marked 2 71 is installed and running on your system, displays a preview, allowing you to choose a CSS template and print or export.
(It doesn’t alter the TaskPaper 3 document itself – just places a markdown version in the clipboard)
// COPY THE ACTIVE TASKPAPER DOCUMENT INTO THE CLIPBOARD IN A FORM THAT CAN BE
// PREVIEWED AND PRINTED (WITH CSS STYLESHEETS) USING MARKED 2
// appIsInstalled :: String -> Bool
function appIsInstalled(strBundleId) {
ObjC.import('AppKit');
return ObjC.unwrap(
$.NSWorkspace.sharedWorkspace
.URLForApplicationWithBundleIdentifier(
strBundleId
)
.fileSystemRepresentation
) !== undefined;
}
var strKME = "com.stairways.keyboardmaestro.engine",
blnKM = appIsInstalled(strKME),
kmVars = blnKM ? Application(strKME)
.variables : undefined;
// Marked 2 > Preview > Clipboard Preview
// http://marked2app.com/
// If Marked 2 is installed, the script opens it and displays a CSS-formatted clipboard preview
// Draft 0.09 Rob Trew 2016-04-13
// 0.09 Allows for hiding of @saved searches
// 0.08 Allows for printing only outline-visible + filter-visible lines
// 0.06 Allows for an API change in 3.2 Preview (197)
// (item.bodyContentString || item.bodyDisplayString)
// 0.05 adds further options (at bottom of script):
// - baseHeaderLevel
// - hideProjectColon
// - markedStyle
// - tagEmphasis
// - tagsToHide
// 0.02 adds option for blank line between a task and its note(s)
(function (dctOptions) {
'use strict';
// TASKPAPER CONTEXT
function TaskPaperContext(editor, options) {
// tagString :: item -> String
function tagString(item, lstHidden, strMDChar) {
strMDChar = strMDChar || '';
var dctAttribs = item.attributes,
lstTags = Object.keys(dctAttribs)
.filter(function (k) {
return lstHidden.indexOf(k) === -1 &&
k !== 'data-type' && k !== 'indent';
})
.map(function (k) {
var v = dctAttribs[k];
return strMDChar + '@' + k.slice(5) +
(v ? '(' + v + ')' : '') + strMDChar;
});
return ' ' + lstTags.join(' ');
}
var blnNoteGap = parseInt(options.noteGap, 10),
blnShowAll = !(parseInt(options.hideFoldedAndFiltered, 10)),
blnHideSearches = parseInt(options.hideSavedSearches, 10),
intBaseLevel = options.baseHeaderLevel,
strStyle = options.markedStyle,
blnColon = !parseInt(options.hideProjectColon, 10),
maybeHidden = options.tagsToHide,
lstHidden = maybeHidden instanceof Array ? ['search'].concat(
maybeHidden)
.map(
function (tag) {
return tag !== '*' ? (
'data-' + (tag.charAt(0) === '@' ? tag.slice(1) :
tag)
) : '*';
}) : ['search'],
blnAllTagsHidden = lstHidden.indexOf('*') !== -1,
strEmphasis = options.tagEmphasis;
// Lines to print
var lstVisible = editor.outline.items
.filter(function (x) {
return (blnShowAll || editor.isDisplayed(x));
}),
lstItems = blnHideSearches ? lstVisible.filter(function (x) {
return x.bodyString.indexOf('@search') !== 0;
}) : lstVisible;
lngLast = lstItems.length - 1;
return (
intBaseLevel ? 'Base Header Level: ' +
intBaseLevel.toString() + '\n' : ''
) + (
strStyle ? 'Marked Style: ' + strStyle + '\n\n' : ''
) + lstItems
// Indent levels required by Markdown format
.reduce(function (a, item) {
var strType = item.getAttribute('data-type'),
blnProj = strType === 'project';
if (blnProj) a.depth = item.depth;
a.lines.push(
blnProj ? {
isProject: true,
item: item,
mdIndent: 0
} : {
isNote: strType !== 'task',
item: item,
mdIndent: item.depth - a.depth
}
);
return a;
}, {
lines: [],
depth: 0
})
.lines
// Hash prefixes, zero indents and extra line breaks for projects,
// adjusted indents for tasks and notes.
.map(function (mItem, i, xs) {
var item = mItem.item,
blnProject = mItem.isProject,
blnNote = mItem.isNote || false,
blnTask = !(blnProject || blnNote),
blnGap = blnNote && blnNoteGap,
blnLastGap = (blnGap && (i < lngLast)) ? (!xs[i + 1]
.isNote
) : false
strText = (item.bodyContentString || item.bodyDisplayString ||
'');
return (
blnProject ? (
'\n' + Array(item.depth + 1)
.join('#') + ' '
) : Array(mItem.mdIndent)
.join('\t')
) +
(blnTask ? '- ' : '') +
(blnGap ? '<p>' : '') +
strText +
(blnProject && blnColon ? ':' : '') +
(blnAllTagsHidden ? '' : tagString(item, lstHidden,
strEmphasis)) +
(blnLastGap ? '<p>' : '');
})
.join('\n');
}
// JAVASCRIPT FOR AUTOMATION CONTEXT
// menuItemClick :: String -> [String] -> IO ()
function menuItemClick(strAppID, lstMenuPath) {
var oApp = Application(strAppID),
strAppName = oApp.name(),
lngChain = lstMenuPath.length,
blnResult = false;
if (lngChain > 1) {
var appSE = Application("System Events"),
lstApps = appSE.processes.where({
name: strAppName
}),
procApp = lstApps.length ? lstApps[0] : null;
if (procApp) {
oApp.activate();
var strMenu = lstMenuPath[0],
fnMenu = procApp.menuBars[0].menus.byName(strMenu),
lngLast = lngChain - 1;
for (var i = 1; i < lngLast; i++) {
strMenu = lstMenuPath[i];
fnMenu = fnMenu.menuItems[strMenu].menus[strMenu];
}
fnMenu.menuItems[
lstMenuPath[lngLast]
].click();
blnResult = true;
}
}
return blnResult;
}
var strMarked2 = "com.brettterpstra.marked2";
var ds = Application("TaskPaper")
.documents,
d = ds.length ? ds[0] : undefined;
var strClip = d ? d.evaluate({
script: TaskPaperContext.toString(),
withOptions: dctOptions
}) : '';
// Place Markdown copy in clipboard,
// displaying in Marked 2 if it is installed
if (strClip.length > 0) {
var a = Application.currentApplication(),
sa = (a.includeStandardAdditions = true, a);
sa.setTheClipboardTo(strClip);
if (appIsInstalled(strMarked2)) {
Application(strMarked2)
.activate();
menuItemClick(strMarked2, ['Preview', 'Clipboard Preview']);
}
}
return strClip;
})(blnKM ? { // Read Keyboard Maestro values in macro
// Integer 1-6 Render unindented projects as H1-H6
baseHeaderLevel: kmVars['Base header level'].value(),
// Bool (Print outline-visible and filter-visible lines only ?)
hideFoldedAndFiltered: kmVars['Hide folded and filtered'].value(),
// Bool
hideProjectColon: kmVars['Hide project colon'].value(),
// Bool
hideSavedSearches: kmVars['Hide saved searches'].value(),
// StyleName or empty string or undefined to use Marked default
markedStyle: kmVars['Marked 2 style'].value(),
// blank line between a task and its note text ?
noteGap: kmVars['Gap between notes'].value(),
// asterisk for italic, two for bold, backtick for code
tagEmphasis: kmVars['Tag emphasis'].value(),
// ['*'] hides all tags in printed version
// ['done', 'due'] hides @done and @due tags
// [] or false hides no tags
tagsToHide: kmVars['Tags to hide'].value()
.split(/[\s\,\;]+/)
} : {
// USE MANUAL VALUES (1 for Bool true, 0 for Bool false)
// Integer 1-6 Render unindented projects as H1-H6
baseHeaderLevel: 4,
// 1|0 (Print outline-visible and filter-visible lines only ?)
hideFoldedAndFiltered: 1,
// 1|0 (true|false)
hideProjectColon: 1,
// 1|0
hideSavedSearches: 1,
// StyleName or empty string or undefined to use Marked default
markedStyle: 'Antique',
// 1|0 blank line between a task and its note text ?
noteGap: 1,
// asterisk for italic, two for bold, backtick for code
tagEmphasis: '*',
// ['*'] hides all tags in printed version
// ['done', 'due'] hides @done and @due tags
// [] or false hides no tags
tagsToHide: ['alert']
});