Working with product model
- 11 minutes to read
Sometimes you may need to transform or analyze a design file. Customer's Canvas provides several solutions for this:
- Use the Design Atoms library directly or through the editor.
- Manipulate the serialized design model directly.
- Use the RESTful Design Atoms API.
In this article, you will learn how you can manipulate print-product models of a design and work with surfaces, print areas, and mockups through HTTP requests. These endpoints are organized in the group DesignAtomsPrintProduct
and allows you to both general CRUD operations on objects embedded in the design and such specific operations as combining multiple designs into one, inserting an external image into an empty design template, resizing an empty template, replacing text in elements, adding mockups, and more.
Using the API
This guide assumes that you are familiar with the principles of using the Customer's Canvas API. If not, we recommend reviewing these articles first.
- API Reference - for an API overview and information about supported API Client libraries
- C# API Client - for a guide on integrating the C# API Client library to your .NET app
Before starting, ensure that your app has credentials to make API calls:
- In your tenant, navigate to Settings > External apps and register your app.
- In your app, implement the authentication as explained in the Authentication in Customer's Canvas Hub article.
Design Atoms API is a multi-tenant service. The tenantId must be correctly filled in the method's parameters. It must match the tenantId encoded in the token. If no tenantId is specified, it will be taken from the token.
Design Atoms API allows you to work with both public and private assets in a tenant. Therefore, the parameters of method calls include "ownerId" fields indicating the owner of an asset. For public assets, these fields are left unfilled. For private assets, they must be filled in.
C# API clients
To use a C# API client, it is necessary to set it up through a dependency injection mechanism. For .NET Core, it is a built-in dependency injection functionality, for .NET Framework we will use Autofac. You may use another IoC container if you prefer. For more details, refer to Setting Up API Clients.
To make successful API calls, it is necessary to authorize them. You can do so by passing an API key or a token received from BackOffice to the configuration. To learn possible ways to authorize a client, refer to the Authorization instructions.
There is a separate client for each section in the API Reference. To work with design models, you will need an interface IDesignAtomsPrintProductApiClient
.
Working with surfaces
The model
A DesignSurfaceDto
object represents a surface and defines its ID, its position in the array of surfaces, and its size.
{
"id": "string",
"name": "string",
"index": 0,
"size": {
"width": 0,
"height": 0
}
}
Methods
The official C# API Client provides the following methods, which are the endpoint wrappers.
GetDesignSurfaces(id): DesignSurfaceDto[]
Gets a list of design surfaces as a set of data models.
GET /api/atoms/v1/designs/{id}/print-product/surfaces
-> DesignSurfaceDto[]
DeleteDesignSurface(id, surfaceId): void
Deletes a surface from a design.
DELETE /api/atoms/v1/designs/{id}/print-product/surfaces/{surfaceId}
GetDesignSurfaceModel(id, surfaceId): string
Gets a serialized surface model containing all its components.
GET /api/atoms/v1/designs/{id}/print-product/surfaces/{surfaceId}/model
-> string
ReplaceDesignSurfaceModel(id, surfaceId, surfaceModel): string
Replaces the specified design surface with another surface transferred as a serialized model. At the same time, the IDs of all elements of the transferred surface are forcibly replaced with new values to prevent a situation where elements with the same ID appear in the design.
PUT /api/atoms/v1/designs/{id}/print-product/surfaces/{surfaceId}/model
string, string, string -> string
AddDesignSurfaceModel(id, surfaceIndex, model): string
Adds a new surface to an existing design. At the same time, this forces the IDs of all elements of the transferred surface to be replaced with new values to prevent a situation where elements with the same ID appear in the design.
POST /api/atoms/v1/designs/{id}/print-product/surfaces/model
string, int, string -> string
Example
Let's learn how you can solve a specific task, combining multiple designs into one. To do this, you need to complete the following general operations:
- Get a surface list from a design.
- Replace the surface contents.
- Add a new surface to a design.
- If needed, delete a surface.
Getting a surface list from a design
First, let's get the surface list.
IDesignAtomsPrintProductApiClient designAtomsPrintProductApiClient;
var designId = "DESIGN_ID_HERE";
// Load list of surfaces short descriptions.
var surfaces = await designAtomsPrintProductApiClient.GetDesignSurfacesAsync(designId);
To serialize the content of surfaces, iterate over the surfaces and load all their models.
List<object> surfaceModels = new List<object>();
foreach (var surface in surfaces)
{
var surfaceModel = await designAtomsPrintProductApiClient.GetDesignSurfaceModelAsync(
id: designId,
surfaceId: surface.Id);
surfaceModels.Add(surfaceModel);
}
Replacing the surface contents
For example, let's swap the first and the last surfaces.
await designAtomsPrintProductApiClient.ReplaceDesignSurfaceModelAsync(
id: designId,
surfaceId: surfaces.First().Id,
body: surfaceModels.Last());
await designAtomsPrintProductApiClient.ReplaceDesignSurfaceModelAsync(
id: designId,
surfaceId: surfaces.Last().Id,
body: surfaceModels.First());
// Now you can check that `surfaces` and `updatedSurfaces` are different.
// Load the updated list of surface short descriptions.
var updatedSurfaces = await designAtomsPrintProductApiClient.GetDesignSurfacesAsync(designId);
Warning
Replacement and addition of surfaces will result in the regeneration of all surface identifiers to prevent collisions. The GetDesignSurfacesAsync
operation is required to process the new or replaced surfaces properly.
Adding a new surface to design
Let's duplicate the first surface of the design.
// Load the first surface model.
var surfaceModel = await designAtomsPrintProductApiClient.GetDesignSurfaceModelAsync(
id: designId,
surfaceId: surfaces.First().Id);
// Insert a copy of the first surface to the end of design.
await designAtomsPrintProductApiClient.AddDesignSurfaceModelAsync(
id: designId,
surfaceIndex: surfaces.Last().Index + 1,
body: surfaceModels.First());
// Now you can check that `surfaces` and `updatedSurfaces` are different.
// Load the updated list of design surface short descriptions.
var updatedSurfaces = await designAtomsPrintProductApiClient.GetDesignSurfacesAsync(designId);
Warning
Replacement and addition of surfaces will result in the regeneration of all surface identifiers to prevent collisions. The GetDesignSurfacesAsync
operation is required to process the new or replaced surfaces properly.
Combining multiple designs into one
Now, let's look at how you can extract surfaces from one design and add them to another design.
var targetDesignId = "TARGET_DESIGN_ID_HERE";
var sourceDesignId = "SOURCE_DESIGN_ID_HERE";
// Load surface descriptions for both designs.
var targetDesignSurfaces = await designAtomsPrintProductApiClient.GetDesignSurfacesAsync(targetDesignId);
var sourceDesignSurfaces = await designAtomsPrintProductApiClient.GetDesignSurfacesAsync(sourceDesignId);
// Get the last surface index for the target design to insert source design surfaces at the end.
var lastTargetSurfaceIndex = targetDesignSurfaces.Last().Index;
// Load all surfaces of source design one by one and insert them to the last position of the target design.
foreach (var sourceDesignSurface in sourceDesignSurfaces)
{
lastTargetSurfaceIndex++;
var newSurface = await designAtomsPrintProductApiClient.GetDesignSurfaceModelAsync(
id: sourceDesignId,
surfaceId: sourceDesignSurface.Id);
// Now you can check that `surfaces` and `updatedSurfaces` are different.
await designAtomsPrintProductApiClient.AddDesignSurfaceModelAsync(
id: targetDesignId,
surfaceIndex: lastTargetSurfaceIndex,
body: newSurface);
}
Deleting
This is how you can delete a surface from a design, for example, the last one.
// Load list of design surfaces short descriptions.
var surfaces = await designAtomsPrintProductApiClient.GetDesignSurfacesAsync(designId);
// Remove the last surface from the design.
await designAtomsPrintProductApiClient.DeleteDesignSurfaceAsync(
id: newDesign.Id,
surfaceId: surfaces.Last().Id);
// Now you can check that `surfaces` and `updatedSurfaces` are different.
// Load the updated list of surface short descriptions.
var updatedSurfaces = await designAtomsPrintProductApiClient.GetDesignSurfacesAsync(designId);
Note
Print products without surfaces are valid, so remove surfaces carefully.
Working with print areas
The model
A DesignPrintAreaDto
object represents a print area and defines its unique ID, surface ID, size, and position relative to the surface.
{
"id": "string",
"name": "string",
"surfaceId": "string",
"size": {
"width": 0,
"height": 0
},
"position": {
"top": 0,
"left": 0
}
}
Methods
The official C# API Client provides the following methods.
GetDesignPrintAreas(id, surfaceId): DesignPrintAreaDto[]
Gets a list of print areas of the design.
GET /api/atoms/v1/designs/{id}/print-product/print-areas
-> DesignPrintAreaDto[]
DeleteDesignPrintArea(id, printAreaId): void
Deletes a print area.
DELETE /api/atoms/v1/designs/{id}/print-product/print-areas/{printAreaId}
GetDesignPrintAreaModel(id, printAreaId): string
Gets a serialized model of the print area.
GET /api/atoms/v1/designs/{id}/print-product/print-areas/{printAreaId}/model
-> string
UpdateDesignPrintAreaModel(id, printAreaId): string
Updates an existing print area based on data from another print area transferred as a serialized model.
PUT /api/atoms/v1/designs/{id}/print-product/print-areas/{printAreaId}/model
string -> string
AddDesignPrintAreaModel(id, surfaceId, printAreaModel): string
Adds a new print area to an existing design. The ID of the transferred print area will be updated with a new value to prevent elements with the same ID from appearing in the design.
POST /api/atoms/v1/designs/{id}/print-product/print-areas/model
string -> string
Example
Let's learn how you can add or remove print areas to a surface. To do this:
- Get a list of print areas in a selected surface.
- Serialized models of all print areas.
- Add a new print area to a surface.
- If needed, delete a print area.
Getting a print area list from a design
First, let's get a list of print areas of the first design surface.
IDesignAtomsPrintProductApiClient designAtomsPrintProductApiClient;
var designId = "DESIGN_ID_HERE";
var surfaces = await designAtomsPrintProductApiClient.GetDesignSurfacesAsync(designId);
var selectedSurface = surfaces.First();
// Load the print area descriptions. If `surfaceId` is null, all print areas of the design will be loaded.
var selectedSurfacePrintAreas = await designAtomsPrintProductApiClient.GetDesignPrintAreasAsync(
id: designId,
surfaceId: selectedSurface.Id);
To obtain serialized models of all print areas of the first design surface, iterate over the print areas and load all their models.
List<object> printAreaModels = new List<object>();
foreach (var printArea in selectedSurfacePrintAreas)
{
var printAreaModel = await designAtomsPrintProductApiClient.GetDesignSurfaceModelAsync(
id: designId,
printAreaId: printArea.Id);
printAreaModels.Add(printAreaModel);
}
Removing and restoring print areas
Now, let's remove all the print areas from the selected surface first, and then add them back to the surface.
List<object> printAreaModels = new List<object>();
// Load the list of print area descriptions. If `surfaceId` is null, all print areas of the design will be loaded.
var selectedSurfacePrintAreas = await designAtomsPrintProductApiClient.GetDesignPrintAreasAsync(
id: designId,
surfaceId: selectedSurface.Id);
// Iterate over print areas to load all their models.
foreach (var printArea in selectedSurfacePrintAreas)
{
var printAreaModel = await designAtomsPrintProductApiClient.GetDesignSurfaceModelAsync(
id: designId,
printAreaId: printArea.Id);
printAreaModels.Add(printAreaModel);
await designAtomsPrintProductApiClient.DeleteDesignPrintAreaAsync(
id: designId,
printAreaId: printArea.Id);
}
// Now you can check that `selectedSurfacePrintAreas` and `updatedSelectedSurfacePrintAreas` are different.
// Load the updated list of surface print areas descriptions. If `surfaceId` is null, all print areas of the design will be loaded.
var updatedSelectedSurfacePrintAreas = await designAtomsPrintProductApiClient.GetDesignPrintAreasAsync(
id: designId,
surfaceId: selectedSurface.Id);
// Restore removed print areas.
foreach (var printAreaModel in printAreaModels)
{
await designAtomsPrintProductApiClient.AddDesignPrintAreaModelAsync(
id: designId,
surfaceId: selectedSurface.Id,
body: printAreaModel);
}
// Load the updated list of surface print areas descriptions. If `surfaceId` is null, all print areas of the design will be loaded.
var restoredSelectedSurfacePrintAreas = await designAtomsPrintProductApiClient.GetDesignPrintAreasAsync(
id: designId,
surfaceId: selectedSurface.Id);
Warning
Replacement and addition of surfaces will result in the regeneration of all print area identifiers to prevent collisions. The GetDesignPrintAreasAsync
operation is required to process the new or replaced print areas properly.
Working with mockups
The model
You can add two types of mockups: up mockup, which is displayed above the design (sometimes it is called an overlay), and down mockup, which is displayed under the design (a substrate). You may add one down mockup, one up mockup, or both.
A DesignSurfaceMockupDto
object represents a mockup description and defines its position, size and resolution.
{
"position": {
"top": 0,
"left": 0
},
"size": {
"width": 0,
"height": 0
},
"surfaceId": "string",
"resolution": 0
}
Methods
The official C# API Client provides the following methods.
GetDesignSurfaceDownMockup(id, surfaceId): DesignSurfaceMockupDto
Gets information about the down mockup of a specified surface.
GET /api/atoms/v1/designs/{id}/print-product/surfaces/{surfaceId}/mockup-down
-> DesignSurfaceMockupDto
UpdateDesignSurfaceDownMockupContent(id, surfaceId): string
Replaces the down mockup of the specified surface.
PATCH /api/atoms/v1/designs/{id}/print-product/surfaces/{surfaceId}/mockup-down
{position.top: number, position.left: number, sourceFile: string($binary)} -> string
GetDesignSurfaceDownMockupContent(id, surfaceId): string($binary)
Gets the down mockup file of the specified surface of an existing design.
GET /api/atoms/v1/designs/{id}/print-product/surfaces/{surfaceId}/mockup-down/file
-> string($binary)
GetDesignSurfaceUpMockup(id, surfaceId): DesignSurfaceMockupDto
Gets information about the up mockup of the specified surface of an existing design.
GET /api/atoms/v1/designs/{id}/print-product/surfaces/{surfaceId}/mockup-up
-> DesignSurfaceMockupDto
UpdateDesignSurfaceUpMockupContent(id, surfaceId): string
Replaces the up mockup file of the specified surface of the existing design.
PATCH /api/atoms/v1/designs/{id}/print-product/surfaces/{surfaceId}/mockup-up
{ position.top: number, position.left: number, sourceFile: string($binary) } -> string
GetDesignSurfaceUpMockupContent(id, surfaceId): string($binary)
Gets the up mockup file of the specified surface of the existing design.
GET /api/atoms/v1/designs/{id}/print-product/surfaces/{surfaceId}/mockup-up/file
-> string($binary)
Example
Let's learn how you can change a down mockup for a surface. To do this:
- Get mockup parameters.
- Download a mockup.
- Update a mockup.
Getting the mockup parameters
First, let's try to obtain a down mockup parameters of the first design surface.
IDesignAtomsPrintProductApiClient designAtomsPrintProductApiClient;
var designId = "DESIGN_ID_HERE";
var surfaces = await designAtomsPrintProductApiClient.GetDesignSurfacesAsync(designId);
var selectedSurface = surfaces.First();
// Load down mockup parameters.
try
{
var selectedSurfaceDownMockup = await designAtomsPrintProductApiClient.GetDesignSurfaceDownMockupAsync(
id: designId,
surfaceId: selectedSurface.Id);
}
catch (ApiClientException e)
{
if (e.StatusCode == 404)
{
// Surface does not contain a down mockup.
// Create one with UpdateDesignSurfaceDownMockupAsync API.
}
}
Downloading a mockup as an image
To download a mockup image, first obtain its content and output it to a file stream. Let's download a mockup image defined for a selected surface.
try
{
var selectedSurfaceDownMockupContent = await designAtomsPrintProductApiClient.GetDesignSurfaceDownMockupContentAsync(
id: designId,
surfaceId: selectedSurface.Id);
var tempFilePath = Path.Combine(Environment.CurrentDirectory, "App_Data", $"{Guid.NewGuid().ToString()}.png");
using (var fileStream = File.Create(tempFilePath))
{
await selectedSurfaceDownMockupContent.Stream.CopyToAsync(fileStream);
}
}
catch (ApiClientException e)
{
if (e.StatusCode == 404)
{
// Surface does not contain a down mockup.
// Create one with UpdateDesignSurfaceDownMockupAsync API.
}
}
Updating a mockup image
This is how you can define a down mockup for the first surface of the design.
var surfaces = await designAtomsPrintProductApiClient.GetDesignSurfacesAsync(designId);
var selectedSurface = surfaces.First();
var mockupFilePath = Path.Combine(Environment.CurrentDirectory, "App_Data", "IMAGE_NAME");
using (var mockupFileStream = File.OpenRead(mockupFilePath))
{
await designAtomsPrintProductApiClient.UpdateDesignSurfaceDownMockupAsync(
id: designId,
position_top: 0,
position_left: 0,
surfaceId: selectedSurface.Id, sourceFile: new FileParameter(mockupFileStream));
}
To learn more about this functionality, you can see the API reference. If you encounter any problem, please contact our support team.