LayOut C API
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
LayOut C API

The LayOut C API is an interface for reading and writing data to and from LayOut documents (.layout files). It can create new documents as well as read or modify existing documents.

LayOut C API Documentation

The documentation contains reference material for all functions, data structures, constants, and enumerations in the LayOut C API.

The online C API documentation can be found here: LayOut C API Online Documentation

Build and Release Considerations

Windows

Just like the SketchUp C API, the LayOut C API library for Windows is built using Microsoft Visual Studio 2019 (v142). It includes only 64-bit binaries. Building and releasing an application using the LayOut C API for Windows requires including the following DLLs which can be found in the SketchUp SDK for Windows:

pdflib.dll is the library used for generating pdf files, and is only required if the function LODocumentExportToPDF is used.

Also, the following C runtime DLLs must be included: msvcp140.dll and msvcr140.dll. Due to Microsoft binary compatability, the runtime DLLs remain named "-v140.dll". Alternatively the 64-bit Microsoft Visual C++ 2019 Redistributable Package can be used, which can be found here:

64-bit redistributable package

LayOut 2017, 2018, and 2019 for Windows were built using Visual Studio 2015 SP1, and so plugins should be built with the Platform Toolset set to Visual Studio 2015 (v140). Using a different Platform Toolset will likely cause the plugin to fail to load and cause LayOut to crash.

macOS

Just like the SketchUp C API, the LayOut C API library for Mac is built in Xcode 10.3 (targeting SDK 10.12). Building and releasing an application using the LayOut C API for macOS requires including both LayOutAPI.framework and SketchUpAPI.framework. The framework is 64-bit, and can be found in the SketchUp SDK for macOS.

Your Xcode project must set a Runpath Search Path for the LayOut C API to use. An app bundle would typically set the @rpath to @executable_path/../Frameworks, and a command-line tool would set the @rpath to @loader_path.

LayOut 2017, 2018, and 2019 for macOS were built using Xcode 7.2.1 (SDK 10.10).

Including LayOut C API Header Files

The LayOut C API functions are organized into header files by object type. For example, document.h contains all functions for creating, reading, and modifying LODocumentRef objects. For the best compilation speed, you should only include the LayOut C API header files that are needed in each source file. However, if simplicity is more important to you than compilation speed and you would rather just include one header file for the entire LayOut C API, you can just include layout.h.

Using the LayOut C API in a SketchUp Extension

The LayOut C API is included in the SketchUp application, meaning developers may utilize the LayOut C API from within their SketchUp extension without being required to include it in their extension. For an example of how to use the LayOut C API from within a SketchUp extension, see the RubyExampleCreateLayOut sample.

Example Projects

Included in the SDK package are several sample projects that demonstrate various uses of the LayOut C API:

Things To Know About The LayOut C API

LOInitialize and LOTerminate

Before calling any other LayOut C API functions, you must call LOInitialize exactly once to initialize the subsystems that the LayOut C API relies on. You must then call LOTerminate exactly once in order to shut down those subsystems and free any memory and resources they are using. If you forget to call LOInitialize before calling other functions of the LayOut C API, this may result in unpredictable failures or unhandled exceptions. It is important to note that you must release any document objects that you have created by calling LODocumentRelease before you call LOTerminate. If you call LODocumentRelease after calling LOTerminate, then LODocumentRelease may fail unpredictably or cause an unhandled exception to be thrown.

LayOut's Coordinate System

LayOut uses a 2D coordinate system for entities, whose origin is at the top-left corner of the page. The positive X axis extends to the right, and the positive Y axis extends downward. The units for all LOPoint2D objects are inches. All 2D lengths and radii are also specified in inches, unless the function documentation specifies otherwise.

LOPoint3D objects are used for certain functions that interact with a SketchUp model entity. LOPoint3D objects specify model-space coordinates within a SketchUp model and follow the same conventions as SUPoint3D within the SketchUp C API.

Shared and Non-Shared Groups

A group entity (see LOGroupRef) may not contain a mix of entities on both shared and non-shared layers. This is enforced when creating groups (see LOGroupCreate) as well as when moving entities into a group (see LOEntityMoveToGroup). Also, functions that cause an entity's sharedness to change may have the side effect of splitting groups (see LOEntityMoveToLayer). In general, these operations follow the same behavior as the LayOut application itself.

To traverse the hierarchical group structure of a document, it is important to understand that due to the rule about shared and non-shared groups, there are multiple group structures in a document. Each page has its own group structure that contains all of the entities on non-shared layers for that page (see LOPageGetNumberOfNonSharedEntities, LOPageGetNonSharedEntityAtIndex, and LOPageGetNonSharedEntities). Likewise, the document has a group structure that contains all of the entities on shared layers for the entire document (see LODocumentGetNumberOfSharedEntities, LODocumentGetSharedEntityAtIndex, and LODocumentGetSharedEntities). These functions provide access to a list of LOEntityRef objects at the top of the hierarchy. LOEntityGetEntityType can be used to check whether or not each entity is a group, and if it is, LOGroupFromEntity can be used to downcast the LOEntityRef to a LOGroupRef. From there, the LOGroupGetNumberOfEntities and LOGroupGetEntityAtIndex functions can be used to recursively traverse the group structure.

Iterating Over Entities On A Page

In cases where you need to iterate over all the entities that are visible on a page, including the entities on both shared and non-shared layers, you can use LOPageCreateEntityIterator to create a LOEntityIteratorRef object. When creating the iterator, you can specify whether or not the iterator should skip entities on hidden layers and/or locked layers. This iterator will visit entities in exactly the same order they are drawn by LayOut. LOPageCreateReverseEntityIterator will create a LOEntityIteratorRef object that visits the entities in reverse order, which is useful for hit detection as it will visit the top-most entity first.

Locked and Hidden Layers

In LayOut, the locked state of a layer is a document-wide setting, whereas the visible state of a layer is a per-page setting. LayOut has a rule that there must always be at least one unlocked, visible layer on every page. This rule is enforced by the LayOut C API by methods such as LOLayerSetLocked, LOPageSetLayerVisible, and LODocumentRemoveLayer.

There Must Be At Least One Page And One Layer In A Document

There must be at least one layer in a document, and there must be at least one page. LODocumentRemoveLayer and LODocumentRemovePage enforce this. Also, new documents created by LODocumentCreateEmpty will create a blank document that starts out with one layer and one page.

Explicit Transforms and Untransformed Bounds

Only some entities in LayOut have explicit transforms: LOEllipseRef, LOFormattedTextRef, LOImageRef, LORectangleRef and LOSketchUpModelRef will always have an explicit transform. These are the only entities that will return a valid value for LOEntityGetUntransformedBounds. LOEntityApplyTransform will work on any entity type, regardless of whether the entity has an explicit transform or not. It is important to note that LayOut will attempt to simplify the transform, which may affect both the untransformed bounds as well as the explicit transform of the entity. Due to this, it is likely that the transform returned by LOEntityGetExplicitTransform will be different than the one that was applied. Also, the bounds returned by LOEntityGetUntransformedBounds will likely be different from the entity's initial untransformed bounds.

Paths Must Have At Least Two Points

LOPathCreate and LOPathCreateBezier enforce the rule that every path must contain at least two points. This is because a 0 or 1-point path is not visible and cannot be selected (or deleted) in the LayOut application.

Text Cannot Be Empty

The functions for LOFormattedTextRef enforce the rule that the content of the text cannot be empty. This is because an empty text box is not visible and cannot be selected (or deleted) in the LayOut application.

Memory Management in the LayOut C API

Memory management in the LayOut C API is different from how memory management works in the SketchUp C API due to the fact that in LayOut objects are reference counted.

Creating And Managing References To Entities

When you create an object using one of the LO*Create*() functions, it will start out with a reference count of one. When you add the object to a document, the document itself adds one or more internal references to it and will increase the object's reference count. Likewise, when you remove the object from a document, this will release the internal reference(s), decreasing the object's reference count. Whenever an object's reference count reaches zero, it will be deleted.

You are responsible for calling LO*Release() exactly once for each object you create, regardless of whether or not the object was added to a document. Unlike the SU*Release() functions of the SketchUp C API, LO*Release() does not directly delete the object. It decrements the reference count, and only when the count reaches zero will the object be deleted. Furthermore, only when LO*Release() causes the object to be deleted will the reference be invalidated.

The following example illustrates what happens with reference counting when creating an object, adding it to a document, then removing it:

LORectangleRef rectangle_ref = SU_INVALID;
LOAxisAlignedRect2D rect_bounds = {{1.0, 2.0}, {4.0, 3.0}};
LORectangleCreate(&rectangle_ref, &rect_bounds);
// rectangle_ref now has a reference count of 1.
LOEntityRef entity_ref = LORectangleToEntity(rectangle_ref);
LODocumentAddEntityUsingIndexes(doc_ref, entity_ref, 0 /* layer */, 0 /* page */);
// The document now holds one or more internal references to rectangle_ref, so
// its reference count will be greater than 1.
LORectangleRelease(&rectangle_ref);
// rectangle_ref is still valid at this point, because the document still holds
// one or more references to it.
LODocumentRemoveEntity(doc_ref, &entity_ref);
// rectangle_ref is now invalid. Removing it from the document caused its
// reference count to reach 0, so the object was deleted.

The LO*AddReference() functions are provided for some but not all object types, so that you may add a reference to an existing object, incrementing its reference count. Use this when you need to prevent an object from being deleted. For example, it may be necessary to add a reference temporarily while removing an object from a document and adding it back to a document again, so that it is not deleted when it is removed. You may also call LO*AddReference() when you need to store a reference to an object, to prevent it from becoming a dangling pointer should it be removed from the document at a later time. You are responsible for calling LO*Release() exactly once for each call to LO*AddReference().

If you forget to call LO*Release() after calling LO*Create*() or LO*AddReference(), you will cause a memory leak. If you call LO*Release() too many times, then it will cause the object to be prematurely deleted, resulting in dangling pointers elsewhere. The most likely result of this is a memory access violation at some later time, within the code that tries to access the dangling pointer.

For these reasons, it is extremely important that you follow the simple rule that you must call LO*Release() exactly once for each call to LO*Create*() or LO*AddReference().

There are certain other functions besides LO*Release() that when called, may cause an object to be deleted due to it being removed from a document. Just like LO*Release(), the reference will be set to invalid upon returning, but only if the object was deleted as a result of the operation. Examples of this are LODocumentRemoveEntity and LOGroupUngroup.

Most of the LO*Create*() functions will create an object that is not yet in a document. One exception is the LOGroupCreate function, which will add the new group to the document if the objects being added to the group are themselves already in a document. Regardless of whether or not the group got added to a document, you still need to call LOGroupRelease after calling LOGroupCreate.

There are other functions that create objects and add them to the document in a single step, such as LODocumentAddLayer, LODocumentAddPage. It is not necessary to to release these objects, and in fact we do not provide any release functions for layer or page objects at all. When in doubt, just follow the rule that if you call a function that has "Create" in its name, then you are responsible for releasing the object that was created.

A transient object is one that is not actually added to a document. Transient objects are managed entirely by the caller. If you create a transient object, you are responsible for releasing it when you are done with it. Examples of transient object types are: LODictionaryRef, LOConnectionPointRef, LOEntityIteratorRef, LOEntityListRef, LOLayerListRef, LOPageListRef, LOStyleRef, and LOTypedValueRef.

SUStringRef - This object type is common between the SketchUp C API and LayOut C API. It is not reference counted, and the same rules apply when using SUStringRef with the LayOut C API as with the SketchUp C API. A string object is always created with one of the SUStringCreate*() functions. It is used to retrieve string data, like a name or a description, from an existing object (e.g., LOPageGetName). It must always be released using SUStringRelease or you will leak memory. There is no memory management concern with the API regarding string data passed to a function that sets something on an object (e.g., LOPageSetName), because a const char* is passed, not SUStringRef.