1

In an effort to reduce clientside load, we are attempting to do the work of flattening Paper.js layers on a Node Express server. We have many layers to flatten with lots of image data. And rather than overwriting our data structure, we want to end up with new objects containing the rasterized (flattened) layers.

So we have an Express route that looks like this:

app.post('/flatten', function (request, response) {
    var pdfs = JSON.parse(request.body.pdfs);

    // Attempt to set up canvas on the server side to work with
    var canvas = new paper.Canvas(1000, 1000);
    paper.setup(canvas);
    paper.view.draw();

    for (var i = 0; i < pdfs.length; i++) {
        var pdf = pdfs[i];
        if (pdf !== null) {
            for (var j = 0; j < pdf.pages.length; j++) {
                if (pdf.pages[j].layer !== undefined) {
                    paper.project.layers.push(pdf.pages[j].layer); // Attempt to add to current project; necessary?
                    pdf.pages[j].layer.activate(); // Blows up
                    pdf.pages[j].layer.visible = true;
                    var layerAsRaster = pdf.pages[j].layer.rasterize(); // Blows up
                    layerAsRaster.visible = false;
                    var dataString = layerAsRaster.toDataURL();
                    pdfs[i].pages[j].pageImageData = dataString.split(',')[1];
                    pdf.pages[j].layer.visible = false;
                }
            }
        }
    }

    response.send(pdfs);
});

The .layer is a native Paper.js layer that was made on the clientside.

We receive this error when hitting this route:

TypeError: pdf.pages[j].layer.activate is not a function

Thinking that perhaps we don't need to worry about activating layers on the serverside, I commented that out, but got the same error for the .rasterize line. (See the two lines commented "Blows up".)

Do I need to somehow import the layers we're receiving from the client into the project? I attempt to do that with the line:

paper.project.layers.push(pdf.pages[j].layer);

but to no avail.

How can I modify this method to successfully work with layers on the serverside?

Scotty H
  • 6,432
  • 6
  • 41
  • 94
  • Why not try using `forEach` to iterate instead? – brandonscript Jan 12 '16 at 15:25
  • @remus that may make the code cleaner, but do you think it will solve the problem at hand? If so how? – Scotty H Jan 12 '16 at 15:26
  • I suspect that the use of an iterator var is causing some confusing code that is getting overlooked. By cleaning it up and using forEach, you'll at least simplify things so you can better troubleshoot. – brandonscript Jan 12 '16 at 15:28
  • What kind of data do you send from the client? I guess you want to export your drawings with either [exportSVG](http://paperjs.org/reference/project/#exportsvg) or [exportJSON](http://paperjs.org/reference/project/#exportjson). You can then import them on your server. – arthur.sw Jan 13 '16 at 08:37

1 Answers1

1

The problem is that you are directly adding the layer to the project with the line paper.project.layers.push(pdf.pages[j].layer);

You're not allowed to directly manipulate paper's data structures. If you want to add a layer to a project use the following (note that this is not documented and will change with the next release of paper, but I don't think you'll need to do this):

(paperscript)

project.addChild(layer);

(javascript)

paper.project.addChild(layer);

It's not clear how pdf.pages[i].layer was created on the server side, whether it was imported via JSON (in which case it could already be inserted into the project), or whether it was removed from another project, so there may be other complications.

I think there is another problem. It doesn't appear that pdf.pages[i].layer has been turned into a server-side layer. So the key question is how was it transferred from the client to the server?

Here's a stab at the whole process:

(client side)

jsonLayer = paper.project.activeLayer.exportJSON();

// send jsonLayer to server using some method

(server side)

// get jsonLayer from client

layer = new paper.Layer();
layer.importJSON(jsonLayer);

layer should already be inserted into the project and should contain all the items that were in jsonLayer which was the layer on the client.

Here's a link to a discussion on how importJSON and exportJSON map to one another:

paperjs group discussion

bmacnaughton
  • 4,950
  • 3
  • 27
  • 36
  • The layer was transferred as a serialized JSON object, using Paper's own exportJSON() method. Making a new paper.Layer() and calling newLayer.importJSON(pdf.pages[i].layer) didn't seem to work either. – Scotty H Jan 16 '16 at 16:12
  • you would want to do project.activeLayer.importJSON(serializedLayer) where serializedLayer is the output of clientLayer.exportJSON() and activeLayer is empty. See https://groups.google.com/forum/#!searchin/paperjs/importJSON/paperjs/Mw5inE0Tyko/nMRWHwi2kKsJ – bmacnaughton Jan 16 '16 at 16:19
  • So each `pdf.pages[i].layer` is a separately generated JSON representation of a layer? Like `foreach (layer in paper.project.layers) {pdf.pages[i].layer = layer.exportJSON()}` (pardon the pseudo-code). – bmacnaughton Jan 16 '16 at 16:29