.NET image manipulation

Overview

Cloudinary is a cloud-based service that provides an end-to-end image management solution including uploads, storage, administration, image manipulation, and delivery.

After uploading images to Cloudinary, they can be made available to users of your web and mobile applications via dynamic URLs. With these simple URLs, you can tell Cloudinary to transform and manipulate your images. Cloudinary optimizes your images and automatically routes them through a fast CDN applying advanced caching techniques for optimal user experience.

Cloudinary can create transformed versions of your images either eagerly while uploading or on-the-fly when their url is first accessed. Transformed images are stored persistently in the cloud for future delivery through worldwide CDN edges. All image manipulations are performed automatically in the cloud. You don't need to install any image processing software such as ImageMagick, ImageMagick.NET, Magick.NET, AForge.NET or ImageResizer. Cloudinary will take care of all your .NET image manipulation needs.

Cloudinary also supports retrieving and manipulating your users' Facebook and Twitter social profile pictures.

In addition to images, Cloudinary can be used to manage non-image files. These can be uploaded to Cloudinary and delivered to your users as-is.

Cloudinary's .NET library simplifies the generation of image manipulation URLs. The library includes view helper methods for embedding images and transformed images in your web views.

For a full list of supported transformations and their usage, refer to Image transformation reference.

Display images

You can add images to your view using the BuildImageTag method of the Url class. This method generates the full image resource URL based on the given transformation parameters and adds the image to your HTML code:

For example, displaying the uploaded image with the sample public ID, while providing an alternate text:

@Model.Cloudinary.Api.UrlImgUp.BuildImageTag("sample.jpg", 
  new CloudinaryDotNet.StringDictionary("alt=Sample Image")
864x576 JPG (Scaled down)

Note: For .NET version v3.5 and earlier, the result should be wrapped with '@Html.Raw' for embedding HTML code in your view. For example:

@Html.Raw(@Model.Cloudinary.Api.UrlImgUp.BuildImageTag("sample.jpg", 
  new CloudinaryDotNet.StringDictionary("alt=Sample Image"))

If Cloud Name is configured to be demo, this code is equivalent to:

<img src='http://res.cloudinary.com/demo/image/upload/sample.jpg' alt='Sample Image'/>

You can use BuildImageTag to show transformed versions of your uploaded images by adding transformation instructions. For example, displaying the 'sample' image resized to fill a 100x150 area:

@Model.Cloudinary.Api.UrlImgUp.Transform(
  new Transformation().Width(100).Height(150).Crop("fill")).BuildImageTag("sample.jpg")
100x150 JPG

This is equivalent to:

<img src='http://res.cloudinary.com/demo/image/upload/c_fill,h_150,w_100/sample.jpg' 
     height='150' width='100'/>

Below you will find many additional examples of image manipulations for your .NET applications. For a full list of supported image manipulations, see Image transformations.

Image versions

By default, Cloudinary assigns a unique public ID to each uploaded image. Alternatively, you can define your own Public ID. If you upload an image with a Public ID that already exists, the file will be overridden. Still, the CDN may already contain a previously cached copy of the older image.

To force the CDN to display the latest uploaded image, you should add a version component to Cloudinary's URLs. The version value is returned by Cloudinary as part of the response of the upload API call, and is unique per upload. Adding the version component to URLs can be done by setting the Version parameter, for example:

@Model.Cloudinary.Api.UrlImgUp.Version("1315746344").BuildImageTag("sample.jpg")

Direct URL building

The BuildImageTag method generates an HTML image tag. In certain occasions, you might want to generate a transformation URL directly, without the containing image tag. You can do that by using the BuildUrl method of the Url class. Here are few examples:

string url = cloudinary.Api.UrlImgUp.Transform(
  new Transformation().Width(100).Height(150).Crop("fill"))
  .BuildUrl("sample.jpg");
  
// Result: "http://res.cloudinary.com/demo/image/upload/c_fill,h_150,w_100/sample.jpg"
string url = cloudinary.Api.UrlImgUp.ResourceType("raw")
  .BuildUrl("sample_spreadsheet.xls");
  
// Result: "http://res.cloudinary.com/demo/raw/upload/sample_spreadsheet.xls"

Secure HTTPS URLs

Cloudinary supports delivering images using HTTPS URLs. These images are delivered through a CDN as well. When using BuildImageTag in your view, this is done automatically for you, based on the request protocol of the page containing the image tag.

You can force BuildImageTag to always use HTTPS URLs by setting the Secure parameter to true, either globally or locally in each call to BuildImageTag or to BuildUrl. For example:

string tag = cloudinary.Api.UrlImgUp.Secure(true)
  .Transform(new Transformation().Width(100).Height(150).Crop("fill"))
  .BuildImageTag("sample.jpg");
  
// Result: 
//   "<img src='https://res.cloudinary.com/demo/image/upload/c_fill,h_150,w_100/sample.jpg' 
//         width='100' height='150'/>"
string url = cloudinary.Api.UrlImgUp.Secure(true)
  .Transform(new Transformation().Width(100).Height(150).Crop("fill"))
  .BuildUrl("sample.jpg");
  
// Result: "https://res.cloudinary.com/demo/image/upload/c_fill,h_150,w_100/sample.jpg"

Multiple CDN sub-domains

Browsers limit the number of download requests they perform concurrently from each unique domain. The lower concurrency means that downloading many images from a single web page might be somewhat slow. To overcome this limitation, Cloudinary supports downloading images from multiple sub-domains. This can results in a much improved web browsing speed.

You can enable downloads from multiple sub-domains by setting CSubDomain to true in each call to BuildImageTag:

cloudinary.Api.CSubDomain = true;
string tag = cloudinary.Api.UrlImgUp.CSubDomain(true)
  .BuildImageTag("sample.jpg");
  
// Result: "<img src='http://a4.res.cloudinary.com/demo/image/upload/sample.jpg'/>"

See this blog post for more details: Reduce site load time with multiple CDN sub-domains.

Error handling

Did you get a broken or an empty image when accessing a Cloudinary transformation URL? It might be a simple matter of using a wrong syntax. To understand more, check your X-Cld-Error HTTP response header. This is where Cloudinary reports the errors it encounters.

For example, trying to access the following URL:

http://res.cloudinary.com/demo/image/upload/w_abc/sample.jpg  

X-Cld-Error: Invalid width - abc

To view the X-Cld-Error header on the Chrome browser, select Developers Tools from the View menu. Then select the Network tab, refresh your page, click on the image name with the 400 status code and look for X-Cld-Error under Response Headers.

Resize, crop and thumbnails

Cloudinary makes it very simple to manipulate images. To show a transformed version of an uploaded image, simply provide transformation instructions as part of Cloudinary's image URLs.

For example, displaying the 'sample' image transformed to fill a 100x150 area:

@Model.Cloudinary.Api.UrlImgUp.Transform(
  new Transformation().Width(100).Height(150).Crop("fill"))
  .BuildImageTag("sample.jpg")
100x150 JPG

Note that you must provide the Crop parameter, for the resize or crop transformation to take effect. Without it, the image will be downloaded at its original dimensions and the browser will end up stretching or shrinking it on the client's side.

If you want to transform an uploaded image to a certain dimension and display it within your page in a different dimension, you can use SetHtmlWidth and SetHtmlHeight methods. Here's an example of the Cloudinary code and its equivalent image tag:

var trans = new Transformation().Width(100).Height(150).Crop("fill").SetHtmlWidth(50).SetHtmlHeight(75);
string tag = m_api.UrlImgUp.Transform(trans).BuildImageTag("sample.jpg");
100x150 PNG displayed as 50x75

This is equivalent to:

<img src='http://res.cloudinary.com/demo/image/upload/c_fill,h_150,w_100/sample.jpg' 
     height='75' width='50'/>

Scale mode

You can scale an image to an exact width and height by setting the Crop parameter to scale:

@Model.Cloudinary.Api.UrlImgUp.Transform(
  new Transformation().Width(100).Height(150).Crop("scale"))
  .BuildImageTag("sample.jpg")
100x150 JPG

Fit mode

Cloudinary's fit cropping mode (set Crop to fit) transforms the image to fit in a given rectangle while retaining its original proportions.

@Model.Cloudinary.Api.UrlImgUp.Transform(
  new Transformation().Width(100).Height(150).Crop("fit"))
  .BuildImageTag("sample.jpg")
100x67 JPG

Fill mode

Transform an image to fill specific dimensions completely while retaining its original proportions by setting Crop to fill. Only part of the original image might be visible if the required proportions are different than the original ones.

@Model.Cloudinary.Api.UrlImgUp.Transform(
  new Transformation().Width(100).Height(150).Crop("fill"))
  .BuildImageTag("sample.jpg")
100x150 JPG

By default, fill keeps the center of the image centered while cropping the image to fill the given dimensions. You can control this behavior by specifying the Gravity parameter. In the following example, gravity is set to South East.

@Model.Cloudinary.Api.UrlImgUp.Transform(
  new Transformation().Width(100).Height(150).Crop("fill").Gravity("south_east"))
  .BuildImageTag("sample.jpg")
100x150 JPG

Limit mode

The limit cropping mode is used to create an image that does not exceed the given width or height. If the original image is smaller than the given limits, the generated image will be identical to the original one. If the original is bigger than the given limits, it will be resized while retaining original proportions (similar to the 'fit' mode in this case).

@Model.Cloudinary.Api.UrlImgUp.Transform(
  new Transformation().Width(100).Height(150).Crop("limit"))
  .BuildImageTag("sample.jpg")
100x67 JPG

Pad mode

Cloudinary's pad cropping mode will resize the image to fill the given dimensions while retaining original proportions. You can specify 'Gravity' to select which portion of the original image will be used to fill the rectangle.

@Model.Cloudinary.Api.UrlImgUp.Transform(
  new Transformation().Width(100).Height(150).Crop("pad"))
  .BuildImageTag("sample.jpg")
100x150 JPG

Crop mode

The crop mode allows custom coordinates cropping based on a given Gravity parameter or a combination of X, Y, Width and Height parameters.

@Model.Cloudinary.Api.UrlImgUp.Transform(
  new Transformation().Width(300).Height(200).Crop("crop").X(355).Y(410))
  .BuildImageTag("brown_sheep.jpg")
300x200 JPG

See Crop Modes documentation for more details.

Percentage based resizing

You can change the dimensions of an image using relative percentage instead of absolute pixels. Simply use a decimal value to define the required size. You can provide only width or height instead of both and Cloudinary will maintain the aspect ratio.

For example, resizing the 'sample' image to 20% of its original size is done by setting the Width parameter to 0.2:

@Model.Cloudinary.Api.UrlImgUp.Transform(
  new Transformation().Width(0.2).Crop("scale"))
  .BuildImageTag("sample.jpg")
172x115 JPG

Increasing the image size is also possible. Setting the Width parameter to 1.5 will scale up the image by 150%:

@Model.Cloudinary.Api.UrlImgUp.Transform(
  new Transformation().Width(1.5).Crop("scale"))
  .BuildImageTag("sample.jpg")

Format conversion

To convert an uploaded image to a different image format, simply change its extension. For example, to convert an uploaded JPG file to GIF, add .gif suffix to the public ID of your image:

@Model.Cloudinary.Api.UrlImgUp.BuildImageTag("sample.gif")

You can also set the first parameter to be your image's public ID and specify the required image type by setting the Format to the required format:

@Model.Cloudinary.Api.UrlImgUp.Format("png").BuildImageTag("sample")

When delivering JPG photos, you might want to control the image quality (compression level). A lower quality means a much smaller file that can be viewed faster by your users. You can set the Quality parameter to a value between 1 to 100.

@Model.Cloudinary.Api.UrlImgUp.Transform(
  new Transformation().Quality(50))
  .BuildImageTag("sample.jpg")

Cloudinary supports generating thumbnails from PDF documents. You can use the Page parameter to generate a thumbnail of a specific page.

@Model.Cloudinary.Api.UrlImgUp.Transform(
  new Transformation().Width(100).Height(140).Crop("fill").Page(2))
  .BuildImageTag("multi_page_pdf.jpg")

The supported image formats are: jpg, png, gif, bmp, tiff, ico, pdf, eps, psd, webp, svg, wdp

Face detection

Cloudinary can detect one or more faces in a picture. This allows it to smartly crop photos or to manipulate images based on the automatically detected faces in them.

You can transform an image to fill given dimensions while keeping the photographed subject's face visible, by setting the Gravity parameter to face together with the fill crop mode:

@Model.Cloudinary.Api.UrlImgUp.Transform(
  new Transformation().Width(90).Height(90).Crop("fill").Gravity("face"))
  .BuildImageTag("face_top.jpg")

Creating a thumbnail centered on a detected face is done by specifying the thumb crop mode and selecting the face gravity:

@Model.Cloudinary.Api.UrlImgUp.Transform(
  new Transformation().Width(90).Height(90).Crop("thumb").Gravity("face"))
  .BuildImageTag("face_top.jpg")

You can also crop a certain region of an image based on face detection by selecting the crop mode and specifying the face gravity:

@Model.Cloudinary.Api.UrlImgUp.Transform(
  new Transformation().Width(50).Height(50).Crop("crop").Gravity("face"))
  .BuildImageTag("face_top.jpg")

In order to create a thumbanil covering multiple faces detected in a photo, set the Gravity parameter to faces.

@Model.Cloudinary.Api.UrlImgUp.Transform(
  new Transformation().Width(115).Height(136).Crop("thumb").Gravity("faces"))
  .BuildImageTag("couple.jpg")

You can even tell Cloudinary to automatically pixelate or blur all detected faces using the pixelate_faces and blur_faces effects. For example:

@Model.Cloudinary.Api.UrlImgUp.Transform(
  new Transformation().Width(150).Height(150).Effect("pixelate_faces").
    Crop("thumb").Gravity("faces"))
  .BuildImageTag("couple.jpg")

Facebook and Twitter profile pictures

The facebook, twitter_profile and twitter_name actions can be used to embed the social profile pictures of your users inside your web or mobile apps.

See Facebook profile pictures and Twitter profile pictures for more details.

You can manipulate the fetched profile pictures with any of Cloudinary's transformations so they perfectly match your graphic design. For example:

@Model.Cloudinary.Api.UrlImgUp
  .Action("facebook")
  .Transform(new Transformation().Width(90).Height(98).Crop("fill").Gravity("face"))
  .BuildImageTag("billclinton.jpg")
@Model.Cloudinary.Api.UrlImgUp
.Action("twitter_name")
.Transform(new Transformation().Width(90).Height(110).Crop("fill"))
.BuildImageTag("billclinton.jpg")

Social pictures were automatically fetched from the social network by Cloudinary, dynamically transformed and delivered cached through a CDN. Social profile pictures delivered by Cloudinary are refreshed once a week. This way, when your users switch their profile photos, the latest ones will be used. See Update and delete images for more details about image refreshing.

Displaying and transforming Google+ and Gravatar images is also supported.

Alter shape

Cloudinary supports various shape and content image manipulation features.

You can tell Cloudinary to round your images corners by setting the Radius parameter. For example, using a radius of 20 pixels:

@Model.Cloudinary.Api.UrlImgUp
  .Transform(new Transformation().Width(150).Height(100).Crop("fill").Radius(20))
  .BuildImageTag("sample.png")

Setting the Radius parameter to max makes an image circular (or elliptic):

@Model.Cloudinary.Api.UrlImgUp
  .Transform(new Transformation().Width(150).Height(150).Crop("thumb").Gravity("face").Radius("max"))
  .BuildImageTag("face_left.jpg")

You can rotate an image arbitrarily using the Angle parameter. For example, rotating by 25 degrees:

@Model.Cloudinary.Api.UrlImgUp
  .Transform(new Transformation().Angle(25))
  .BuildImageTag("sample.jpg")

Photos taken by digital cameras or smartphones usually include additional metadata (Exif, IPTC) that stores the orientation of the photo (e.g., portrait or landscape). Cloudinary can automatically rotate an image based on its orientation metadata by setting the Angle parameter to exif:

@Model.Cloudinary.Api.UrlImgUp
  .Transform(new Transformation().Angle("exif"))
  .BuildImageTag("sample.jpg")

See this blog post for more details about Automatic and custom image rotation.

Add a border around an image by calling one of overloads of the Border method using either two parameters (width in pixels and color (RGB of name)) or a string of the following format '4px_solid_rgb:553311'. For example:

@Model.Cloudinary.Api.UrlImgUp
  .Transform(new Transformation().Border(4, "#553311"))
  .BuildImageTag("sample.jpg")

See this blog post for more details about Adding borders to images.

You can make an image semi-transparent by setting the Opacity parameter to a value smaller than 100. For example, reducing opacity to 20%:

@Model.Cloudinary.Api.UrlImgUp
  .Transform(new Transformation().Opacity(20))
  .BuildImageTag("mountain.jpg")

See this blog post for additional details about Image opacity manipulation.

Chained transformations

Cloudinary supports powerful transformations. You can even combine multiple transformations together as part of a single transformation request, e.g. crop an image and add a border. In certain cases you may want to perform additional transformations on the result of a single transformation request. In order to do that, you can use Cloudinary's chained transformations.

To support chained transformations, Cloudinary's transformation URLs allows you to include multiple transformation components separated by '/'. Each transformation component is executed on the result of the previous one. Applying multiple transformations is done by using the Chain method of the Transformation class.

The following example first crops the original image to a specific set of custom coordinates and then transforms the result so it fills a 130x100 rectangle:

@Model.Cloudinary.Api.UrlImgUp
  .Transform(new Transformation().Width(300).Height(200).X(355).Y(410).Crop("crop")
  .Chain().Width(130).Height(100).Crop("fill"))
  .BuildImageTag("brown_sheep.jpg")
130x100 JPG

You can chain more than two transformations together. The following example applies 4 chained transformations: custom cropping to 300x200, fill to 130x100, rotate by 20 degrees and scale to 50%.

@Model.Cloudinary.Api.UrlImgUp
  .Transform(
    new CloudinaryDotNet.Transformation().Width(300).Height(200).X(355).Y(410).Crop("crop")
  .Chain().Width(130).Height(100).Crop("fill")
  .Chain().Andle(20)
  .Chain().Width(0.5).Crop("scale"))
  .BuildImageTag("brown_sheep.jpg")
78x69 JPG

Named transformations

Cloudinary's URLs include your full transformation instructions. If you use Cloudinary to apply complex transformations on your images, you may end up with somewhat long URLs.

To shorten your URLs, You can give any dynamic transformation a short name. You can name single transformations and also complex chained transformation. Named transformations can be created either in the Transformations web interface of our Management Console or using the Admin API.

Using named transformations also hides the exact manipulations you apply to your images, from your more advanced users.

The Named method of the Transformation class can be used to call a named transformation. For example, if you created a transformation named 'jpg_with_quality_30', you can apply it like this:

@Model.Cloudinary.Api.UrlImgUp
  .Transform(new Transformation().Named("jpg_with_quality_30"))
  .BuildImageTag("sample.jpg")
864x576 JPG (Scaled down)

Using a named transformation means that your image URLs will include this transformation's name. If you wish to modify this transformation, we recommended that you assign a different name to it, otherwise your image URLs won't change and older, cached copies of your transformed images might be returned by the CDN.

Cloudinary's web interface and Admin API can be used to delete and re-create a named transformation. This is currently limited to transformations with up to 100 derived images associations.

Filters and effects

Cloudinary supports applying various filters and effects on your pictures. A full list of filters and effects is available here. You may also want to look at the following blog posts:

Applying an effect can be done by setting the Effect parameter with the name of the required filter or effect. For example, applying the sepia effect:

@Model.Cloudinary.Api.UrlImgUp
  .Transform(new Transformation().Effect("sepia"))
  .BuildImageTag("sample.jpg")

Some effects can be controlled using a level attribute. The following example increases the image's saturation by 70% using the 'saturation:70' effect parameter:

@Model.Cloudinary.Api.UrlImgUp
  .Transform(new Transformation().Effect("saturation:70"))
  .BuildImageTag("sample.jpg")

Some effects accept a negative level value. The following example reduces the image's brightness level by 30%:

@Model.Cloudinary.Api.UrlImgUp
  .Transform(new Transformation().Effect("brightness:-30"))
  .BuildImageTag("sample.jpg")

Using chained transformations you can apply multiple effects to reach more complex results. The following example reduces the image's green level by 50%, increases brightness by 50% and applies a gradient fade effect:

@Model.Cloudinary.Api.UrlImgUp
  .Transform(new Transformation().Effect("green:-30")
  .Chain().Effect("brightness:50")
  .Chain().Effect("gradient_fade"))
  .BuildImageTag("sample.jpg")

Don't forget to browse through our always growing list of filters and effects.

Overlays, underlays and watermarks

Cloudinary supports generating new images by layer multiple images one on top of the other.

See this blog post for some examples: Adding watermarks, credits, badges and text overlays to images.

Add an overlay to an image by setting the Overlay parameter to the public ID of a previously uploaded image. All additional transformation parameters will be applied to the overlay images instead of the original one.

The following example adds the image with the public ID of 'cloudinary_icon' as an overlay to the image with the public ID of 'mountain'. The overlay is scaled to a width of 100 pixels before being appended.

@Model.Cloudinary.Api.UrlImgUp
  .Transform(new Transformation().Overlay("cloudinary_icon").Width(100))
  .BuildImageTag("mountain.jpg")

You can resize and position the overlay using the Gravity, X and Y parameters. You can also apply effects to the overlay and reduce its opacity to generate a watermark:

@Model.Cloudinary.Api.UrlImgUp
  .Transform(new Transformation()
    .Overlay("cloudinary_icon")
    .Width(80)
    .Opacity(50)
    .Gravity("south_east")
    .X(5)
    .Y(5)
    .Effect("brightness:200"))
  .BuildImageTag("mountain.jpg")

In case you want to place an image underneath another image, you can use the Underlay parameter. It is identical to Overlay but positions the layer below the main image.

@Model.Cloudinary.Api.UrlImgUp
  .Transform(new Transformation()
    .Underlay("site_bg.jpg").Width(80).Height(80).Effect("brightness:100")
    .Chain().Crop("fill").Height(80).Width(80))
  .BuildImageTag("smartphone.png")

Text layers

You can use any image as an overlay or underlay of another image. This includes uploaded images, social profile pictures and also text layers. In order to append text layers, first you need to define your custom text style using our API. See Text creation for more details.

Appending a text overlay is done by setting the Overlay parameter to 'text:STYLE-NAME:YOUR-TEXT'. Notice that the actual text should be URL encoded. The following example adds the "Hello World" text using the previously created 'bold_dark' font style:

@Model.Cloudinary.Api.UrlImgUp
  .Transform(new Transformation()
    .Overlay("text:bold_dark:Hello+World").Gravity("south_east").X(8).Y(8))
  .BuildImageTag("face_center.png")

You can customize the size and position of the text overlay as well as chaining additional layers:

@Model.Cloudinary.Api.UrlImgUp
  .Transform(new Transformation()
    .Overlay("text:bold_dark:Chained+Sample")
      .Gravity("south").Width(0.9).Y(5).Flags("relative")
    .Chain().Overlay("sample_watermark").Gravity("north_west").Y(5).X(23).Width(50))
  .BuildImageTag("face_center.png")

Fetch images

Most examples in this page discussed how your can show and manipulate images uploaded to Cloudinary, but Cloudinary can also manipulate images that already exist online, given their public HTTP URLs.

Cloudinary's "fetch" capabilities can automatically fetch images from remote URLs, store them persistently, transform them on-the-fly to match your requirements and deliver them to your users via Cloudinary's CDN. Cloudinary will even automatically refresh these images periodically in case the image in the original URL is modified.

See this blog post for more details: Delivering all your websites’ images through a CDN.

The following example embeds a remote image fetched by Cloudinary:

@Model.Cloudinary.Api.UrlImgUp.Action("fetch").BuildImageTag("http://upload.wikimedia.org/wikipedia/commons/4/46/Jennifer_Lawrence_at_the_83rd_Academy_Awards.jpg")

The same remote image is available in the following example after turning it into a 150x150 face detection based thumbnail with rounded corners:

@Model.Cloudinary.Api.UrlImgUp.Action("fetch")
  .Transform(new Transformation()
    .Width(150)
    .Height(150)
    .Crop("thumb")
    .Gravity("face")
    .Radius(20))
  .BuildImageTag("http://upload.wikimedia.org/wikipedia/commons/4/46/Jennifer_Lawrence_at_the_83rd_Academy_Awards.jpg")

You can convert the format of fetched images by setting the FetchFormat parameter to your preferred image type. The following example converted a remote PNG image to the JPG format:

@Model.Cloudinary.Api.UrlImgUp
  .Action("fetch")
  .Transform(new Transformation()
    .Width(150).Height(150).Crop("fill").FetchFormat("jpg")
  .BuildImageTag("http://upload.wikimedia.org/wikipedia/commons/e/e4/Globe.png")

More options

For a full reference of all supported transformations and their usage, refer to Transformations reference.