mutool run#

The run command executes a JavaScript program, which has access to most of the features of the MuPDF library. The command supports ECMAScript 5 syntax in strict mode. All of the MuPDF constructors and functions live in the global object, and the command line arguments are accessible from the global scriptArgs object. The name of the script is in the global scriptPath variable.

See the mutool JavaScript API for more.

mutool run script.js [ arguments ... ]

Note

Command line parameters within square brackets [] are optional.

script.js

The JavaScript file which you would like to run.

Note

See the mutool JavaScript API for more.


[ arguments ... ]

If invoked without any arguments, it will drop you into an interactive REPL (read-eval-print-loop). To exit this loop type quit() (same as pressing ctrl-C two times).

JavaScript Shell#

Several global functions that are common for command line shells are available:

gc(report)

Run the garbage collector to free up memory. Optionally report statistics on the garbage collection.

load(fileName)

Load and execute script in “fileName”.

print(...)

Print arguments to stdout, separated by spaces and followed by a newline.

quit()

Exit the shell.

read(fileName)

Read the contents of a file and return them as a UTF-8 decoded string.

readline()

Read one line of input from stdin and return it as a string.

require(module)

Load a JavaScript module.

write(...)

Print arguments to stdout, separated by spaces.

Example scripts#

Create and edit PDF documents#

Create PDF document from scratch using only low level functions

docs/examples/pdf-create-lowlevel.js#
// Create a PDF from scratch.

// This example creates a new PDF file from scratch, using only the low level APIs.
// This assumes a basic working knowledge of the PDF file format.

// Create a new empty document with no pages.
var pdf = new PDFDocument()

// Create and add a font resource.
var font = pdf.addObject({
	Type: "Font",
	Subtype: "Type1",
	Encoding: "WinAnsiEncoding",
	BaseFont: "Times-Roman",
})

// Create and add an image resource:
var image = pdf.addRawStream(
	// The raw stream contents, hex encoded to match the Filter entry:
	"004488CCEEBB7733>",
	// The image object dictionary:
	{
		Type: "XObject",
		Subtype: "Image",
		Width: 4,
		Height: 2,
		BitsPerComponent: 8,
		ColorSpace: "DeviceGray",
		Filter: "ASCIIHexDecode",
	}
);

// Create resource dictionary.
var resources = pdf.addObject({
	Font: { Tm: font },
	XObject: { Im0: image },
})

// Create content stream.
var buffer = new Buffer()
buffer.writeLine("10 10 280 330 re s")
buffer.writeLine("q 200 0 0 200 50 100 cm /Im0 Do Q")
buffer.writeLine("BT /Tm 16 Tf 50 50 TD (Hello, world!) Tj ET")
var contents = pdf.addStream(buffer)

// Create page object.
var page = pdf.addObject({
	Type: "Page",
	MediaBox: [0,0,300,350],
	Contents: contents,
	Resources: resources,
})

// Insert page object into page tree.
var pagetree = pdf.getTrailer().Root.Pages
pagetree.Count = 1
pagetree.Kids = [ page ]
page.Parent = pagetree

// Save the document.
pdf.save("out.pdf")

Create PDF document from scratch, using helper functions

docs/examples/pdf-create.js#
// Create a PDF from scratch using helper functions.

// This example creates a new PDF file from scratch, using helper
// functions to create resources and page objects.
// This assumes a basic working knowledge of the PDF file format.

// Create a new empty document with no pages.
var pdf = new PDFDocument()

// Load built-in font and create WinAnsi encoded simple font resource.
var font = pdf.addSimpleFont(new Font("Times-Roman"))

// Load PNG file and create image resource.
var image = pdf.addImage(new Image("example.png"))

// Create resource dictionary.
var resources = pdf.addObject({
	Font: { Tm: font },
	XObject: { Im0: image },
})

// Create content stream data.
var contents =
	"10 10 280 330 re s\n" +
	"q 200 0 0 200 50 100 cm /Im0 Do Q\n" +
	"BT /Tm 16 Tf 50 50 TD (Hello, world!) Tj ET\n"

// Create a new page object.
var page = pdf.addPage([0,0,300,350], 0, resources, contents)

// Insert page object at the end of the document.
pdf.insertPage(-1, page)

// Save the document to file.
pdf.save("out.pdf", "pretty,ascii,compress-images,compress-fonts")

Merge pages from multiple PDF documents into one PDF file

docs/examples/pdf-merge.js#
// A re-implementation of "mutool merge" in JavaScript.

function copyPage(dstDoc, srcDoc, pageNumber, dstFromSrc) {
	var srcPage, dstPage
	srcPage = srcDoc.findPage(pageNumber)
	dstPage = dstDoc.newDictionary()
	dstPage.Type = dstDoc.newName("Page")
	if (srcPage.MediaBox) dstPage.MediaBox = dstFromSrc.graftObject(srcPage.MediaBox)
	if (srcPage.Rotate) dstPage.Rotate = dstFromSrc.graftObject(srcPage.Rotate)
	if (srcPage.Resources) dstPage.Resources = dstFromSrc.graftObject(srcPage.Resources)
	if (srcPage.Contents) dstPage.Contents = dstFromSrc.graftObject(srcPage.Contents)
	dstDoc.insertPage(-1, dstDoc.addObject(dstPage))
}

function copyAllPages(dstDoc, srcDoc) {
	var dstFromSrc = dstDoc.newGraftMap()
	var k, n = srcDoc.countPages()
	for (k = 0; k < n; ++k)
		copyPage(dstDoc, srcDoc, k, dstFromSrc)
}

function pdfmerge() {
	var srcDoc, dstDoc, i

	dstDoc = new PDFDocument()
	for (i = 1; i < scriptArgs.length; ++i) {
		srcDoc = Document.openDocument(scriptArgs[i])
		copyAllPages(dstDoc, srcDoc)
	}
	dstDoc.save(scriptArgs[0], "compress")
}

if (scriptArgs.length < 2)
	print("usage: mutool run pdf-merge.js output.pdf input1.pdf input2.pdf ...")
else
	pdfmerge()

Graphics and the device interface#

Draw all pages in a document to PNG files

docs/examples/draw-document.js#
// Draw all pages in a document and save them as PNG files.

var doc = Document.openDocument(scriptArgs[0]);
var n = doc.countPages();
for (var i = 0; i < n; ++i) {
	var page = doc.loadPage(i);
	var pixmap = page.toPixmap(Matrix.identity, ColorSpace.DeviceRGB);
	pixmap.saveAsPNG("out" + (i+1) + ".png");
}

Use device API to draw graphics and save as a PNG file

docs/examples/draw-device.js#
// Use device interface to draw some graphics and save as a PNG.

var font = new Font("Times-Roman");
var image = new Image("huntingofthesnark.png");
var path, text;

var pixmap = new Pixmap(ColorSpace.DeviceRGB, [0,0,500,600], false);
pixmap.clear(255);
var device = new DrawDevice(Matrix.identity, pixmap);
var transform = [2,0,0,2,0,0]
{
	text = new Text();
	{
		text.showString(font, [16,0,0,-16,100,30], "Hello, world!");
		text.showString(font, [0,16,16,0,15,100], "Hello, world!");
	}
	device.fillText(text, transform, ColorSpace.DeviceGray, [0], 1);

	path = new Path();
	{
		path.moveTo(10, 10);
		path.lineTo(90, 10);
		path.lineTo(90, 90);
		path.lineTo(10, 90);
		path.closePath();
	}
	device.fillPath(path, false, transform, ColorSpace.DeviceRGB, [1,0,0], 1);
	device.strokePath(path, {dashes:[5,10], lineWidth:3, lineCap:'Round'}, transform, ColorSpace.DeviceRGB, [0,0,0], 1);

	path = new Path();
	{
		path.moveTo(100,100);
		path.curveTo(150,100, 200,150, 200,200);
		path.curveTo(200,300, 0,300, 100,100);
		path.closePath();
	}
	device.clipPath(path, true, transform);
	{
		device.fillImage(image, Matrix.concat(transform, [300,0,0,300,0,0]), 1);
	}
	device.popClip();
}
device.close();

pixmap.saveAsPNG("out.png");

Implement a device in JavaScript

docs/examples/trace-device.js#
var Q = JSON.stringify

var pathPrinter = {
	moveTo: function (x,y) { print("moveTo", x, y) },
	lineTo: function (x,y) { print("lineTo", x, y) },
	curveTo: function (x1,y1,x2,y2,x3,y3) { print("curveTo", x1, y1, x2, y2, x3, y3) },
	closePath: function () { print("closePath") },
}

var textPrinter = {
	beginSpan: function (f,m,wmode, bidi, dir, lang) {
		print("beginSpan",f,m,wmode,bidi,dir,repr(lang));
	},
	showGlyph: function (f,m,g,u,v,b) { print("glyph",f,m,g,u,v,b) },
	endSpan: function () { print("endSpan"); }
}

var traceDevice = {
	fillPath: function (path, evenOdd, ctm, colorSpace, color, alpha) {
		print("fillPath", evenOdd, ctm, colorSpace, color, alpha)
		path.walk(pathPrinter)
	},
	clipPath: function (path, evenOdd, ctm) {
		print("clipPath", evenOdd, ctm)
		path.walk(pathPrinter)
	},
	strokePath: function (path, stroke, ctm, colorSpace, color, alpha) {
		print("strokePath", Q(stroke), ctm, colorSpace, color, alpha)
		path.walk(pathPrinter)
	},
	clipStrokePath: function (path, stroke, ctm) {
		print("clipStrokePath", Q(stroke), ctm)
		path.walk(pathPrinter)
	},

	fillText: function (text, ctm, colorSpace, color, alpha) {
		print("fillText", ctm, colorSpace, color, alpha)
		text.walk(textPrinter)
	},
	clipText: function (text, ctm) {
		print("clipText", ctm)
		text.walk(textPrinter)
	},
	strokeText: function (text, stroke, ctm, colorSpace, color, alpha) {
		print("strokeText", Q(stroke), ctm, colorSpace, color, alpha)
		text.walk(textPrinter)
	},
	clipStrokeText: function (text, stroke, ctm) {
		print("clipStrokeText", Q(stroke), ctm)
		text.walk(textPrinter)
	},
	ignoreText: function (text, ctm) {
		print("ignoreText", ctm)
		text.walk(textPrinter)
	},

	fillShade: function (shade, ctm, alpha) {
		print("fillShade", shade, ctm, alpha)
	},
	fillImage: function (image, ctm, alpha) {
		print("fillImage", image, ctm, alpha)
	},
	fillImageMask: function (image, ctm, colorSpace, color, alpha) {
		print("fillImageMask", image, ctm, colorSpace, color, alpha)
	},
	clipImageMask: function (image, ctm) {
		print("clipImageMask", image, ctm)
	},

	beginMask: function (area, luminosity, colorspace, color) {
		print("beginMask", area, luminosity, colorspace, color)
	},
	endMask: function () {
		print("endMask")
	},

	popClip: function () {
		print("popClip")
	},

	beginGroup: function (area, isolated, knockout, blendmode, alpha) {
		print("beginGroup", area, isolated, knockout, blendmode, alpha)
	},
	endGroup: function () {
		print("endGroup")
	},
	beginTile: function (area, view, xstep, ystep, ctm, id) {
		print("beginTile", area, view, xstep, ystep, ctm, id)
		return 0
	},
	endTile: function () {
		print("endTile")
	},
	beginLayer: function (name) {
		print("beginLayer", name)
	},
	endLayer: function () {
		print("endLayer")
	},
	beginStructure: function (structure, raw, uid) {
		print("beginStructure", structure, raw, uiw)
	},
	endStructure: function () {
		print("endStructure")
	},
	beginMetatext: function (meta, metatext) {
		print("beginMetatext", meta, metatext)
	},
	endMetatext: function () {
		print("endMetatext")
	},

	renderFlags: function (set, clear) {
		print("renderFlags", set, clear)
	},
	setDefaultColorSpaces: function (colorSpaces) {
		print("setDefaultColorSpaces", colorSpaces.getDefaultGray(),
		colorSpaces.getDefaultRGB(), colorSpaces.getDefaultCMYK(),
		colorSpaces.getOutputIntent())
	},

	close: function () {
		print("close")
	},
}

if (scriptArgs.length != 2)
	print("usage: mutool run trace-device.js document.pdf pageNumber")
else {
	var doc = Document.openDocument(scriptArgs[0]);
	var page = doc.loadPage(parseInt(scriptArgs[1])-1);
	page.run(traceDevice, Matrix.identity);
}

Advanced examples#

Create a PDF from rendered page thumbnails

docs/examples/create-thumbnail.js#
// Create a PDF containing thumbnails of pages rendered from another PDF.

var pdf = new PDFDocument()

var subdoc = Document.openDocument("pdfref17.pdf")

var resources = { XObject: {} }

var contents = new Buffer()
for (var i=0; i < 5; ++i) {
	var pixmap = subdoc.loadPage(1140+i).toPixmap([0.2,0,0,0.2,0,0], ColorSpace.DeviceRGB, true)
	resources.XObject["Im" + i] = pdf.addImage(new Image(pixmap))
	contents.writeLine("q 100 0 0 150 " + (50+100*i) + " 50 cm /Im" + i + " Do Q")
}

var page = pdf.addPage([0,0,100+i*100,250], 0, resources, contents)
pdf.insertPage(-1, page)

pdf.save("out.pdf")

This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of that license. Refer to licensing information at artifex.com or contact Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, CA 94129, USA, for further information.

Discord logo