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.
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
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.
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).
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.
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.
Included in the SDK package are several sample projects that demonstrate various uses of the LayOut C API:
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 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.
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.
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.
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 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.
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.
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.
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 is different from how memory management works in the SketchUp C API due to the fact that in LayOut objects are reference counted.
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:
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.