Skip to content
Back to top

# Advanced

## Create PDF

    import * as mupdf from "mupdf"

    // 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.

    import mupdf from "mupdf"

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

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

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

    // 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")

## Create PDF (low level)

    import * as mupdf from "mupdf"

    // 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 mupdf.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")

## Merge PDF files

    // A re-implementation of "mutool merge" in JavaScript.

    import * as mupdf from "mupdf"

    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 mupdf.PDFDocument()
    	for (i = 3; i < process.argv.length; ++i) {
    		srcDoc = mupdf.Document.openDocument(process.argv[i])
    		copyAllPages(dstDoc, srcDoc)
    	}
    	dstDoc.save(process.argv[2], "compress")
    }

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

## Create PDF with thumbnails

    // Create a PDF containing thumbnails of pages rendered from another PDF.

    import * as mupdf from "mupdf"

    var pdf = new mupdf.PDFDocument()

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

    var resources = { XObject: {} }

    var contents = new mupdf.Buffer()
    for (var i=0; i < 5; ++i) {
    	var pixmap = subdoc.loadPage(1140+i).toPixmap([0.2,0,0,0.2,0,0], mupdf.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")

## Use Device to draw graphics

    // Use device interface to draw some graphics and save as a PNG.

    import * as mupdf from "mupdf"

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

    var pixmap = new mupdf.Pixmap(mupdf.ColorSpace.DeviceRGB, [ 0, 0, 500, 600 ], false)
    pixmap.clear(255)
    var device = new mupdf.DrawDevice(mupdf.Matrix.identity, pixmap)
    var transform = [ 2, 0, 0, 2, 0, 0 ]
    {
    	text = new mupdf.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, mupdf.ColorSpace.DeviceGray, [ 0 ], 1)

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

    	path = new mupdf.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")

## Trace Device

    import * as mupdf from "mupdf"

    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 = mupdf.Document.openDocument(scriptArgs[0]);
    	var page = doc.loadPage(parseInt(scriptArgs[1])-1);
    	page.run(traceDevice, mupdf.Matrix.identity);
    }