summaryrefslogtreecommitdiff
path: root/docs/examples
diff options
context:
space:
mode:
authorTor Andersson <tor.andersson@artifex.com>2017-04-07 16:16:56 +0200
committerTor Andersson <tor.andersson@artifex.com>2017-04-13 13:16:21 +0200
commit9c805cc9f934cd12e89014db8ad70e3191cdaf2d (patch)
tree5aece10eaccc25d060b2803c4d201341048caf96 /docs/examples
parent49d5a9856020627af1fc4063ff5a26b26e9270cf (diff)
downloadmupdf-9c805cc9f934cd12e89014db8ad70e3191cdaf2d.tar.xz
Move all examples to docs/examples directory.
Diffstat (limited to 'docs/examples')
-rw-r--r--docs/examples/create-thumbnail.js19
-rw-r--r--docs/examples/draw-device.js45
-rw-r--r--docs/examples/draw-document.js9
-rw-r--r--docs/examples/example.c129
-rw-r--r--docs/examples/multi-threaded.c289
-rw-r--r--docs/examples/pdf-create-lowlevel.js63
-rw-r--r--docs/examples/pdf-create.js35
-rw-r--r--docs/examples/pdf-merge.js67
-rw-r--r--docs/examples/trace-device.js102
9 files changed, 758 insertions, 0 deletions
diff --git a/docs/examples/create-thumbnail.js b/docs/examples/create-thumbnail.js
new file mode 100644
index 00000000..120a8e88
--- /dev/null
+++ b/docs/examples/create-thumbnail.js
@@ -0,0 +1,19 @@
+// Create a PDF containing thumbnails of pages rendered from another PDF.
+
+var pdf = new PDFDocument()
+
+var subdoc = new Document("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], 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")
diff --git a/docs/examples/draw-device.js b/docs/examples/draw-device.js
new file mode 100644
index 00000000..42ec263d
--- /dev/null
+++ b/docs/examples/draw-device.js
@@ -0,0 +1,45 @@
+// Use device interface to draw some graphics and save as a PNG.
+
+var font = new Font("Times-Roman");
+var image = new Image("example.png");
+var path, text;
+
+var pixmap = new Pixmap(DeviceRGB, [0,0,500,600], false);
+pixmap.clear(255);
+var device = new DrawDevice(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, 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, DeviceRGB, [1,0,0], 1);
+ device.strokePath(path, {dashes:[5,10], lineWidth:3, lineCap:'Round'}, transform, 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, Concat(transform, [300,0,0,300,0,0]), 1);
+ }
+ device.popClip();
+}
+device.close();
+
+pixmap.saveAsPNG("out.png");
diff --git a/docs/examples/draw-document.js b/docs/examples/draw-document.js
new file mode 100644
index 00000000..1e285ede
--- /dev/null
+++ b/docs/examples/draw-document.js
@@ -0,0 +1,9 @@
+// Draw all pages in a document and save them as PNG files.
+
+var doc = new Document(argv[1]);
+var n = doc.countPages();
+for (var i = 0; i < n; ++i) {
+ var page = doc.loadPage(i);
+ var pixmap = page.toPixmap(Identity, DeviceRGB);
+ pixmap.saveAsPNG("out" + (i+1) + ".png");
+}
diff --git a/docs/examples/example.c b/docs/examples/example.c
new file mode 100644
index 00000000..1bf67635
--- /dev/null
+++ b/docs/examples/example.c
@@ -0,0 +1,129 @@
+/*
+ How to use MuPDF to render a single page and print the result as a PPM to stdout.
+
+ Build the mupdf library using make, then either run 'make examples' or
+ compile the example manually:
+
+ gcc -Iinclude -o example.exe docs/example.c \
+ build/debug/libmupdf.a \
+ build/debug/libmupdfthird.a
+ -lcrypto \
+ -lm
+
+ ./example.exe document.pdf 100 0 1
+*/
+
+#include <mupdf/fitz.h>
+
+int main(int argc, char **argv)
+{
+ char *input;
+ float zoom, rotate;
+ int page_number, page_count;
+ fz_context *ctx;
+ fz_document *doc;
+ fz_pixmap *pix;
+ fz_matrix ctm;
+ int x, y;
+
+ if (argc < 3)
+ {
+ fprintf(stderr, "usage: example input-file page-number [ zoom [ rotate ] ]\n");
+ fprintf(stderr, "\tinput-file: path of PDF, XPS, CBZ or EPUB document to open\n");
+ fprintf(stderr, "\tPage numbering starts from one.\n");
+ fprintf(stderr, "\tZoom level is in percent (100 percent is 72 dpi).\n");
+ fprintf(stderr, "\tRotation is in degrees clockwise.\n");
+ return EXIT_FAILURE;
+ }
+
+ input = argv[1];
+ page_number = atoi(argv[2]) - 1;
+ zoom = argc > 3 ? atof(argv[3]) : 100;
+ rotate = argc > 4 ? atof(argv[4]) : 0;
+
+ /* Create a context to hold the exception stack and various caches. */
+ ctx = fz_new_context(NULL, NULL, FZ_STORE_UNLIMITED);
+ if (!ctx)
+ {
+ fprintf(stderr, "cannot create mupdf context\n");
+ return EXIT_FAILURE;
+ }
+
+ /* Register the default file types to handle. */
+ fz_try(ctx)
+ fz_register_document_handlers(ctx);
+ fz_catch(ctx)
+ {
+ fprintf(stderr, "cannot register document handlers: %s\n", fz_caught_message(ctx));
+ fz_drop_context(ctx);
+ return EXIT_FAILURE;
+ }
+
+ /* Open the document. */
+ fz_try(ctx)
+ doc = fz_open_document(ctx, input);
+ fz_catch(ctx)
+ {
+ fprintf(stderr, "cannot open document: %s\n", fz_caught_message(ctx));
+ fz_drop_context(ctx);
+ return EXIT_FAILURE;
+ }
+
+ /* Count the number of pages. */
+ fz_try(ctx)
+ page_count = fz_count_pages(ctx, doc);
+ fz_catch(ctx)
+ {
+ fprintf(stderr, "cannot count number of pages: %s\n", fz_caught_message(ctx));
+ fz_drop_document(ctx, doc);
+ fz_drop_context(ctx);
+ return EXIT_FAILURE;
+ }
+
+ if (page_number < 0 || page_number >= page_count)
+ {
+ fprintf(stderr, "page number out of range: %d (page count %d)\n", page_number + 1, page_count);
+ fz_drop_document(ctx, doc);
+ fz_drop_context(ctx);
+ return EXIT_FAILURE;
+ }
+
+ /* Compute a transformation matrix for the zoom and rotation desired. */
+ /* The default resolution without scaling is 72 dpi. */
+ fz_scale(&ctm, zoom / 100, zoom / 100);
+ fz_pre_rotate(&ctm, rotate);
+
+ /* Render page to an RGB pixmap. */
+ fz_try(ctx)
+ pix = fz_new_pixmap_from_page_number(ctx, doc, page_number, &ctm, fz_device_rgb(ctx), 0);
+ fz_catch(ctx)
+ {
+ fprintf(stderr, "cannot render page: %s\n", fz_caught_message(ctx));
+ fz_drop_document(ctx, doc);
+ fz_drop_context(ctx);
+ return EXIT_FAILURE;
+ }
+
+ /* Print image data in ascii PPM format. */
+ printf("P3\n");
+ printf("%d %d\n", pix->w, pix->h);
+ printf("255\n");
+ for (y = 0; y < pix->h; ++y)
+ {
+ unsigned char *p = &pix->samples[y * pix->stride];
+ for (x = 0; x < pix->w; ++x)
+ {
+ if (x > 0)
+ printf(" ");
+ printf("%3d %3d %3d", p[0], p[1], p[2]);
+ p += pix->n;
+ }
+ printf("\n");
+ }
+
+ /* Clean up. */
+ fz_drop_pixmap(ctx, pix);
+ fz_drop_document(ctx, doc);
+ fz_drop_context(ctx);
+ return EXIT_SUCCESS;
+}
diff --git a/docs/examples/multi-threaded.c b/docs/examples/multi-threaded.c
new file mode 100644
index 00000000..15fdcba2
--- /dev/null
+++ b/docs/examples/multi-threaded.c
@@ -0,0 +1,289 @@
+// Multi-threaded rendering of all pages in a document to PNG images.
+
+// First look at doc/example.c and make sure you understand it.
+// Then read the multi-threading section in doc/overview.txt,
+// before coming back here to see an example of multi-threading.
+
+// This example will create one main thread for reading pages from the
+// document, and one thread per page for rendering. After rendering
+// the main thread will wait for each rendering thread to complete before
+// writing that thread's rendered image to a PNG image. There is
+// nothing in MuPDF requiring a rendering thread to only render a
+// single page, this is just a design decision taken for this example.
+
+// Compile a debug build of mupdf, then compile and run this example:
+//
+// gcc -g -o build/debug/example-mt -Iinclude docs/multi-threaded.c \
+// build/debug/libmupdf.a \
+// build/debug/libmupdfthird.a \
+// -lpthread -lcrypto -lm
+//
+// build/debug/example-mt /path/to/document.pdf
+//
+// Caution! As all pages are rendered simultaneously, please choose a
+// file with just a few pages to avoid stressing your machine too
+// much. Also you may run in to a limitation on the number of threads
+// depending on your environment.
+
+// Include the MuPDF header file, and pthread's header file.
+#include <mupdf/fitz.h>
+#include <pthread.h>
+
+// A convenience function for dying abruptly on pthread errors.
+
+void
+fail(char *msg)
+{
+ fprintf(stderr, "%s\n", msg);
+ abort();
+}
+
+// The data structure passed between the requesting main thread and
+// each rendering thread.
+
+struct data {
+ // A pointer to the original context in the main thread sent
+ // from main to rendering thread. It will be used to create
+ // each rendering thread's context clone.
+ fz_context *ctx;
+
+ // Page number sent from main to rendering thread for printing
+ int pagenumber;
+
+ // The display list as obtained by the main thread and sent
+ // from main to rendering thread. This contains the drawing
+ // commands (text, images, etc.) for the page that should be
+ // rendered.
+ fz_display_list *list;
+
+ // The area of the page to render as obtained by the main
+ // thread and sent from main to rendering thread.
+ fz_rect bbox;
+
+ // This is the result, a pixmap containing the rendered page.
+ // It is passed first from main thread to the rendering
+ // thread, then its samples are changed by the rendering
+ // thread, and then back from the rendering thread to the main
+ // thread.
+ fz_pixmap *pix;
+};
+
+// This is the function run by each rendering function. It takes
+// pointer to an instance of the data structure described above and
+// renders the display list into the pixmap before exiting.
+
+void *
+renderer(void *data)
+{
+ int pagenumber = ((struct data *) data)->pagenumber;
+ fz_context *ctx = ((struct data *) data)->ctx;
+ fz_display_list *list = ((struct data *) data)->list;
+ fz_rect bbox = ((struct data *) data)->bbox;
+ fz_pixmap *pix = ((struct data *) data)->pix;
+ fz_device *dev;
+
+ fprintf(stderr, "thread at page %d loading!\n", pagenumber);
+
+ // The context pointer is pointing to the main thread's
+ // context, so here we create a new context based on it for
+ // use in this thread.
+
+ ctx = fz_clone_context(ctx);
+
+ // Next we run the display list through the draw device which
+ // will render the request area of the page to the pixmap.
+
+ fprintf(stderr, "thread at page %d rendering!\n", pagenumber);
+ dev = fz_new_draw_device(ctx, &fz_identity, pix);
+ fz_run_display_list(ctx, list, dev, &fz_identity, &bbox, NULL);
+ fz_close_device(ctx, dev);
+ fz_drop_device(ctx, dev);
+
+ // This threads context is freed.
+
+ fz_drop_context(ctx);
+
+ fprintf(stderr, "thread at page %d done!\n", pagenumber);
+
+ return data;
+}
+
+// These are the two locking functions required by MuPDF when
+// operating in a multi-threaded environment. They each take a user
+// argument that can be used to transfer some state, in this case a
+// pointer to the array of mutexes.
+
+void lock_mutex(void *user, int lock)
+{
+ pthread_mutex_t *mutex = (pthread_mutex_t *) user;
+
+ if (pthread_mutex_lock(&mutex[lock]) != 0)
+ fail("pthread_mutex_lock()");
+}
+
+void unlock_mutex(void *user, int lock)
+{
+ pthread_mutex_t *mutex = (pthread_mutex_t *) user;
+
+ if (pthread_mutex_unlock(&mutex[lock]) != 0)
+ fail("pthread_mutex_unlock()");
+}
+
+int main(int argc, char **argv)
+{
+ char *filename = argc >= 2 ? argv[1] : "";
+ pthread_t *thread = NULL;
+ fz_locks_context locks;
+ pthread_mutex_t mutex[FZ_LOCK_MAX];
+ fz_context *ctx;
+ fz_document *doc;
+ int threads;
+ int i;
+
+ // Initialize FZ_LOCK_MAX number of non-recursive mutexes.
+
+ for (i = 0; i < FZ_LOCK_MAX; i++)
+ {
+ if (pthread_mutex_init(&mutex[i], NULL) != 0)
+ fail("pthread_mutex_init()");
+ }
+
+ // Initialize the locking structure with function pointers to
+ // the locking functions and to the user data. In this case
+ // the user data is a pointer to the array of mutexes so the
+ // locking functions can find the relevant lock to change when
+ // they are called. This way we avoid global variables.
+
+ locks.user = mutex;
+ locks.lock = lock_mutex;
+ locks.unlock = unlock_mutex;
+
+ // This is the main threads context function, so supply the
+ // locking structure. This context will be used to parse all
+ // the pages from the document.
+
+ ctx = fz_new_context(NULL, &locks, FZ_STORE_UNLIMITED);
+
+ // Register default file types.
+
+ fz_register_document_handlers(ctx);
+
+ // Open the PDF, XPS or CBZ document. Note, this binds doc to ctx.
+ // You must only ever use doc with ctx - never a clone of it!
+
+ doc = fz_open_document(ctx, filename);
+
+ // Retrieve the number of pages, which translates to the
+ // number of threads used for rendering pages.
+
+ threads = fz_count_pages(ctx, doc);
+ fprintf(stderr, "spawning %d threads, one per page...\n", threads);
+
+ thread = malloc(threads * sizeof (pthread_t));
+
+ for (i = 0; i < threads; i++)
+ {
+ fz_page *page;
+ fz_rect bbox;
+ fz_irect rbox;
+ fz_display_list *list;
+ fz_device *dev;
+ fz_pixmap *pix;
+ struct data *data;
+
+ // Load the relevant page for each thread. Note, that this
+ // cannot be done on the worker threads, as each use of doc
+ // uses ctx, and only one thread can be using ctx at a time.
+
+ page = fz_load_page(ctx, doc, i);
+
+ // Compute the bounding box for each page.
+
+ fz_bound_page(ctx, page, &bbox);
+
+ // Create a display list that will hold the drawing
+ // commands for the page. Once we have the display list
+ // this can safely be used on any other thread as it is
+ // not bound to a given context.
+
+ list = fz_new_display_list(ctx, &bbox);
+
+ // Run the loaded page through a display list device
+ // to populate the page's display list.
+
+ dev = fz_new_list_device(ctx, list);
+ fz_run_page(ctx, page, dev, &fz_identity, NULL);
+ fz_close_device(ctx, dev);
+ fz_drop_device(ctx, dev);
+
+ // The page is no longer needed, all drawing commands
+ // are now in the display list.
+
+ fz_drop_page(ctx, page);
+
+ // Create a white pixmap using the correct dimensions.
+
+ pix = fz_new_pixmap_with_bbox(ctx, fz_device_rgb(ctx), fz_round_rect(&rbox, &bbox), 0);
+ fz_clear_pixmap_with_value(ctx, pix, 0xff);
+
+ // Populate the data structure to be sent to the
+ // rendering thread for this page.
+
+ data = malloc(sizeof (struct data));
+
+ data->pagenumber = i + 1;
+ data->ctx = ctx;
+ data->list = list;
+ data->bbox = bbox;
+ data->pix = pix;
+
+ // Create the thread and pass it the data structure.
+
+ if (pthread_create(&thread[i], NULL, renderer, data) != 0)
+ fail("pthread_create()");
+ }
+
+ // Now each thread is rendering pages, so wait for each thread
+ // to complete its rendering.
+
+ fprintf(stderr, "joining %d threads...\n", threads);
+ for (i = threads - 1; i >= 0; i--)
+ {
+ char filename[42];
+ struct data *data;
+
+ if (pthread_join(thread[i], (void **) &data) != 0)
+ fail("pthread_join");
+
+ sprintf(filename, "out%04d.png", i);
+ fprintf(stderr, "\tSaving %s...\n", filename);
+
+ // Write the rendered image to a PNG file
+
+ fz_save_pixmap_as_png(ctx, data->pix, filename);
+
+ // Free the thread's pixmap and display list since
+ // they were allocated by the main thread above.
+
+ fz_drop_pixmap(ctx, data->pix);
+ fz_drop_display_list(ctx, data->list);
+
+ // Free the data structured passed back and forth
+ // between the main thread and rendering thread.
+
+ free(data);
+ }
+
+ fprintf(stderr, "finally!\n");
+ fflush(NULL);
+
+ free(thread);
+
+ // Finally the document is closed and the main thread's
+ // context is freed.
+
+ fz_drop_document(ctx, doc);
+ fz_drop_context(ctx);
+
+ return 0;
+}
diff --git a/docs/examples/pdf-create-lowlevel.js b/docs/examples/pdf-create-lowlevel.js
new file mode 100644
index 00000000..658adfb5
--- /dev/null
+++ b/docs/examples/pdf-create-lowlevel.js
@@ -0,0 +1,63 @@
+// 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:
+// Allocate a slot for a new object and get a reference to it.
+var image = pdf.createObject()
+// Write a dictionary object into the slot.
+image.writeObject({
+ Type: "XObject",
+ Subtype: "Image",
+ Width: 4,
+ Height: 2,
+ BitsPerComponent: 8,
+ ColorSpace: "DeviceGray",
+ // The compression filter to be used:
+ Filter: "ASCIIHexDecode",
+})
+// Write raw stream data into the slot; hex encoded
+// to match the Filter entry in the dictionary.
+image.writeRawStream("004488CCEEBB7733>")
+
+// 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")
diff --git a/docs/examples/pdf-create.js b/docs/examples/pdf-create.js
new file mode 100644
index 00000000..290d1737
--- /dev/null
+++ b/docs/examples/pdf-create.js
@@ -0,0 +1,35 @@
+// 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")
diff --git a/docs/examples/pdf-merge.js b/docs/examples/pdf-merge.js
new file mode 100644
index 00000000..d8cdcfcb
--- /dev/null
+++ b/docs/examples/pdf-merge.js
@@ -0,0 +1,67 @@
+// A re-implementation of "mutool merge" in JavaScript.
+
+function graftObject(dstDoc, srcDoc, srcObj, map) {
+ var srcNum, dstRef, dstObj
+ if (!map)
+ map = []
+ if (srcObj.isIndirect()) {
+ srcNum = srcObj.toIndirect()
+ if (map[srcNum])
+ return map[srcNum]
+ map[srcNum] = dstRef = dstDoc.createObject()
+ dstRef.writeObject(graftObject(dstDoc, srcDoc, srcObj.resolve(), map))
+ if (srcObj.isStream())
+ dstRef.writeRawStream(srcObj.readRawStream())
+ return dstRef
+ }
+ if (srcObj.isArray()) {
+ dstObj = dstDoc.newArray()
+ srcObj.forEach(function (key, val) {
+ dstObj[key] = graftObject(dstDoc, srcDoc, val, map)
+ })
+ return dstObj
+ }
+ if (srcObj.isDictionary()) {
+ dstObj = dstDoc.newDictionary()
+ srcObj.forEach(function (key, val) {
+ dstObj[key] = graftObject(dstDoc, srcDoc, val, map)
+ })
+ return dstObj
+ }
+ return srcObj /* primitive objects are not bound to a document */
+}
+
+function copyPage(dstDoc, srcDoc, pageNumber, map) {
+ var srcPage, dstPage
+ srcPage = srcDoc.findPage(pageNumber)
+ dstPage = dstDoc.newDictionary()
+ dstPage.Type = dstDoc.newName("Page")
+ if (srcPage.MediaBox) dstPage.MediaBox = graftObject(dstDoc, srcDoc, srcPage.MediaBox, map)
+ if (srcPage.Rotate) dstPage.Rotate = graftObject(dstDoc, srcDoc, srcPage.Rotate, map)
+ if (srcPage.Resources) dstPage.Resources = graftObject(dstDoc, srcDoc, srcPage.Resources, map)
+ if (srcPage.Contents) dstPage.Contents = graftObject(dstDoc, srcDoc, srcPage.Contents, map)
+ dstDoc.insertPage(-1, dstDoc.addObject(dstPage))
+}
+
+function copyAllPages(dstDoc, srcDoc) {
+ var k, n = srcDoc.countPages()
+ var srcMap = []
+ for (k = 0; k < n; ++k)
+ copyPage(dstDoc, srcDoc, k, srcMap)
+}
+
+function pdfmerge() {
+ var srcDoc, dstDoc, i
+
+ dstDoc = new PDFDocument()
+ for (i = 2; i < argv.length; ++i) {
+ srcDoc = new PDFDocument(argv[i])
+ copyAllPages(dstDoc, srcDoc)
+ }
+ dstDoc.save(argv[1], "compress")
+}
+
+if (argv.length < 3)
+ print("usage: mutool run pdf-merge.js output.pdf input1.pdf input2.pdf ...")
+else
+ pdfmerge()
diff --git a/docs/examples/trace-device.js b/docs/examples/trace-device.js
new file mode 100644
index 00000000..9d7d4373
--- /dev/null
+++ b/docs/examples/trace-device.js
@@ -0,0 +1,102 @@
+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 = {
+ showGlyph: function (f,m,g,u,v,b) { print("glyph",f,m,g,u,v,b) },
+}
+
+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")
+ },
+
+ close: function () {
+ print("close")
+ },
+}
+
+if (argv.length != 3)
+ print("usage: mutool run trace-device.js document.pdf pageNumber")
+else {
+ var doc = new Document(argv[1]);
+ var page = doc.loadPage(parseInt(argv[2])-1);
+ page.run(traceDevice, Identity);
+}