Overview
Livingdocs supports mechanisms to crop images and serve images in an optimised way to browsers and devices. This is done by uploading an original image and then cropping or resizing it on the fly through a SaaS image service depending
There are 3 parts involved in image management:
- The storage of the images Uploaded images are stored in a configurable key value store. Usually we recommend S3. The location in the key value store and the metadata of the image are also stored in the Media Library.
- The delivery through a SaaS image service
Livingdocs does not serve images to your end-users directly. For this you
can choose a service provider of your choice. In Livingdocs itself we have the
image-service
concept which simply is an url rewriter so that images get served through your SaaS provider. Currently we offer a pre-made image-service for imgix (Contact us to add other ones or do so yourself). - The render strategies to create HTML markup
Image markup can be rendered in different ways depending on what you want to achieve (e.g. an
img
tag with asrcset
attribute).
Storage
The storage of choice for Livingdocs is Amazon S3, the bucket is configured in the server like this:
server configuration:
mediaLibrary: {
images: {
// base url of the storage
publicUrl: 'https://livingdocs-images-dev.s3.amazonaws.com',
// Storage Configuration
storage: {
strategy: 's3',
prefix: 'images/' // optional, the storage key will be prefixed (Added in: release-2021-06)
config: {
bucket: 'livingdocs-images-development',
region: 'eu-central-1',
secretAccessKey: '****',
accessKeyId: '****'
}
}
}
}
You can configure your own image storage using the following configuration:
mediaLibrary: {
images: {
proxy: {
url: 'http://your-image-storage.com'
}
}
}
This will route image upload requests from Livingdocs to your chosen URL instead of S3. For details please see the reference documentation for the server configuration
Delivery
The delivery of the images is done through a URL-pattern-based web service. Out of the box, Livingdocs supports imgix (https://www.imgix.com/).
To use imgix you have to create an account at imgix and configure your server as described further down this document.
You can also use other image services as described at the end of this document.
Render Strategies
You can choose different render strategies how an image should be rendered in HTML.
Example of an image with a srcset attribute:
<img
src="https://livingdocs.imgix.net/2017/3/13/6ff-ef019.jpeg?w=1024"
srcset="https://livingdocs.imgix.net/2017/3/13/6ff-ef019.jpeg?w=1024 1024w,
https://livingdocs.imgix.net/2017/3/13/6ff-ef019.jpeg?w=620 620w"
sizes="100vw">
A div with a data
attribute meant to be processed by a client-side script.
<div
class="resrc"
data-src="https://livingdocs.imgix.net/2017/3/13/6ff-ef019.jpeg">
There are many more ways how an image can be rendered. And if you miss a strategy please get in touch.
Why image services? - Some Background
You might wonder why Livingdocs uses an image service to start with and does not just write the Amazon URL into an img
tag’s src
attribute. Two reasons mainly:
- cropping
- different image widths (sizes) for different devices
The first reason is easily explained. Livingdocs provides an image cropping tool (https://github.com/livingdocsIO/srcissors) in the Livingdocs editor that allows users to crop their images and change the aspect ratio. The Livingdocs server can not generate different versions of an image and deliver them. It gives this responsibility to a web service such as imgix. Currently, Livingdocs expects that a web service takes the cropping information in the form of a URL parameter (example of imgix: https://docs.imgix.com/apis/rendering/size/rect). This is the case for all services we know of.
The second reason for using an image service is a bit more involved. Put simply, you don’t want to download a 5 Megapixel image on your mobile phone. Instead you want a website to be “smart” and download the image in such a size that the resolution is perfect for your device but the size is just as large as need be. There is actually a whole community for this topic: https://responsiveimages.org/
Livingdocs support different strategies for responsive images:
srcset
images- script-based plugin
The first approach uses the an images srcset
attribute. A good article on the topic is found here: https://ericportis.com/posts/2014/srcset-sizes/ The srcset
attribute is supported by all major browsers. It is not supported in IE11 and below. Of course that does not mean that users can not see images on IE11 just that they are not responsive there.
Srcset only works for inline images (img
tag). If you use a background image, you can currently set a max width. Read here on why srcset does not work for background images.
The second approach expects you to provide a client-side Javascript that runs in the reader’s browser, detects the respective device size and requests the optimal image for this size. We can provide such a script based on the now out-of-service service rescr.it. For another image service you would need to provide your own script.
An Example of the whole process:
A user uploads an image to the Livingdocs editor, the corresponding file is stored on Amazon S3 and the URL to the Amazon file is available on the component as originalUrl
. The framework chooses the correct image service (e.g. imgix) for generating the correct HTML markup in the document that is shown in the Livingdocs editor. (The image service to be used is configured via selectedImageService
. Let’s say imgix
is configured.) The framework loads the imgix_image_service
and calls the set
method which will generate an img
tag with a URL that fits the imgix url specification (https://docs.imgix.com/setup/serving-images). The browser then renders the image by querying imgix for the respective image.
Configuring an image service
You need to configure your image service in the server. You add the configuration for one or more image services as well as the selected image service.
Below we’ll outline the configuration for Imgix.
Server Configuration
documents: {
selectedImageService: 'imgix',
imageServices: {
imgix: {
host: 'https://livingdocs-dev.imgix.net',
preferWebp: true, // default: true
secureToken: '<your-token>' // optional - Added in: release-2021-03
}
}
}
The selectedImageService
field tells Livingdocs which image service should be used.
The imageServices
contains the configurations for one or more image services.
You can in theory configure several images services in the server, but as of now only one can be used (the one specified in selectedImageService).
The host
is simply where your imgix images are served from.
If preferWebp
is set to true
Livingdocs will pass the auto=format
parameter (https://docs.imgix.com/apis/url/auto).
When the optional property secureToken
is set, the images are secured.
Backwards compatible image rendering server config
Until all your designs contain an mediaRendering
config you should
keep the server wide ‘backgroundImage’ and ‘srcSet’
imageServices configurations.
Server Config:
documents: {
selectedImageService: 'imgix',
imageServices: {
imgix: {
host: 'https://livingdocs-dev.imgix.net',
preferWebp: true,
backgroundImage: {
maxWidth: 2048
},
srcSet: {
defaultWidth: 1024,
widths: [
2048,
1024,
620,
320
],
sizes: ['100vw']
}
}
}
}
Images in the Metadata of a Document
Images in the metadata have a similar format to images in the document and also use the same image service.
Metadata fields of type li-image
will contain the srcset
in a specific crop
, but not in the root. The reason for this is simply that you normally only want teaser images in a certain crop and in the metadata definition it is not even possible to have a metadata image without a crop definition.
An example:
metadata: {
teaserImage: {
originalUrl: 'http://livingdocs-images-dev.s3.amazonaws.com/2017/3/29/56cac115-07ef-4421-9fbf-4c886d4543cd.jpeg',
url: 'https://livingdocs-dev.imgix.net/2017/3/29/56cac115-07ef-4421-9fbf-4c886d4543cd.jpeg?w=1024&auto=format',
width: 1760,
height: 791,
imageService: 'imgix',
crops: [
{
url: 'https://livingdocs-dev.imgix.net/2017/3/29/56cac115-07ef-4421-9fbf-4c886d4543cd.jpeg?rect=177%2C0%2C1406%2C791&w=1024&auto=format',
name: '16:9',
x: 177,
y: 0,
width: 1406,
height: 791,
srcSet: [
{
url: 'https://livingdocs-dev.imgix.net/2017/3/29/56cac115-07ef-4421-9fbf-4c886d4543cd.jpeg?rect=177%2C0%2C1406%2C791&w=2048&auto=format',
width: 2048
},
{
url: 'https://livingdocs-dev.imgix.net/2017/3/29/56cac115-07ef-4421-9fbf-4c886d4543cd.jpeg?rect=177%2C0%2C1406%2C791&w=1024&auto=format',
width: 1024
},
{
url: 'https://livingdocs-dev.imgix.net/2017/3/29/56cac115-07ef-4421-9fbf-4c886d4543cd.jpeg?rect=177%2C0%2C1406%2C791&w=620&auto=format',
width: 620
},
{
url: 'https://livingdocs-dev.imgix.net/2017/3/29/56cac115-07ef-4421-9fbf-4c886d4543cd.jpeg?rect=177%2C0%2C1406%2C791&w=320&auto=format',
width: 320
}
]
}
]
}
}
The srcset
information is useful when you want to have responsive images in your overview pages or embeds (doc-include
directives). In such a case you could render the doc-include
HTML in a server-side plugin (template). This plugin will not use the Livingdocs framework to render thus your configured srcset
will not be automatically applied. You can though use the information on the metadata field to apply it manually in the template.
An example template:
module.exports =
<a href="<%= article.url %>">
<div class="teaser__img-wrap">
<img class="teaser__img"
src="<%= article.teaserImage %>"
srcset="<%= article.srcSet %>"
sizes="(min-width: 1024px) 100vw, 50vw">
</div>
</a>
You apply the srcset
attribute manually in your doc-include
template.
Note also that we prefer here to write the sizes
attribute directly in the template. This makes sense since the template knows best how the responsive behavior should be.
Integrate your own image service
Important: You will have to add image services to both your server
and editor
.
An image service in Livingdocs is basically just a url transformer that
allows to proxy image requests through a ‘real’ image service like imgix and
write params to the url dynamically to define e.g. the width
or crop
of an image.
We support the setting of allowed or disabled mime types which defines which mime types the custom image service will handle but does not stop upload in the editor itself. This is for cases where you want to use different image services for different mime types.
Example of registering an image service:
const myImageService = {
name: 'myImageService',
hasBrowserPlugin: true
supportsCrop: true,
supportsWidth: true,
supportsVideoConversion: true, // default: false
// Either allowedMimeTypes or disabledMimeTypes can be defined.
// If you define both of them, disabledMimeTypes will be ignored.
// If neither is defined, all mime types will be handled by this service
allowedMimeTypes: ['image/jpeg', 'image/png'],
disabledMimeTypes: ['image/gif', 'image/webp'],
// This is not a real-world example. It just rewrites
// the url to go through myproxy.com and embeds the width
// and image url in the path of the generated url.
getUrl: function (imageUrl, {crop, width}) {
imageUrl = imageUrl || ''
return `https://myproxy.com/w${width}/${imageUrl}`
},
// Return an image url without any cropping applied. This is used
// e.g for the cropping interface. This is not a real-world example neither,
// it just rewrites the url to go through myproxy.com.
getUncroppedUrl: function (imageUrl) {
imageUrl = imageUrl || ''
return `https://myproxy.com/${imageUrl}`
}
}
const livingdocs = require('@livingdocs/framework')
livingdocs.imageServices.add(myImageService)
In practice we recommend to create a separate npm package to share your image service between a downstream server and editor:
const myImageService = require('@yourOrg/myImageService')
const livingdocs = require('@livingdocs/framework')
livingdocs.imageServices.add(myImageService)