LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/dgn - dgnwrite.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 325 843 38.6 %
Date: 2024-05-08 14:54:11 Functions: 13 24 54.2 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Microstation DGN Access Library
       4             :  * Purpose:  DGN Access functions related to writing DGN elements.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2002, Frank Warmerdam <warmerdam@pobox.com>
       9             :  * Copyright (c) 2011-2013, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * Permission is hereby granted, free of charge, to any person obtaining a
      12             :  * copy of this software and associated documentation files (the "Software"),
      13             :  * to deal in the Software without restriction, including without limitation
      14             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      15             :  * and/or sell copies of the Software, and to permit persons to whom the
      16             :  * Software is furnished to do so, subject to the following conditions:
      17             :  *
      18             :  * The above copyright notice and this permission notice shall be included
      19             :  * in all copies or substantial portions of the Software.
      20             :  *
      21             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      22             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      23             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      24             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      25             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      26             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      27             :  * DEALINGS IN THE SOFTWARE.
      28             :  ****************************************************************************/
      29             : 
      30             : #include "dgnlibp.h"
      31             : 
      32             : #include <cmath>
      33             : 
      34             : #include <algorithm>
      35             : 
      36             : static void DGNPointToInt(DGNInfo *psDGN, DGNPoint *psPoint,
      37             :                           unsigned char *pabyTarget);
      38             : 
      39             : /************************************************************************/
      40             : /*                          DGNResizeElement()                          */
      41             : /************************************************************************/
      42             : 
      43             : /**
      44             :  * Resize an existing element.
      45             :  *
      46             :  * If the new size is the same as the old nothing happens.
      47             :  *
      48             :  * Otherwise, the old element in the file is marked as deleted, and the
      49             :  * DGNElemCore.offset and element_id are set to -1 indicating that the
      50             :  * element should be written to the end of file when next written by
      51             :  * DGNWriteElement().  The internal raw data buffer is updated to the new
      52             :  * size.
      53             :  *
      54             :  * Only elements with "raw_data" loaded may be moved.
      55             :  *
      56             :  * In normal use the DGNResizeElement() call would be called on a previously
      57             :  * loaded element, and afterwards the raw_data would be updated before calling
      58             :  * DGNWriteElement().  If DGNWriteElement() isn't called after
      59             :  * DGNResizeElement() then the element will be lost having been marked as
      60             :  * deleted in its old position but never written at the new location.
      61             :  *
      62             :  * @param hDGN the DGN file on which the element lives.
      63             :  * @param psElement the element to alter.
      64             :  * @param nNewSize the desired new size of the element in bytes.  Must be
      65             :  * a multiple of 2.
      66             :  *
      67             :  * @return TRUE on success, or FALSE on error.
      68             :  */
      69             : 
      70           0 : int DGNResizeElement(DGNHandle hDGN, DGNElemCore *psElement, int nNewSize)
      71             : 
      72             : {
      73           0 :     DGNInfo *psDGN = (DGNInfo *)hDGN;
      74             : 
      75             :     /* -------------------------------------------------------------------- */
      76             :     /*      Check various conditions.                                       */
      77             :     /* -------------------------------------------------------------------- */
      78           0 :     if (psElement->raw_bytes == 0 || psElement->raw_bytes != psElement->size)
      79             :     {
      80           0 :         CPLError(CE_Failure, CPLE_AppDefined,
      81             :                  "Raw bytes not loaded, or not matching element size.");
      82           0 :         return FALSE;
      83             :     }
      84             : 
      85           0 :     if (nNewSize % 2 == 1)
      86             :     {
      87           0 :         CPLError(CE_Failure, CPLE_AppDefined,
      88             :                  "DGNResizeElement(%d): "
      89             :                  "can't change to odd (not divisible by two) size.",
      90             :                  nNewSize);
      91           0 :         return FALSE;
      92             :     }
      93             : 
      94           0 :     if (nNewSize == psElement->raw_bytes)
      95           0 :         return TRUE;
      96             : 
      97             :     /* -------------------------------------------------------------------- */
      98             :     /*      Mark the existing element as deleted if the element has to      */
      99             :     /*      move to the end of the file.                                    */
     100             :     /* -------------------------------------------------------------------- */
     101             : 
     102           0 :     if (psElement->offset != -1)
     103             :     {
     104           0 :         vsi_l_offset nOldFLoc = VSIFTellL(psDGN->fp);
     105             :         unsigned char abyLeader[2];
     106             : 
     107           0 :         if (VSIFSeekL(psDGN->fp, psElement->offset, SEEK_SET) != 0 ||
     108           0 :             VSIFReadL(abyLeader, sizeof(abyLeader), 1, psDGN->fp) != 1)
     109             :         {
     110           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     111             :                      "Failed seek or read when trying to mark existing\n"
     112             :                      "element as deleted in DGNResizeElement()\n");
     113           0 :             return FALSE;
     114             :         }
     115             : 
     116           0 :         abyLeader[1] |= 0x80;
     117             : 
     118           0 :         if (VSIFSeekL(psDGN->fp, psElement->offset, SEEK_SET) != 0 ||
     119           0 :             VSIFWriteL(abyLeader, sizeof(abyLeader), 1, psDGN->fp) != 1 ||
     120           0 :             VSIFSeekL(psDGN->fp, nOldFLoc, SEEK_SET) != 0)
     121             :         {
     122           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     123             :                      "Failed seek or write when trying to mark existing\n"
     124             :                      "element as deleted in DGNResizeElement()\n");
     125           0 :             return FALSE;
     126             :         }
     127             : 
     128           0 :         if (psElement->element_id != -1 && psDGN->index_built)
     129           0 :             psDGN->element_index[psElement->element_id].flags |= DGNEIF_DELETED;
     130             :     }
     131             : 
     132           0 :     psElement->offset = -1; /* move to end of file. */
     133           0 :     psElement->element_id = -1;
     134             : 
     135             :     /* -------------------------------------------------------------------- */
     136             :     /*      Set the new size information, and realloc the raw data buffer.  */
     137             :     /* -------------------------------------------------------------------- */
     138           0 :     psElement->size = nNewSize;
     139           0 :     psElement->raw_data =
     140           0 :         (unsigned char *)CPLRealloc(psElement->raw_data, nNewSize);
     141           0 :     psElement->raw_bytes = nNewSize;
     142             : 
     143             :     /* -------------------------------------------------------------------- */
     144             :     /*      Update the size information within the raw buffer.              */
     145             :     /* -------------------------------------------------------------------- */
     146           0 :     const int nWords = (nNewSize / 2) - 2;
     147             : 
     148           0 :     psElement->raw_data[2] = (unsigned char)(nWords % 256);
     149           0 :     psElement->raw_data[3] = (unsigned char)(nWords / 256);
     150             : 
     151           0 :     return TRUE;
     152             : }
     153             : 
     154             : /************************************************************************/
     155             : /*                          DGNWriteElement()                           */
     156             : /************************************************************************/
     157             : 
     158             : /**
     159             :  * Write element to file.
     160             :  *
     161             :  * Only elements with "raw_data" loaded may be written.  This should
     162             :  * include elements created with the various DGNCreate*() functions, and
     163             :  * those read from the file with the DGNO_CAPTURE_RAW_DATA flag turned on
     164             :  * with DGNSetOptions().
     165             :  *
     166             :  * The passed element is written to the indicated file.  If the
     167             :  * DGNElemCore.offset field is -1 then the element is written at the end of
     168             :  * the file (and offset/element are reset properly) otherwise the element
     169             :  * is written back to the location indicated by DGNElemCore.offset.
     170             :  *
     171             :  * If the element is added at the end of the file, and if an element index
     172             :  * has already been built, it will be updated to reference the new element.
     173             :  *
     174             :  * This function takes care of ensuring that the end-of-file marker is
     175             :  * maintained after the last element.
     176             :  *
     177             :  * @param hDGN the file to write the element to.
     178             :  * @param psElement the element to write.
     179             :  *
     180             :  * @return TRUE on success or FALSE in case of failure.
     181             :  */
     182             : 
     183         283 : int DGNWriteElement(DGNHandle hDGN, DGNElemCore *psElement)
     184             : 
     185             : {
     186         283 :     DGNInfo *psDGN = (DGNInfo *)hDGN;
     187             : 
     188             :     /* ==================================================================== */
     189             :     /*      If this element hasn't been positioned yet, place it at the     */
     190             :     /*      end of the file.                                                */
     191             :     /* ==================================================================== */
     192         283 :     if (psElement->offset == -1)
     193             :     {
     194             :         // We must have an index, in order to properly assign the
     195             :         // element id of the newly written element.  Ensure it is built.
     196         283 :         if (!psDGN->index_built)
     197          33 :             DGNBuildIndex(psDGN);
     198             : 
     199             :         // Read the current "last" element.
     200         283 :         if (!DGNGotoElement(hDGN, psDGN->element_count - 1))
     201           0 :             return FALSE;
     202             : 
     203         283 :         int nJunk = 0;
     204         283 :         if (!DGNLoadRawElement(psDGN, &nJunk, &nJunk))
     205           0 :             return FALSE;
     206             : 
     207             :         // Establish the position of the new element.
     208         283 :         psElement->offset = static_cast<int>(VSIFTellL(psDGN->fp));
     209         283 :         psElement->element_id = psDGN->element_count;
     210             : 
     211             :         // Grow element buffer if needed.
     212         283 :         if (psDGN->element_count == psDGN->max_element_count)
     213             :         {
     214           0 :             psDGN->max_element_count += 500;
     215             : 
     216           0 :             psDGN->element_index = (DGNElementInfo *)CPLRealloc(
     217           0 :                 psDGN->element_index,
     218           0 :                 psDGN->max_element_count * sizeof(DGNElementInfo));
     219             :         }
     220             : 
     221             :         // Set up the element info
     222         283 :         DGNElementInfo *psInfo = psDGN->element_index + psDGN->element_count;
     223         283 :         psInfo->level = (unsigned char)psElement->level;
     224         283 :         psInfo->type = (unsigned char)psElement->type;
     225         283 :         psInfo->stype = (unsigned char)psElement->stype;
     226         283 :         psInfo->offset = psElement->offset;
     227         283 :         if (psElement->complex)
     228           3 :             psInfo->flags = DGNEIF_COMPLEX;
     229             :         else
     230         280 :             psInfo->flags = 0;
     231             : 
     232         283 :         psDGN->element_count++;
     233             :     }
     234             : 
     235             :     /* -------------------------------------------------------------------- */
     236             :     /*      Write out the element.                                          */
     237             :     /* -------------------------------------------------------------------- */
     238         566 :     if (VSIFSeekL(psDGN->fp, psElement->offset, SEEK_SET) != 0 ||
     239         283 :         VSIFWriteL(psElement->raw_data, psElement->raw_bytes, 1, psDGN->fp) !=
     240             :             1)
     241             :     {
     242           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     243             :                  "Error seeking or writing new element of %d bytes at %d.",
     244             :                  psElement->offset, psElement->raw_bytes);
     245           0 :         return FALSE;
     246             :     }
     247             : 
     248         283 :     psDGN->next_element_id = psElement->element_id + 1;
     249             : 
     250             :     /* -------------------------------------------------------------------- */
     251             :     /*      Write out the end of file 0xffff marker (if we were             */
     252             :     /*      extending the file), but push the file pointer back before      */
     253             :     /*      this EOF when done.                                             */
     254             :     /* -------------------------------------------------------------------- */
     255         283 :     if (psDGN->next_element_id == psDGN->element_count)
     256             :     {
     257         283 :         const unsigned char abyEOF[2] = {0xff, 0xff};
     258             : 
     259         283 :         VSIFWriteL(abyEOF, 2, 1, psDGN->fp);
     260         283 :         VSIFSeekL(psDGN->fp, VSIFTellL(psDGN->fp) - 2, SEEK_SET);
     261             :     }
     262             : 
     263         283 :     return TRUE;
     264             : }
     265             : 
     266             : /************************************************************************/
     267             : /*                             DGNCreate()                              */
     268             : /************************************************************************/
     269             : 
     270             : /**
     271             :  * Create new DGN file.
     272             :  *
     273             :  * This function will create a new DGN file based on the provided seed
     274             :  * file, and return a handle on which elements may be read and written.
     275             :  *
     276             :  * The following creation flags may be passed:
     277             :  * <ul>
     278             :  * <li> DGNCF_USE_SEED_UNITS: The master and subunit resolutions and names
     279             :  * from the seed file will be used in the new file.  The nMasterUnitPerSubUnit,
     280             :  * nUORPerSubUnit, pszMasterUnits, and pszSubUnits arguments will be ignored.
     281             :  * <li> DGNCF_USE_SEED_ORIGIN: The origin from the seed file will be used
     282             :  * and the X, Y and Z origin passed into the call will be ignored.
     283             :  * <li> DGNCF_COPY_SEED_FILE_COLOR_TABLE: Should the first color table occurring
     284             :  * in the seed file also be copied?
     285             :  * <li> DGNCF_COPY_WHOLE_SEED_FILE: By default only the first three elements
     286             :  * (TCB, Digitizer Setup and Level Symbology) are copied from the seed file.
     287             :  * If this flag is provided the entire seed file is copied verbatim (with the
     288             :  * TCB origin and units possibly updated).
     289             :  * </ul>
     290             :  *
     291             :  * @param pszNewFilename the filename to create.  If it already exists
     292             :  * it will be overwritten.
     293             :  * @param pszSeedFile the seed file to copy header from.
     294             :  * @param nCreationFlags An ORing of DGNCF_* flags that are to take effect.
     295             :  * @param dfOriginX the X origin for the file.
     296             :  * @param dfOriginY the Y origin for the file.
     297             :  * @param dfOriginZ the Z origin for the file.
     298             :  * @param nSubUnitsPerMasterUnit the number of subunits in one master unit.
     299             :  * @param nUORPerSubUnit the number of UOR (units of resolution) per subunit.
     300             :  * @param pszMasterUnits the name of the master units (2 characters).
     301             :  * @param pszSubUnits the name of the subunits (2 characters).
     302             :  */
     303             : 
     304          34 : DGNHandle DGNCreate(const char *pszNewFilename, const char *pszSeedFile,
     305             :                     int nCreationFlags, double dfOriginX, double dfOriginY,
     306             :                     double dfOriginZ, int nSubUnitsPerMasterUnit,
     307             :                     int nUORPerSubUnit, const char *pszMasterUnits,
     308             :                     const char *pszSubUnits)
     309             : 
     310             : {
     311             :     /* -------------------------------------------------------------------- */
     312             :     /*      Open output file.                                               */
     313             :     /* -------------------------------------------------------------------- */
     314          34 :     VSILFILE *fpNew = VSIFOpenL(pszNewFilename, "wb");
     315          34 :     if (fpNew == nullptr)
     316             :     {
     317           1 :         CPLError(CE_Failure, CPLE_OpenFailed, "Failed to open output file: %s",
     318             :                  pszNewFilename);
     319           1 :         return nullptr;
     320             :     }
     321             : 
     322             :     /* -------------------------------------------------------------------- */
     323             :     /*      Open seed file, and read TCB element.                           */
     324             :     /* -------------------------------------------------------------------- */
     325          33 :     DGNInfo *psSeed = (DGNInfo *)DGNOpen(pszSeedFile, FALSE);
     326          33 :     if (psSeed == nullptr)
     327             :     {
     328           0 :         VSIFCloseL(fpNew);
     329           0 :         return nullptr;
     330             :     }
     331             : 
     332          33 :     DGNSetOptions(psSeed, DGNO_CAPTURE_RAW_DATA);
     333             : 
     334          33 :     DGNElemCore *psSrcTCB = DGNReadElement(psSeed);
     335             : 
     336          33 :     CPLAssert(psSrcTCB->raw_bytes >= 1536);
     337             : 
     338             :     /* -------------------------------------------------------------------- */
     339             :     /*      Modify TCB appropriately for the output file.                   */
     340             :     /* -------------------------------------------------------------------- */
     341          33 :     GByte *pabyRawTCB = static_cast<GByte *>(CPLMalloc(psSrcTCB->raw_bytes));
     342             : 
     343          33 :     memcpy(pabyRawTCB, psSrcTCB->raw_data, psSrcTCB->raw_bytes);
     344             : 
     345          33 :     if (!(nCreationFlags & DGNCF_USE_SEED_UNITS))
     346             :     {
     347          33 :         memcpy(pabyRawTCB + 1120, pszMasterUnits, 2);
     348          33 :         memcpy(pabyRawTCB + 1122, pszSubUnits, 2);
     349             : 
     350          33 :         DGN_WRITE_INT32(nUORPerSubUnit, pabyRawTCB + 1116);
     351          33 :         DGN_WRITE_INT32(nSubUnitsPerMasterUnit, pabyRawTCB + 1112);
     352             :     }
     353             :     else
     354             :     {
     355           0 :         nUORPerSubUnit = DGN_INT32(pabyRawTCB + 1116);
     356           0 :         nSubUnitsPerMasterUnit = DGN_INT32(pabyRawTCB + 1112);
     357             :     }
     358             : 
     359          33 :     if (!(nCreationFlags & DGNCF_USE_SEED_ORIGIN))
     360             :     {
     361          33 :         dfOriginX *= (nUORPerSubUnit * nSubUnitsPerMasterUnit);
     362          33 :         dfOriginY *= (nUORPerSubUnit * nSubUnitsPerMasterUnit);
     363          33 :         dfOriginZ *= (nUORPerSubUnit * nSubUnitsPerMasterUnit);
     364             : 
     365          33 :         memcpy(pabyRawTCB + 1240, &dfOriginX, 8);
     366          33 :         memcpy(pabyRawTCB + 1248, &dfOriginY, 8);
     367          33 :         memcpy(pabyRawTCB + 1256, &dfOriginZ, 8);
     368             : 
     369          33 :         IEEE2DGNDouble(pabyRawTCB + 1240);
     370          33 :         IEEE2DGNDouble(pabyRawTCB + 1248);
     371          33 :         IEEE2DGNDouble(pabyRawTCB + 1256);
     372             :     }
     373             : 
     374             :     /* -------------------------------------------------------------------- */
     375             :     /*      Write TCB and EOF to new file.                                  */
     376             :     /* -------------------------------------------------------------------- */
     377          33 :     VSIFWriteL(pabyRawTCB, psSrcTCB->raw_bytes, 1, fpNew);
     378          33 :     CPLFree(pabyRawTCB);
     379             : 
     380          33 :     unsigned char abyEOF[2] = {0xff, 0xff};
     381             : 
     382          33 :     VSIFWriteL(abyEOF, 2, 1, fpNew);
     383             : 
     384          33 :     DGNFreeElement(psSeed, psSrcTCB);
     385             : 
     386             :     /* -------------------------------------------------------------------- */
     387             :     /*      Close and re-open using DGN API.                                */
     388             :     /* -------------------------------------------------------------------- */
     389          33 :     VSIFCloseL(fpNew);
     390             : 
     391          33 :     DGNInfo *psDGN = (DGNInfo *)DGNOpen(pszNewFilename, TRUE);
     392             : 
     393             :     /* -------------------------------------------------------------------- */
     394             :     /*      Now copy over elements according to options in effect.          */
     395             :     /* -------------------------------------------------------------------- */
     396          33 :     DGNElemCore *psSrcElement = nullptr;
     397          33 :     DGNElemCore *psDstElement = nullptr;
     398             : 
     399         270 :     while ((psSrcElement = DGNReadElement(psSeed)) != nullptr)
     400             :     {
     401         237 :         if ((nCreationFlags & DGNCF_COPY_WHOLE_SEED_FILE) ||
     402           0 :             (psSrcElement->stype == DGNST_COLORTABLE &&
     403           0 :              nCreationFlags & DGNCF_COPY_SEED_FILE_COLOR_TABLE) ||
     404           0 :             psSrcElement->element_id <= 2)
     405             :         {
     406         237 :             psDstElement = DGNCloneElement(psSeed, psDGN, psSrcElement);
     407         237 :             DGNWriteElement(psDGN, psDstElement);
     408         237 :             DGNFreeElement(psDGN, psDstElement);
     409             :         }
     410             : 
     411         237 :         DGNFreeElement(psSeed, psSrcElement);
     412             :     }
     413             : 
     414          33 :     DGNClose(psSeed);
     415             : 
     416          33 :     return psDGN;
     417             : }
     418             : 
     419             : /************************************************************************/
     420             : /*                          DGNCloneElement()                           */
     421             : /************************************************************************/
     422             : 
     423             : /**
     424             :  * Clone a retargeted element.
     425             :  *
     426             :  * Creates a copy of an element in a suitable form to write to a
     427             :  * different file than that it was read from.
     428             :  *
     429             :  * NOTE: At this time the clone operation will fail if the source
     430             :  * and destination file have a different origin or master/sub units.
     431             :  *
     432             :  * @param hDGNSrc the source file (from which psSrcElement was read).
     433             :  * @param hDGNDst the destination file (to which the returned element may be
     434             :  * written).
     435             :  * @param psSrcElement the element to be cloned (from hDGNSrc).
     436             :  *
     437             :  * @return NULL on failure, or an appropriately modified copy of
     438             :  * the source element suitable to write to hDGNDst.
     439             :  */
     440             : 
     441         237 : DGNElemCore *DGNCloneElement(CPL_UNUSED DGNHandle hDGNSrc, DGNHandle hDGNDst,
     442             :                              DGNElemCore *psSrcElement)
     443             : 
     444             : {
     445         237 :     DGNElemCore *psClone = nullptr;
     446             : 
     447         237 :     DGNLoadTCB(hDGNDst);
     448             : 
     449             :     /* -------------------------------------------------------------------- */
     450             :     /*      Per structure specific copying.  The core is fixed up later.    */
     451             :     /* -------------------------------------------------------------------- */
     452         237 :     if (psSrcElement->stype == DGNST_CORE)
     453             :     {
     454         237 :         psClone = static_cast<DGNElemCore *>(CPLMalloc(sizeof(DGNElemCore)));
     455         237 :         memcpy(psClone, psSrcElement, sizeof(DGNElemCore));
     456             :     }
     457           0 :     else if (psSrcElement->stype == DGNST_MULTIPOINT)
     458             :     {
     459           0 :         DGNElemMultiPoint *psSrcMP = (DGNElemMultiPoint *)psSrcElement;
     460             : 
     461           0 :         const size_t nSize = sizeof(DGNElemMultiPoint) +
     462           0 :                              sizeof(DGNPoint) * (psSrcMP->num_vertices - 1);
     463             : 
     464             :         DGNElemMultiPoint *psMP =
     465           0 :             static_cast<DGNElemMultiPoint *>(CPLMalloc(nSize));
     466           0 :         memcpy(psMP, psSrcElement, nSize);
     467             : 
     468           0 :         psClone = (DGNElemCore *)psMP;
     469             :     }
     470           0 :     else if (psSrcElement->stype == DGNST_ARC)
     471             :     {
     472             :         DGNElemArc *psArc =
     473           0 :             static_cast<DGNElemArc *>(CPLMalloc(sizeof(DGNElemArc)));
     474           0 :         memcpy(psArc, psSrcElement, sizeof(DGNElemArc));
     475             : 
     476           0 :         psClone = (DGNElemCore *)psArc;
     477             :     }
     478           0 :     else if (psSrcElement->stype == DGNST_TEXT)
     479             :     {
     480           0 :         DGNElemText *psSrcText = (DGNElemText *)psSrcElement;
     481           0 :         const size_t nSize = sizeof(DGNElemText) + strlen(psSrcText->string);
     482             : 
     483           0 :         DGNElemText *psText = static_cast<DGNElemText *>(CPLMalloc(nSize));
     484           0 :         memcpy(psText, psSrcElement, nSize);
     485             : 
     486           0 :         psClone = (DGNElemCore *)psText;
     487             :     }
     488           0 :     else if (psSrcElement->stype == DGNST_TEXT_NODE)
     489             :     {
     490             :         DGNElemTextNode *psNode =
     491           0 :             static_cast<DGNElemTextNode *>(CPLMalloc(sizeof(DGNElemTextNode)));
     492           0 :         memcpy(psNode, psSrcElement, sizeof(DGNElemTextNode));
     493             : 
     494           0 :         psClone = (DGNElemCore *)psNode;
     495             :     }
     496           0 :     else if (psSrcElement->stype == DGNST_COMPLEX_HEADER)
     497             :     {
     498             :         DGNElemComplexHeader *psCH = static_cast<DGNElemComplexHeader *>(
     499           0 :             CPLMalloc(sizeof(DGNElemComplexHeader)));
     500           0 :         memcpy(psCH, psSrcElement, sizeof(DGNElemComplexHeader));
     501             : 
     502           0 :         psClone = (DGNElemCore *)psCH;
     503             :     }
     504           0 :     else if (psSrcElement->stype == DGNST_COLORTABLE)
     505             :     {
     506             :         DGNElemColorTable *psCT = static_cast<DGNElemColorTable *>(
     507           0 :             CPLMalloc(sizeof(DGNElemColorTable)));
     508           0 :         memcpy(psCT, psSrcElement, sizeof(DGNElemColorTable));
     509             : 
     510           0 :         psClone = (DGNElemCore *)psCT;
     511             :     }
     512           0 :     else if (psSrcElement->stype == DGNST_TCB)
     513             :     {
     514             :         DGNElemTCB *psTCB =
     515           0 :             static_cast<DGNElemTCB *>(CPLMalloc(sizeof(DGNElemTCB)));
     516           0 :         memcpy(psTCB, psSrcElement, sizeof(DGNElemTCB));
     517             : 
     518           0 :         psClone = (DGNElemCore *)psTCB;
     519             :     }
     520           0 :     else if (psSrcElement->stype == DGNST_CELL_HEADER)
     521             :     {
     522             :         DGNElemCellHeader *psCH = static_cast<DGNElemCellHeader *>(
     523           0 :             CPLMalloc(sizeof(DGNElemCellHeader)));
     524           0 :         memcpy(psCH, psSrcElement, sizeof(DGNElemCellHeader));
     525             : 
     526           0 :         psClone = (DGNElemCore *)psCH;
     527             :     }
     528           0 :     else if (psSrcElement->stype == DGNST_CELL_LIBRARY)
     529             :     {
     530             :         DGNElemCellLibrary *psCL = static_cast<DGNElemCellLibrary *>(
     531           0 :             CPLMalloc(sizeof(DGNElemCellLibrary)));
     532           0 :         memcpy(psCL, psSrcElement, sizeof(DGNElemCellLibrary));
     533             : 
     534           0 :         psClone = (DGNElemCore *)psCL;
     535             :     }
     536           0 :     else if (psSrcElement->stype == DGNST_TAG_VALUE)
     537             :     {
     538             :         DGNElemTagValue *psTV =
     539           0 :             static_cast<DGNElemTagValue *>(CPLMalloc(sizeof(DGNElemTagValue)));
     540           0 :         memcpy(psTV, psSrcElement, sizeof(DGNElemTagValue));
     541             : 
     542           0 :         if (psTV->tagType == 1)
     543           0 :             psTV->tagValue.string = CPLStrdup(psTV->tagValue.string);
     544             : 
     545           0 :         psClone = (DGNElemCore *)psTV;
     546             :     }
     547           0 :     else if (psSrcElement->stype == DGNST_TAG_SET)
     548             :     {
     549             :         DGNElemTagSet *psTS =
     550           0 :             static_cast<DGNElemTagSet *>(CPLMalloc(sizeof(DGNElemTagSet)));
     551           0 :         memcpy(psTS, psSrcElement, sizeof(DGNElemTagSet));
     552             : 
     553           0 :         psTS->tagSetName = CPLStrdup(psTS->tagSetName);
     554             : 
     555             :         DGNTagDef *pasTagList = static_cast<DGNTagDef *>(
     556           0 :             CPLMalloc(sizeof(DGNTagDef) * psTS->tagCount));
     557           0 :         memcpy(pasTagList, psTS->tagList, sizeof(DGNTagDef) * psTS->tagCount);
     558             : 
     559           0 :         for (int iTag = 0; iTag < psTS->tagCount; iTag++)
     560             :         {
     561           0 :             pasTagList[iTag].name = CPLStrdup(pasTagList[iTag].name);
     562           0 :             pasTagList[iTag].prompt = CPLStrdup(pasTagList[iTag].prompt);
     563           0 :             if (pasTagList[iTag].type == 1)
     564           0 :                 pasTagList[iTag].defaultValue.string =
     565           0 :                     CPLStrdup(pasTagList[iTag].defaultValue.string);
     566             :         }
     567             : 
     568           0 :         psTS->tagList = pasTagList;
     569           0 :         psClone = (DGNElemCore *)psTS;
     570             :     }
     571           0 :     else if (psSrcElement->stype == DGNST_CONE)
     572             :     {
     573             :         DGNElemCone *psCone =
     574           0 :             static_cast<DGNElemCone *>(CPLMalloc(sizeof(DGNElemCone)));
     575           0 :         memcpy(psCone, psSrcElement, sizeof(DGNElemCone));
     576             : 
     577           0 :         psClone = (DGNElemCore *)psCone;
     578             :     }
     579           0 :     else if (psSrcElement->stype == DGNST_BSPLINE_SURFACE_HEADER)
     580             :     {
     581             :         DGNElemBSplineSurfaceHeader *psSurface =
     582             :             static_cast<DGNElemBSplineSurfaceHeader *>(
     583           0 :                 CPLMalloc(sizeof(DGNElemBSplineSurfaceHeader)));
     584           0 :         memcpy(psSurface, psSrcElement, sizeof(DGNElemBSplineSurfaceHeader));
     585             : 
     586           0 :         psClone = (DGNElemCore *)psSurface;
     587             :     }
     588           0 :     else if (psSrcElement->stype == DGNST_BSPLINE_CURVE_HEADER)
     589             :     {
     590             :         DGNElemBSplineCurveHeader *psCurve =
     591             :             static_cast<DGNElemBSplineCurveHeader *>(
     592           0 :                 CPLMalloc(sizeof(DGNElemBSplineCurveHeader)));
     593           0 :         memcpy(psCurve, psSrcElement, sizeof(DGNElemBSplineCurveHeader));
     594             : 
     595           0 :         psClone = (DGNElemCore *)psCurve;
     596             :     }
     597           0 :     else if (psSrcElement->stype == DGNST_BSPLINE_SURFACE_BOUNDARY)
     598             :     {
     599           0 :         DGNElemBSplineSurfaceBoundary *psSrcBSB =
     600             :             (DGNElemBSplineSurfaceBoundary *)psSrcElement;
     601             : 
     602           0 :         const size_t nSize = sizeof(DGNElemBSplineSurfaceBoundary) +
     603           0 :                              sizeof(DGNPoint) * (psSrcBSB->numverts - 1);
     604             : 
     605             :         DGNElemBSplineSurfaceBoundary *psBSB =
     606           0 :             static_cast<DGNElemBSplineSurfaceBoundary *>(CPLMalloc(nSize));
     607           0 :         memcpy(psBSB, psSrcElement, nSize);
     608             : 
     609           0 :         psClone = (DGNElemCore *)psBSB;
     610             :     }
     611           0 :     else if (psSrcElement->stype == DGNST_KNOT_WEIGHT)
     612             :     {
     613             :         // FIXME: Is it OK to assume that the # of elements corresponds
     614             :         // directly to the element size? kintel 20051218.
     615           0 :         const int numelems =
     616           0 :             (psSrcElement->size - 36 - psSrcElement->attr_bytes) / 4;
     617             : 
     618             :         /* DGNElemKnotWeight *psSrcArray = (DGNElemKnotWeight *) psSrcElement;
     619             :          */
     620             : 
     621           0 :         const size_t nSize =
     622           0 :             sizeof(DGNElemKnotWeight) + sizeof(long) * (numelems - 1);
     623             : 
     624             :         DGNElemKnotWeight *psArray =
     625           0 :             static_cast<DGNElemKnotWeight *>(CPLMalloc(nSize));
     626           0 :         memcpy(psArray, psSrcElement, nSize);
     627             : 
     628           0 :         psClone = (DGNElemCore *)psArray;
     629             :     }
     630           0 :     else if (psSrcElement->stype == DGNST_SHARED_CELL_DEFN)
     631             :     {
     632             :         DGNElemSharedCellDefn *psCH = static_cast<DGNElemSharedCellDefn *>(
     633           0 :             CPLMalloc(sizeof(DGNElemSharedCellDefn)));
     634           0 :         memcpy(psCH, psSrcElement, sizeof(DGNElemSharedCellDefn));
     635             : 
     636           0 :         psClone = (DGNElemCore *)psCH;
     637             :     }
     638             :     else
     639             :     {
     640           0 :         CPLAssert(false);
     641             :         return nullptr;
     642             :     }
     643             : 
     644             :     /* -------------------------------------------------------------------- */
     645             :     /*      Copy core raw data, and attributes.                             */
     646             :     /* -------------------------------------------------------------------- */
     647         237 :     if (psClone->raw_bytes != 0)
     648             :     {
     649         237 :         psClone->raw_data =
     650         237 :             static_cast<unsigned char *>(CPLMalloc(psClone->raw_bytes));
     651         237 :         memcpy(psClone->raw_data, psSrcElement->raw_data, psClone->raw_bytes);
     652             :     }
     653             : 
     654         237 :     if (psClone->attr_bytes != 0)
     655             :     {
     656           0 :         psClone->attr_data =
     657           0 :             static_cast<unsigned char *>(CPLMalloc(psClone->attr_bytes));
     658           0 :         memcpy(psClone->attr_data, psSrcElement->attr_data,
     659           0 :                psClone->attr_bytes);
     660             :     }
     661             : 
     662             :     /* -------------------------------------------------------------------- */
     663             :     /*      Clear location and id information.                              */
     664             :     /* -------------------------------------------------------------------- */
     665         237 :     psClone->offset = -1;
     666         237 :     psClone->element_id = -1;
     667             : 
     668         237 :     return psClone;
     669             : }
     670             : 
     671             : /************************************************************************/
     672             : /*                         DGNUpdateElemCore()                          */
     673             : /************************************************************************/
     674             : 
     675             : /**
     676             :  * Change element core values.
     677             :  *
     678             :  * The indicated values in the element are updated in the structure, as well
     679             :  * as in the raw data.  The updated element is not written to disk.  That
     680             :  * must be done with DGNWriteElement().   The element must have raw_data
     681             :  * loaded.
     682             :  *
     683             :  * @param hDGN the file on which the element belongs.
     684             :  * @param psElement the element to modify.
     685             :  * @param nLevel the new level value.
     686             :  * @param nGraphicGroup the new graphic group value.
     687             :  * @param nColor the new color index.
     688             :  * @param nWeight the new element weight.
     689             :  * @param nStyle the new style value for the element.
     690             :  *
     691             :  * @return Returns TRUE on success or FALSE on failure.
     692             :  */
     693             : 
     694          45 : int DGNUpdateElemCore(DGNHandle hDGN, DGNElemCore *psElement, int nLevel,
     695             :                       int nGraphicGroup, int nColor, int nWeight, int nStyle)
     696             : 
     697             : {
     698          45 :     psElement->level = nLevel;
     699          45 :     psElement->graphic_group = nGraphicGroup;
     700          45 :     psElement->color = nColor;
     701          45 :     psElement->weight = nWeight;
     702          45 :     psElement->style = nStyle;
     703             : 
     704          45 :     return DGNUpdateElemCoreExtended(hDGN, psElement);
     705             : }
     706             : 
     707             : /************************************************************************/
     708             : /*                     DGNUpdateElemCoreExtended()                      */
     709             : /************************************************************************/
     710             : 
     711             : /**
     712             :  * Update internal raw data representation.
     713             :  *
     714             :  * The raw_data representation of the passed element is updated to reflect
     715             :  * the various core fields.  The DGNElemCore level, type, complex, deleted,
     716             :  * graphic_group, properties, color, weight and style values are all
     717             :  * applied to the raw_data representation.  Spatial bounds, element type
     718             :  * specific information and attributes are not updated in the raw data.
     719             :  *
     720             :  * @param hDGN the file to which the element belongs.
     721             :  * @param psElement the element to be updated.
     722             :  *
     723             :  * @return TRUE on success, or FALSE on failure.
     724             :  */
     725             : 
     726         136 : int DGNUpdateElemCoreExtended(CPL_UNUSED DGNHandle hDGN, DGNElemCore *psElement)
     727             : {
     728         136 :     GByte *rd = psElement->raw_data;
     729         136 :     const int nWords = (psElement->raw_bytes / 2) - 2;
     730             : 
     731         136 :     if (psElement->raw_data == nullptr || psElement->raw_bytes < 36)
     732             :     {
     733           0 :         CPLAssert(false);
     734             :         return FALSE;
     735             :     }
     736             : 
     737             :     /* -------------------------------------------------------------------- */
     738             :     /*      Setup first four bytes.                                         */
     739             :     /* -------------------------------------------------------------------- */
     740         136 :     rd[0] = (GByte)psElement->level;
     741         136 :     if (psElement->complex)
     742           5 :         rd[0] |= 0x80;
     743             : 
     744         136 :     rd[1] = (GByte)psElement->type;
     745         136 :     if (psElement->deleted)
     746           0 :         rd[1] |= 0x80;
     747             : 
     748         136 :     rd[2] = (GByte)(nWords % 256);
     749         136 :     rd[3] = (GByte)(nWords / 256);
     750             : 
     751             :     /* -------------------------------------------------------------------- */
     752             :     /*      If the attribute offset hasn't been set, set it now under       */
     753             :     /*      the assumption it should point to the end of the element.       */
     754             :     /* -------------------------------------------------------------------- */
     755         136 :     if (psElement->raw_data[30] == 0 && psElement->raw_data[31] == 0)
     756             :     {
     757          46 :         const int nAttIndex = (psElement->raw_bytes - 32) / 2;
     758             : 
     759          46 :         psElement->raw_data[30] = (GByte)(nAttIndex % 256);
     760          46 :         psElement->raw_data[31] = (GByte)(nAttIndex / 256);
     761             :     }
     762             :     /* -------------------------------------------------------------------- */
     763             :     /*      Handle the graphic properties.                                  */
     764             :     /* -------------------------------------------------------------------- */
     765         136 :     if (psElement->raw_bytes > 36 && DGNElemTypeHasDispHdr(psElement->type))
     766             :     {
     767         136 :         rd[28] = (GByte)(psElement->graphic_group % 256);
     768         136 :         rd[29] = (GByte)(psElement->graphic_group / 256);
     769         136 :         rd[32] = (GByte)(psElement->properties % 256);
     770         136 :         rd[33] = (GByte)(psElement->properties / 256);
     771         136 :         rd[34] = (GByte)(psElement->style | (psElement->weight << 3));
     772         136 :         rd[35] = (GByte)psElement->color;
     773             :     }
     774             : 
     775         136 :     return TRUE;
     776             : }
     777             : 
     778             : /************************************************************************/
     779             : /*                         DGNInitializeElemCore()                      */
     780             : /************************************************************************/
     781             : 
     782          46 : static void DGNInitializeElemCore(CPL_UNUSED DGNHandle hDGN,
     783             :                                   DGNElemCore *psElement)
     784             : {
     785          46 :     memset(psElement, 0, sizeof(DGNElemCore));
     786             : 
     787          46 :     psElement->offset = -1;
     788          46 :     psElement->element_id = -1;
     789          46 : }
     790             : 
     791             : /************************************************************************/
     792             : /*                           DGNWriteBounds()                           */
     793             : /*                                                                      */
     794             : /*      Write bounds to element raw data.                               */
     795             : /************************************************************************/
     796             : 
     797          46 : static void DGNWriteBounds(DGNInfo *psInfo, DGNElemCore *psElement,
     798             :                            DGNPoint *psMin, DGNPoint *psMax)
     799             : 
     800             : {
     801          46 :     CPLAssert(psElement->raw_bytes >= 28);
     802             : 
     803          46 :     DGNInverseTransformPointToInt(psInfo, psMin, psElement->raw_data + 4);
     804          46 :     DGNInverseTransformPointToInt(psInfo, psMax, psElement->raw_data + 16);
     805             : 
     806             :     /* convert from twos complement to "binary offset" format. */
     807             : 
     808          46 :     psElement->raw_data[5] ^= 0x80;
     809          46 :     psElement->raw_data[9] ^= 0x80;
     810          46 :     psElement->raw_data[13] ^= 0x80;
     811          46 :     psElement->raw_data[17] ^= 0x80;
     812          46 :     psElement->raw_data[21] ^= 0x80;
     813          46 :     psElement->raw_data[25] ^= 0x80;
     814          46 : }
     815             : 
     816             : /************************************************************************/
     817             : /*                      DGNCreateMultiPointElem()                       */
     818             : /************************************************************************/
     819             : 
     820             : /**
     821             :  * Create new multi-point element.
     822             :  *
     823             :  * The newly created element will still need to be written to file using
     824             :  * DGNWriteElement(). Also the level and other core values will be defaulted.
     825             :  * Use DGNUpdateElemCore() on the element before writing to set these values.
     826             :  *
     827             :  * NOTE: There are restrictions on the nPointCount for some elements. For
     828             :  * instance, DGNT_LINE can only have 2 points. Maximum element size
     829             :  * precludes very large numbers of points.
     830             :  *
     831             :  * @param hDGN the file on which the element will eventually be written.
     832             :  * @param nType the type of the element to be created.  It must be one of
     833             :  * DGNT_LINE, DGNT_LINE_STRING, DGNT_SHAPE, DGNT_CURVE or DGNT_BSPLINE_POLE.
     834             :  * @param nPointCount the number of points in the pasVertices list.
     835             :  * @param pasVertices the list of points to be written.
     836             :  *
     837             :  * @return the new element (a DGNElemMultiPoint structure) or NULL on failure.
     838             :  */
     839             : 
     840          44 : DGNElemCore *DGNCreateMultiPointElem(DGNHandle hDGN, int nType, int nPointCount,
     841             :                                      DGNPoint *pasVertices)
     842             : 
     843             : {
     844          44 :     DGNInfo *psDGN = (DGNInfo *)hDGN;
     845             : 
     846          44 :     CPLAssert(nType == DGNT_LINE || nType == DGNT_LINE_STRING ||
     847             :               nType == DGNT_SHAPE || nType == DGNT_CURVE ||
     848             :               nType == DGNT_BSPLINE_POLE);
     849             : 
     850          44 :     DGNLoadTCB(hDGN);
     851             : 
     852             :     /* -------------------------------------------------------------------- */
     853             :     /*      Is this too many vertices to write to a single element?         */
     854             :     /* -------------------------------------------------------------------- */
     855          44 :     if (nPointCount > 101)
     856             :     {
     857           0 :         CPLError(CE_Failure, CPLE_ElementTooBig,
     858             :                  "Attempt to create %s element with %d points failed.\n"
     859             :                  "Element would be too large.",
     860             :                  DGNTypeToName(nType), nPointCount);
     861           0 :         return nullptr;
     862             :     }
     863             : 
     864             :     /* -------------------------------------------------------------------- */
     865             :     /*      Allocate element.                                               */
     866             :     /* -------------------------------------------------------------------- */
     867          88 :     DGNElemMultiPoint *psMP = static_cast<DGNElemMultiPoint *>(CPLCalloc(
     868          44 :         sizeof(DGNElemMultiPoint) + sizeof(DGNPoint) * (nPointCount - 1), 1));
     869          44 :     DGNElemCore *psCore = &(psMP->core);
     870             : 
     871          44 :     DGNInitializeElemCore(hDGN, psCore);
     872          44 :     psCore->stype = DGNST_MULTIPOINT;
     873          44 :     psCore->type = nType;
     874             : 
     875             :     /* -------------------------------------------------------------------- */
     876             :     /*      Set multipoint specific information in the structure.           */
     877             :     /* -------------------------------------------------------------------- */
     878          44 :     psMP->num_vertices = nPointCount;
     879             :     // coverity[overrun-buffer-arg]
     880          44 :     memcpy(psMP->vertices + 0, pasVertices, sizeof(DGNPoint) * nPointCount);
     881             : 
     882             :     /* -------------------------------------------------------------------- */
     883             :     /*      Setup Raw data for the multipoint section.                      */
     884             :     /* -------------------------------------------------------------------- */
     885          44 :     if (nType == DGNT_LINE)
     886             :     {
     887          14 :         CPLAssert(nPointCount == 2);
     888             : 
     889          14 :         psCore->raw_bytes = 36 + psDGN->dimension * 4 * nPointCount;
     890             : 
     891          14 :         psCore->raw_data =
     892          14 :             static_cast<unsigned char *>(CPLCalloc(psCore->raw_bytes, 1));
     893             : 
     894          14 :         DGNInverseTransformPointToInt(psDGN, pasVertices + 0,
     895          14 :                                       psCore->raw_data + 36);
     896          14 :         DGNInverseTransformPointToInt(psDGN, pasVertices + 1,
     897          14 :                                       psCore->raw_data + 36 +
     898          14 :                                           psDGN->dimension * 4);
     899             :     }
     900             :     else
     901             :     {
     902          30 :         CPLAssert(nPointCount >= 2);
     903             : 
     904          30 :         psCore->raw_bytes = 38 + psDGN->dimension * 4 * nPointCount;
     905          30 :         psCore->raw_data =
     906          30 :             static_cast<unsigned char *>(CPLCalloc(psCore->raw_bytes, 1));
     907             : 
     908          30 :         psCore->raw_data[36] = (unsigned char)(nPointCount % 256);
     909          30 :         psCore->raw_data[37] = (unsigned char)(nPointCount / 256);
     910             : 
     911         199 :         for (int i = 0; i < nPointCount; i++)
     912         169 :             DGNInverseTransformPointToInt(psDGN, pasVertices + i,
     913         169 :                                           psCore->raw_data + 38 +
     914         169 :                                               psDGN->dimension * i * 4);
     915             :     }
     916             : 
     917             :     /* -------------------------------------------------------------------- */
     918             :     /*      Set the core raw data, including the bounds.                    */
     919             :     /* -------------------------------------------------------------------- */
     920          44 :     DGNUpdateElemCoreExtended(hDGN, psCore);
     921             : 
     922          44 :     DGNPoint sMin = pasVertices[0];
     923          44 :     DGNPoint sMax = pasVertices[0];
     924         197 :     for (int i = 1; i < nPointCount; i++)
     925             :     {
     926         153 :         sMin.x = std::min(pasVertices[i].x, sMin.x);
     927         153 :         sMin.y = std::min(pasVertices[i].y, sMin.y);
     928         153 :         sMin.z = std::min(pasVertices[i].z, sMin.z);
     929         153 :         sMax.x = std::max(pasVertices[i].x, sMax.x);
     930         153 :         sMax.y = std::max(pasVertices[i].y, sMax.y);
     931         153 :         sMax.z = std::max(pasVertices[i].z, sMax.z);
     932             :     }
     933             : 
     934          44 :     DGNWriteBounds(psDGN, psCore, &sMin, &sMax);
     935             : 
     936          44 :     return (DGNElemCore *)psMP;
     937             : }
     938             : 
     939             : /************************************************************************/
     940             : /*                         DGNCreateArcElem2D()                         */
     941             : /************************************************************************/
     942             : 
     943           0 : DGNElemCore *DGNCreateArcElem2D(DGNHandle hDGN, int nType, double dfOriginX,
     944             :                                 double dfOriginY, double dfPrimaryAxis,
     945             :                                 double dfSecondaryAxis, double dfRotation,
     946             :                                 double dfStartAngle, double dfSweepAngle)
     947             : 
     948             : {
     949           0 :     return DGNCreateArcElem(hDGN, nType, dfOriginX, dfOriginY, 0.0,
     950             :                             dfPrimaryAxis, dfSecondaryAxis, dfStartAngle,
     951           0 :                             dfSweepAngle, dfRotation, nullptr);
     952             : }
     953             : 
     954             : /************************************************************************/
     955             : /*                          DGNCreateArcElem()                          */
     956             : /************************************************************************/
     957             : 
     958             : /**
     959             :  * Create Arc or Ellipse element.
     960             :  *
     961             :  * Create a new 2D or 3D arc or ellipse element.  The start angle, and sweep
     962             :  * angle are ignored for DGNT_ELLIPSE but used for DGNT_ARC.
     963             :  *
     964             :  * The newly created element will still need to be written to file using
     965             :  * DGNWriteElement(). Also the level and other core values will be defaulted.
     966             :  * Use DGNUpdateElemCore() on the element before writing to set these values.
     967             :  *
     968             :  * @param hDGN the DGN file on which the element will eventually be written.
     969             :  * @param nType either DGNT_ELLIPSE or DGNT_ARC to select element type.
     970             :  * @param dfOriginX the origin (center of rotation) of the arc (X).
     971             :  * @param dfOriginY the origin (center of rotation) of the arc (Y).
     972             :  * @param dfOriginZ the origin (center of rotation) of the arc (Y).
     973             :  * @param dfPrimaryAxis the length of the primary axis.
     974             :  * @param dfSecondaryAxis the length of the secondary axis.
     975             :  * @param dfStartAngle start angle, degrees counterclockwise of primary axis.
     976             :  * @param dfSweepAngle sweep angle, degrees
     977             :  * @param dfRotation Counterclockwise rotation in degrees.
     978             :  * @param panQuaternion 3D orientation quaternion (NULL to use rotation).
     979             :  *
     980             :  * @return the new element (DGNElemArc) or NULL on failure.
     981             :  */
     982             : 
     983           0 : DGNElemCore *DGNCreateArcElem(DGNHandle hDGN, int nType, double dfOriginX,
     984             :                               double dfOriginY, double dfOriginZ,
     985             :                               double dfPrimaryAxis, double dfSecondaryAxis,
     986             :                               double dfStartAngle, double dfSweepAngle,
     987             :                               double dfRotation, int *panQuaternion)
     988             : 
     989             : {
     990           0 :     CPLAssert(nType == DGNT_ARC || nType == DGNT_ELLIPSE);
     991             : 
     992           0 :     DGNInfo *psDGN = (DGNInfo *)hDGN;
     993           0 :     DGNLoadTCB(hDGN);
     994             : 
     995             :     /* -------------------------------------------------------------------- */
     996             :     /*      Allocate element.                                               */
     997             :     /* -------------------------------------------------------------------- */
     998             :     DGNElemArc *psArc =
     999           0 :         static_cast<DGNElemArc *>(CPLCalloc(sizeof(DGNElemArc), 1));
    1000           0 :     DGNElemCore *psCore = &(psArc->core);
    1001             : 
    1002           0 :     DGNInitializeElemCore(hDGN, psCore);
    1003           0 :     psCore->stype = DGNST_ARC;
    1004           0 :     psCore->type = nType;
    1005             : 
    1006             :     /* -------------------------------------------------------------------- */
    1007             :     /*      Set arc specific information in the structure.                  */
    1008             :     /* -------------------------------------------------------------------- */
    1009           0 :     DGNPoint sOrigin = {dfOriginX, dfOriginY, dfOriginZ};
    1010             : 
    1011           0 :     psArc->origin = sOrigin;
    1012           0 :     psArc->primary_axis = dfPrimaryAxis;
    1013           0 :     psArc->secondary_axis = dfSecondaryAxis;
    1014           0 :     memset(psArc->quat, 0, sizeof(int) * 4);
    1015           0 :     psArc->startang = dfStartAngle;
    1016           0 :     psArc->sweepang = dfSweepAngle;
    1017             : 
    1018           0 :     psArc->rotation = dfRotation;
    1019           0 :     if (panQuaternion == nullptr)
    1020             :     {
    1021           0 :         DGNRotationToQuaternion(dfRotation, psArc->quat);
    1022             :     }
    1023             :     else
    1024             :     {
    1025           0 :         memcpy(psArc->quat, panQuaternion, sizeof(int) * 4);
    1026             :     }
    1027             : 
    1028             :     /* -------------------------------------------------------------------- */
    1029             :     /*      Setup Raw data for the arc section.                             */
    1030             :     /* -------------------------------------------------------------------- */
    1031           0 :     if (nType == DGNT_ARC)
    1032             :     {
    1033             :         double dfScaledAxis;
    1034             : 
    1035           0 :         if (psDGN->dimension == 3)
    1036           0 :             psCore->raw_bytes = 100;
    1037             :         else
    1038           0 :             psCore->raw_bytes = 80;
    1039           0 :         psCore->raw_data =
    1040           0 :             static_cast<unsigned char *>(CPLCalloc(psCore->raw_bytes, 1));
    1041             : 
    1042             :         /* start angle */
    1043           0 :         GInt32 nAngle = (int)(dfStartAngle * 360000.0);
    1044           0 :         DGN_WRITE_INT32(nAngle, psCore->raw_data + 36);
    1045             : 
    1046             :         /* sweep angle */
    1047           0 :         if (dfSweepAngle < 0.0)
    1048             :         {
    1049           0 :             nAngle = static_cast<int>(std::abs(dfSweepAngle) * 360000.0);
    1050           0 :             nAngle |= 0x80000000;
    1051             :         }
    1052           0 :         else if (dfSweepAngle > 364.9999)
    1053             :         {
    1054           0 :             nAngle = 0;
    1055             :         }
    1056             :         else
    1057             :         {
    1058           0 :             nAngle = (int)(dfSweepAngle * 360000.0);
    1059             :         }
    1060           0 :         DGN_WRITE_INT32(nAngle, psCore->raw_data + 40);
    1061             : 
    1062             :         /* axes */
    1063           0 :         dfScaledAxis = dfPrimaryAxis / psDGN->scale;
    1064           0 :         memcpy(psCore->raw_data + 44, &dfScaledAxis, 8);
    1065           0 :         IEEE2DGNDouble(psCore->raw_data + 44);
    1066             : 
    1067           0 :         dfScaledAxis = dfSecondaryAxis / psDGN->scale;
    1068           0 :         memcpy(psCore->raw_data + 52, &dfScaledAxis, 8);
    1069           0 :         IEEE2DGNDouble(psCore->raw_data + 52);
    1070             : 
    1071           0 :         if (psDGN->dimension == 3)
    1072             :         {
    1073             :             /* quaternion */
    1074           0 :             DGN_WRITE_INT32(psArc->quat[0], psCore->raw_data + 60);
    1075           0 :             DGN_WRITE_INT32(psArc->quat[1], psCore->raw_data + 64);
    1076           0 :             DGN_WRITE_INT32(psArc->quat[2], psCore->raw_data + 68);
    1077           0 :             DGN_WRITE_INT32(psArc->quat[3], psCore->raw_data + 72);
    1078             : 
    1079             :             /* origin */
    1080           0 :             DGNInverseTransformPoint(psDGN, &sOrigin);
    1081           0 :             memcpy(psCore->raw_data + 76, &(sOrigin.x), 8);
    1082           0 :             memcpy(psCore->raw_data + 84, &(sOrigin.y), 8);
    1083           0 :             memcpy(psCore->raw_data + 92, &(sOrigin.z), 8);
    1084           0 :             IEEE2DGNDouble(psCore->raw_data + 76);
    1085           0 :             IEEE2DGNDouble(psCore->raw_data + 84);
    1086           0 :             IEEE2DGNDouble(psCore->raw_data + 92);
    1087             :         }
    1088             :         else
    1089             :         {
    1090             :             /* rotation */
    1091           0 :             nAngle = (int)(dfRotation * 360000.0);
    1092           0 :             DGN_WRITE_INT32(nAngle, psCore->raw_data + 60);
    1093             : 
    1094             :             /* origin */
    1095           0 :             DGNInverseTransformPoint(psDGN, &sOrigin);
    1096           0 :             memcpy(psCore->raw_data + 64, &(sOrigin.x), 8);
    1097           0 :             memcpy(psCore->raw_data + 72, &(sOrigin.y), 8);
    1098           0 :             IEEE2DGNDouble(psCore->raw_data + 64);
    1099           0 :             IEEE2DGNDouble(psCore->raw_data + 72);
    1100             :         }
    1101             :     }
    1102             : 
    1103             :     /* -------------------------------------------------------------------- */
    1104             :     /*      Setup Raw data for the ellipse section.                         */
    1105             :     /* -------------------------------------------------------------------- */
    1106             :     else
    1107             :     {
    1108             :         double dfScaledAxis;
    1109             : 
    1110           0 :         if (psDGN->dimension == 3)
    1111           0 :             psCore->raw_bytes = 92;
    1112             :         else
    1113           0 :             psCore->raw_bytes = 72;
    1114           0 :         psCore->raw_data = (unsigned char *)CPLCalloc(psCore->raw_bytes, 1);
    1115             : 
    1116             :         /* axes */
    1117           0 :         dfScaledAxis = dfPrimaryAxis / psDGN->scale;
    1118           0 :         memcpy(psCore->raw_data + 36, &dfScaledAxis, 8);
    1119           0 :         IEEE2DGNDouble(psCore->raw_data + 36);
    1120             : 
    1121           0 :         dfScaledAxis = dfSecondaryAxis / psDGN->scale;
    1122           0 :         memcpy(psCore->raw_data + 44, &dfScaledAxis, 8);
    1123           0 :         IEEE2DGNDouble(psCore->raw_data + 44);
    1124             : 
    1125           0 :         if (psDGN->dimension == 3)
    1126             :         {
    1127             :             /* quaternion */
    1128           0 :             DGN_WRITE_INT32(psArc->quat[0], psCore->raw_data + 52);
    1129           0 :             DGN_WRITE_INT32(psArc->quat[1], psCore->raw_data + 56);
    1130           0 :             DGN_WRITE_INT32(psArc->quat[2], psCore->raw_data + 60);
    1131           0 :             DGN_WRITE_INT32(psArc->quat[3], psCore->raw_data + 64);
    1132             : 
    1133             :             /* origin */
    1134           0 :             DGNInverseTransformPoint(psDGN, &sOrigin);
    1135           0 :             memcpy(psCore->raw_data + 68, &(sOrigin.x), 8);
    1136           0 :             memcpy(psCore->raw_data + 76, &(sOrigin.y), 8);
    1137           0 :             memcpy(psCore->raw_data + 84, &(sOrigin.z), 8);
    1138           0 :             IEEE2DGNDouble(psCore->raw_data + 68);
    1139           0 :             IEEE2DGNDouble(psCore->raw_data + 76);
    1140           0 :             IEEE2DGNDouble(psCore->raw_data + 84);
    1141             :         }
    1142             :         else
    1143             :         {
    1144             :             /* rotation */
    1145           0 :             GInt32 nAngle = (int)(dfRotation * 360000.0);
    1146           0 :             DGN_WRITE_INT32(nAngle, psCore->raw_data + 52);
    1147             : 
    1148             :             /* origin */
    1149           0 :             DGNInverseTransformPoint(psDGN, &sOrigin);
    1150           0 :             memcpy(psCore->raw_data + 56, &(sOrigin.x), 8);
    1151           0 :             memcpy(psCore->raw_data + 64, &(sOrigin.y), 8);
    1152           0 :             IEEE2DGNDouble(psCore->raw_data + 56);
    1153           0 :             IEEE2DGNDouble(psCore->raw_data + 64);
    1154             :         }
    1155             : 
    1156           0 :         psArc->startang = 0.0;
    1157           0 :         psArc->sweepang = 360.0;
    1158             :     }
    1159             : 
    1160             :     /* -------------------------------------------------------------------- */
    1161             :     /*      Set the core raw data, including the bounds.                    */
    1162             :     /* -------------------------------------------------------------------- */
    1163           0 :     DGNUpdateElemCoreExtended(hDGN, psCore);
    1164             : 
    1165           0 :     DGNPoint sMin = {dfOriginX - std::max(dfPrimaryAxis, dfSecondaryAxis),
    1166           0 :                      dfOriginY - std::max(dfPrimaryAxis, dfSecondaryAxis),
    1167           0 :                      dfOriginZ - std::max(dfPrimaryAxis, dfSecondaryAxis)};
    1168           0 :     DGNPoint sMax = {dfOriginX + std::max(dfPrimaryAxis, dfSecondaryAxis),
    1169           0 :                      dfOriginY + std::max(dfPrimaryAxis, dfSecondaryAxis),
    1170           0 :                      dfOriginZ + std::max(dfPrimaryAxis, dfSecondaryAxis)};
    1171             : 
    1172           0 :     DGNWriteBounds(psDGN, psCore, &sMin, &sMax);
    1173             : 
    1174           0 :     return (DGNElemCore *)psArc;
    1175             : }
    1176             : 
    1177             : /************************************************************************/
    1178             : /*                          DGNCreateConeElem()                         */
    1179             : /************************************************************************/
    1180             : 
    1181             : /**
    1182             :  * Create Cone element.
    1183             :  *
    1184             :  * Create a new 3D cone element.
    1185             :  *
    1186             :  * The newly created element will still need to be written to file using
    1187             :  * DGNWriteElement(). Also the level and other core values will be defaulted.
    1188             :  * Use DGNUpdateElemCore() on the element before writing to set these values.
    1189             :  *
    1190             :  * @param hDGN the DGN file on which the element will eventually be written.
    1191             :  * @param dfCenter_1X the center of the first bounding circle (X).
    1192             :  * @param dfCenter_1Y the center of the first bounding circle (Y).
    1193             :  * @param dfCenter_1Z the center of the first bounding circle (Z).
    1194             :  * @param dfRadius_1 the radius of the first bounding circle.
    1195             :  * @param dfCenter_2X the center of the second bounding circle (X).
    1196             :  * @param dfCenter_2Y the center of the second bounding circle (Y).
    1197             :  * @param dfCenter_2Z the center of the second bounding circle (Z).
    1198             :  * @param dfRadius_2 the radius of the second bounding circle.
    1199             :  * @param panQuaternion 3D orientation quaternion (NULL for default orientation
    1200             :  * - circles parallel to the X-Y plane).
    1201             :  *
    1202             :  * @return the new element (DGNElemCone) or NULL on failure.
    1203             :  */
    1204             : 
    1205           0 : DGNElemCore *DGNCreateConeElem(DGNHandle hDGN, double dfCenter_1X,
    1206             :                                double dfCenter_1Y, double dfCenter_1Z,
    1207             :                                double dfRadius_1, double dfCenter_2X,
    1208             :                                double dfCenter_2Y, double dfCenter_2Z,
    1209             :                                double dfRadius_2, int *panQuaternion)
    1210             : {
    1211           0 :     DGNInfo *psDGN = (DGNInfo *)hDGN;
    1212             : 
    1213           0 :     DGNLoadTCB(hDGN);
    1214             : 
    1215             :     /* -------------------------------------------------------------------- */
    1216             :     /*      Allocate element.                                               */
    1217             :     /* -------------------------------------------------------------------- */
    1218           0 :     DGNElemCone *psCone = (DGNElemCone *)CPLCalloc(sizeof(DGNElemCone), 1);
    1219           0 :     DGNElemCore *psCore = &(psCone->core);
    1220             : 
    1221           0 :     DGNInitializeElemCore(hDGN, psCore);
    1222           0 :     psCore->stype = DGNST_CONE;
    1223           0 :     psCore->type = DGNT_CONE;
    1224             : 
    1225             :     /* -------------------------------------------------------------------- */
    1226             :     /*      Set cone specific information in the structure.                 */
    1227             :     /* -------------------------------------------------------------------- */
    1228           0 :     DGNPoint sCenter_1 = {dfCenter_1X, dfCenter_1Y, dfCenter_1Z};
    1229           0 :     DGNPoint sCenter_2 = {dfCenter_2X, dfCenter_2Y, dfCenter_2Z};
    1230           0 :     psCone->center_1 = sCenter_1;
    1231           0 :     psCone->center_2 = sCenter_2;
    1232           0 :     psCone->radius_1 = dfRadius_1;
    1233           0 :     psCone->radius_2 = dfRadius_2;
    1234             : 
    1235           0 :     memset(psCone->quat, 0, sizeof(int) * 4);
    1236           0 :     if (panQuaternion != nullptr)
    1237             :     {
    1238           0 :         memcpy(psCone->quat, panQuaternion, sizeof(int) * 4);
    1239             :     }
    1240             :     else
    1241             :     {
    1242           0 :         psCone->quat[0] = static_cast<int>(1U << 31);
    1243           0 :         psCone->quat[1] = 0;
    1244           0 :         psCone->quat[2] = 0;
    1245           0 :         psCone->quat[3] = 0;
    1246             :     }
    1247             : 
    1248             :     /* -------------------------------------------------------------------- */
    1249             :     /*      Setup Raw data for the cone.                                    */
    1250             :     /* -------------------------------------------------------------------- */
    1251           0 :     psCore->raw_bytes = 118;
    1252           0 :     psCore->raw_data = (unsigned char *)CPLCalloc(psCore->raw_bytes, 1);
    1253             : 
    1254             :     /* unknown data */
    1255           0 :     psCore->raw_data[36] = 0;
    1256           0 :     psCore->raw_data[37] = 0;
    1257             : 
    1258             :     /* quaternion */
    1259           0 :     DGN_WRITE_INT32(psCone->quat[0], psCore->raw_data + 38);
    1260           0 :     DGN_WRITE_INT32(psCone->quat[1], psCore->raw_data + 42);
    1261           0 :     DGN_WRITE_INT32(psCone->quat[2], psCore->raw_data + 46);
    1262           0 :     DGN_WRITE_INT32(psCone->quat[3], psCore->raw_data + 50);
    1263             : 
    1264             :     /* center_1 */
    1265           0 :     DGNInverseTransformPoint(psDGN, &sCenter_1);
    1266           0 :     memcpy(psCore->raw_data + 54, &sCenter_1.x, 8);
    1267           0 :     memcpy(psCore->raw_data + 62, &sCenter_1.y, 8);
    1268           0 :     memcpy(psCore->raw_data + 70, &sCenter_1.z, 8);
    1269           0 :     IEEE2DGNDouble(psCore->raw_data + 54);
    1270           0 :     IEEE2DGNDouble(psCore->raw_data + 62);
    1271           0 :     IEEE2DGNDouble(psCore->raw_data + 70);
    1272             : 
    1273             :     /* radius_1 */
    1274           0 :     double dfScaledRadius = psCone->radius_1 / psDGN->scale;
    1275           0 :     memcpy(psCore->raw_data + 78, &dfScaledRadius, 8);
    1276           0 :     IEEE2DGNDouble(psCore->raw_data + 78);
    1277             : 
    1278             :     /* center_2 */
    1279           0 :     DGNInverseTransformPoint(psDGN, &sCenter_2);
    1280           0 :     memcpy(psCore->raw_data + 86, &sCenter_2.x, 8);
    1281           0 :     memcpy(psCore->raw_data + 94, &sCenter_2.y, 8);
    1282           0 :     memcpy(psCore->raw_data + 102, &sCenter_2.z, 8);
    1283           0 :     IEEE2DGNDouble(psCore->raw_data + 86);
    1284           0 :     IEEE2DGNDouble(psCore->raw_data + 94);
    1285           0 :     IEEE2DGNDouble(psCore->raw_data + 102);
    1286             : 
    1287             :     /* radius_2 */
    1288           0 :     dfScaledRadius = psCone->radius_2 / psDGN->scale;
    1289           0 :     memcpy(psCore->raw_data + 110, &dfScaledRadius, 8);
    1290           0 :     IEEE2DGNDouble(psCore->raw_data + 110);
    1291             : 
    1292             :     /* -------------------------------------------------------------------- */
    1293             :     /*      Set the core raw data, including the bounds.                    */
    1294             :     /* -------------------------------------------------------------------- */
    1295           0 :     DGNUpdateElemCoreExtended(hDGN, psCore);
    1296             : 
    1297             :     // FIXME: Calculate bounds. Do we need to take the quaternion into account?
    1298             :     //  kintel 20030819
    1299             : 
    1300             :     // Old implementation attempt:
    1301             :     // What if center_1.z > center_2.z ?
    1302             :     //     double largestRadius =
    1303             :     //       psCone->radius_1>psCone->radius_2?psCone->radius_1:psCone->radius_2;
    1304             :     //     sMin.x = psCone->center_1.x-largestRadius;
    1305             :     //     sMin.y = psCone->center_1.y-largestRadius;
    1306             :     //     sMin.z = psCone->center_1.z;
    1307             :     //     sMax.x = psCone->center_2.x+largestRadius;
    1308             :     //     sMax.y = psCone->center_2.y+largestRadius;
    1309             :     //     sMax.z = psCone->center_2.z;
    1310             : 
    1311           0 :     DGNPoint sMin = {0.0, 0.0, 0.0};
    1312           0 :     DGNPoint sMax = {0.0, 0.0, 0.0};
    1313           0 :     DGNWriteBounds(psDGN, psCore, &sMin, &sMax);
    1314             : 
    1315           0 :     return (DGNElemCore *)psCone;
    1316             : }
    1317             : 
    1318             : /************************************************************************/
    1319             : /*                         DGNCreateTextElem()                          */
    1320             : /************************************************************************/
    1321             : 
    1322             : /**
    1323             :  * Create text element.
    1324             :  *
    1325             :  * The newly created element will still need to be written to file using
    1326             :  * DGNWriteElement(). Also the level and other core values will be defaulted.
    1327             :  * Use DGNUpdateElemCore() on the element before writing to set these values.
    1328             :  *
    1329             :  * @param hDGN the file on which the element will eventually be written.
    1330             :  * @param pszText the string of text.
    1331             :  * @param nFontId microstation font id for the text.  1 may be used as default.
    1332             :  * @param nJustification text justification.  One of DGNJ_LEFT_TOP,
    1333             :  * DGNJ_LEFT_CENTER, DGNJ_LEFT_BOTTOM, DGNJ_CENTER_TOP, DGNJ_CENTER_CENTER,
    1334             :  * DGNJ_CENTER_BOTTOM, DGNJ_RIGHT_TOP, DGNJ_RIGHT_CENTER, DGNJ_RIGHT_BOTTOM.
    1335             :  * @param dfLengthMult character width in master units.
    1336             :  * @param dfHeightMult character height in master units.
    1337             :  * @param dfRotation Counterclockwise text rotation in degrees.
    1338             :  * @param panQuaternion 3D orientation quaternion (NULL to use rotation).
    1339             :  * @param dfOriginX Text origin (X).
    1340             :  * @param dfOriginY Text origin (Y).
    1341             :  * @param dfOriginZ Text origin (Z).
    1342             :  *
    1343             :  * @return the new element (DGNElemText) or NULL on failure.
    1344             :  */
    1345             : 
    1346           1 : DGNElemCore *DGNCreateTextElem(DGNHandle hDGN, const char *pszText, int nFontId,
    1347             :                                int nJustification, double dfLengthMult,
    1348             :                                double dfHeightMult, double dfRotation,
    1349             :                                int *panQuaternion, double dfOriginX,
    1350             :                                double dfOriginY, double dfOriginZ)
    1351             : 
    1352             : {
    1353           1 :     DGNInfo *psDGN = (DGNInfo *)hDGN;
    1354             : 
    1355           1 :     DGNLoadTCB(hDGN);
    1356             : 
    1357             :     /* -------------------------------------------------------------------- */
    1358             :     /*      Allocate element.                                               */
    1359             :     /* -------------------------------------------------------------------- */
    1360             :     DGNElemText *psText =
    1361           1 :         (DGNElemText *)CPLCalloc(sizeof(DGNElemText) + strlen(pszText), 1);
    1362           1 :     DGNElemCore *psCore = &(psText->core);
    1363             : 
    1364           1 :     DGNInitializeElemCore(hDGN, psCore);
    1365           1 :     psCore->stype = DGNST_TEXT;
    1366           1 :     psCore->type = DGNT_TEXT;
    1367             : 
    1368             :     /* -------------------------------------------------------------------- */
    1369             :     /*      Set arc specific information in the structure.                  */
    1370             :     /* -------------------------------------------------------------------- */
    1371           1 :     psText->font_id = nFontId;
    1372           1 :     psText->justification = nJustification;
    1373           1 :     psText->length_mult = dfLengthMult;
    1374           1 :     psText->height_mult = dfHeightMult;
    1375           1 :     psText->rotation = dfRotation;
    1376           1 :     psText->origin.x = dfOriginX;
    1377           1 :     psText->origin.y = dfOriginY;
    1378           1 :     psText->origin.z = dfOriginZ;
    1379           1 :     strcpy(psText->string, pszText);
    1380             : 
    1381             :     /* -------------------------------------------------------------------- */
    1382             :     /*      Setup Raw data for the text specific portion.                   */
    1383             :     /* -------------------------------------------------------------------- */
    1384           1 :     if (psDGN->dimension == 2)
    1385           1 :         psCore->raw_bytes = 60 + static_cast<int>(strlen(pszText));
    1386             :     else
    1387           0 :         psCore->raw_bytes = 76 + static_cast<int>(strlen(pszText));
    1388             : 
    1389           1 :     psCore->raw_bytes += (psCore->raw_bytes % 2);
    1390           1 :     psCore->raw_data = (unsigned char *)CPLCalloc(psCore->raw_bytes, 1);
    1391             : 
    1392           1 :     psCore->raw_data[36] = (unsigned char)nFontId;
    1393           1 :     psCore->raw_data[37] = (unsigned char)nJustification;
    1394             : 
    1395           1 :     GInt32 nIntValue =
    1396           1 :         static_cast<int>(dfLengthMult * 1000.0 / (psDGN->scale * 6.0) + 0.5);
    1397           1 :     DGN_WRITE_INT32(nIntValue, psCore->raw_data + 38);
    1398             : 
    1399           1 :     nIntValue = (int)(dfHeightMult * 1000.0 / (psDGN->scale * 6.0) + 0.5);
    1400           1 :     DGN_WRITE_INT32(nIntValue, psCore->raw_data + 42);
    1401             : 
    1402           1 :     GInt32 nBase = 0;
    1403             : 
    1404           1 :     if (psDGN->dimension == 2)
    1405             :     {
    1406           1 :         nIntValue = (int)(dfRotation * 360000.0);
    1407           1 :         DGN_WRITE_INT32(nIntValue, psCore->raw_data + 46);
    1408             : 
    1409           1 :         DGNInverseTransformPointToInt(psDGN, &(psText->origin),
    1410           1 :                                       psCore->raw_data + 50);
    1411             : 
    1412           1 :         nBase = 58;
    1413             :     }
    1414             :     else
    1415             :     {
    1416             :         int anQuaternion[4];
    1417             : 
    1418           0 :         if (panQuaternion == nullptr)
    1419           0 :             DGNRotationToQuaternion(dfRotation, anQuaternion);
    1420             :         else
    1421           0 :             memcpy(anQuaternion, panQuaternion, sizeof(int) * 4);
    1422             : 
    1423           0 :         DGN_WRITE_INT32(anQuaternion[0], psCore->raw_data + 46);
    1424           0 :         DGN_WRITE_INT32(anQuaternion[1], psCore->raw_data + 50);
    1425           0 :         DGN_WRITE_INT32(anQuaternion[2], psCore->raw_data + 54);
    1426           0 :         DGN_WRITE_INT32(anQuaternion[3], psCore->raw_data + 58);
    1427             : 
    1428           0 :         DGNInverseTransformPointToInt(psDGN, &(psText->origin),
    1429           0 :                                       psCore->raw_data + 62);
    1430           0 :         nBase = 74;
    1431             :     }
    1432             : 
    1433           1 :     psCore->raw_data[nBase] = (unsigned char)strlen(pszText);
    1434           1 :     psCore->raw_data[nBase + 1] = 0; /* edflds? */
    1435           1 :     memcpy(psCore->raw_data + nBase + 2, pszText, strlen(pszText));
    1436             : 
    1437             :     /* -------------------------------------------------------------------- */
    1438             :     /*      Set the core raw data, including the bounds.                    */
    1439             :     /*                                                                      */
    1440             :     /*      Code contributed by Mart Kelder.                                */
    1441             :     /* -------------------------------------------------------------------- */
    1442           1 :     DGNUpdateElemCoreExtended(hDGN, psCore);
    1443             : 
    1444             :     // calculate bounds if rotation is 0
    1445           1 :     DGNPoint sMin = {dfOriginX, dfOriginY, 0.0};
    1446           1 :     DGNPoint sMax = {dfOriginX + dfLengthMult * strlen(pszText),
    1447           1 :                      dfOriginY + dfHeightMult, 0.0};
    1448             : 
    1449             : #if 0
    1450             :     //calculate rotated bounding box coordinates
    1451             :     const double length = sMax.x-sMin.x;
    1452             :     const double height = sMax.y-sMin.y;
    1453             :     const double diagonal=sqrt(length*length+height*height);
    1454             :     const DGNPoint sLowLeft = { sMin.x, sMin.y, 0.0 };
    1455             :     const DGNPoint sLowRight = {
    1456             :        sMin.x+cos(psText->rotation*M_PI/180.0)*length,
    1457             :        sMin.y+sin(psText->rotation*M_PI/180.0)*length,
    1458             :        0.0
    1459             :     };
    1460             :     const DGNPoint sUpRight = {
    1461             :         sMin.x+cos((psText->rotation*M_PI/180.0)+atan(height/length))*diagonal,
    1462             :         sMin.y+sin((psText->rotation*M_PI/180.0)+atan(height/length))*diagonal,
    1463             :         0.0
    1464             :     };
    1465             :     const DGNPoint sUpLeft = {
    1466             :         sMin.x+cos((psText->rotation+90.0)*M_PI/180.0)*height,
    1467             :         sMin.y+sin((psText->rotation+90.0)*M_PI/180.0)*height,
    1468             :         0.0
    1469             :     };
    1470             : 
    1471             :     //calculate new values for bounding box
    1472             :     sMin.x = std::min(sLowLeft.x,
    1473             :                       std::min(sLowRight.x, std::min(sUpLeft.x, sUpRight.x)));
    1474             :     sMin.y = std::min(sLowLeft.y,
    1475             :                       std::min(sLowRight.y, std::min(sUpLeft.y, sUpRight.y)));
    1476             :     sMax.x = std::max(sLowLeft.x,
    1477             :                       std::max(sLowRight.x, std::max(sUpLeft.x, sUpRight.x)));
    1478             :     sMax.y = std::max(sLowLeft.y,
    1479             :                       std::max(sLowRight.y, std::max(sUpLeft.y, sUpRight.y)));
    1480             : #endif
    1481           1 :     sMin.x = dfOriginX - dfLengthMult * strlen(pszText);
    1482           1 :     sMin.y = dfOriginY - dfHeightMult;
    1483           1 :     sMin.z = 0.0;
    1484           1 :     sMax.x = dfOriginX + dfLengthMult * strlen(pszText);
    1485           1 :     sMax.y = dfOriginY + dfHeightMult;
    1486           1 :     sMax.z = 0.0;
    1487             : 
    1488           1 :     DGNWriteBounds(psDGN, psCore, &sMin, &sMax);
    1489             : 
    1490           1 :     return (DGNElemCore *)psText;
    1491             : }
    1492             : 
    1493             : /************************************************************************/
    1494             : /*                      DGNCreateColorTableElem()                       */
    1495             : /************************************************************************/
    1496             : 
    1497             : /**
    1498             :  * Create color table element.
    1499             :  *
    1500             :  * Creates a color table element with the indicated color table.
    1501             :  *
    1502             :  * Note that color table elements are actually of type DGNT_GROUP_DATA(5)
    1503             :  * and always on level 1.  Do not alter the level with DGNUpdateElemCore()
    1504             :  * or the element will essentially be corrupt.
    1505             :  *
    1506             :  * The newly created element will still need to be written to file using
    1507             :  * DGNWriteElement(). Also the level and other core values will be defaulted.
    1508             :  * Use DGNUpdateElemCore() on the element before writing to set these values.
    1509             :  *
    1510             :  * @param hDGN the file to which the element will eventually be written.
    1511             :  * @param nScreenFlag the screen to which the color table applies
    1512             :  * (0 = left, 1 = right).
    1513             :  * @param abyColorInfo array of 256 color entries. The first is
    1514             :  * the background color.
    1515             :  *
    1516             :  * @return the new element (DGNElemColorTable) or NULL on failure.
    1517             :  */
    1518             : 
    1519           0 : DGNElemCore *DGNCreateColorTableElem(DGNHandle hDGN, int nScreenFlag,
    1520             :                                      GByte abyColorInfo[256][3])
    1521             : 
    1522             : {
    1523             :     /* -------------------------------------------------------------------- */
    1524             :     /*      Allocate element.                                               */
    1525             :     /* -------------------------------------------------------------------- */
    1526             :     DGNElemColorTable *psCT =
    1527           0 :         (DGNElemColorTable *)CPLCalloc(sizeof(DGNElemColorTable), 1);
    1528           0 :     DGNElemCore *psCore = &(psCT->core);
    1529             : 
    1530           0 :     DGNInitializeElemCore(hDGN, psCore);
    1531           0 :     psCore->stype = DGNST_COLORTABLE;
    1532           0 :     psCore->type = DGNT_GROUP_DATA;
    1533           0 :     psCore->level = DGN_GDL_COLOR_TABLE;
    1534             : 
    1535             :     /* -------------------------------------------------------------------- */
    1536             :     /*      Set colortable specific information in the structure.           */
    1537             :     /* -------------------------------------------------------------------- */
    1538           0 :     psCT->screen_flag = nScreenFlag;
    1539           0 :     memcpy(psCT->color_info, abyColorInfo, 768);
    1540             : 
    1541             :     /* -------------------------------------------------------------------- */
    1542             :     /*      Setup Raw data for the color table specific portion.            */
    1543             :     /* -------------------------------------------------------------------- */
    1544           0 :     psCore->raw_bytes = 41 + (256 - 1) * 3;
    1545           0 :     psCore->raw_data = (unsigned char *)CPLCalloc(psCore->raw_bytes, 1);
    1546             : 
    1547           0 :     psCore->raw_data[36] = (unsigned char)(nScreenFlag % 256);
    1548           0 :     psCore->raw_data[37] = (unsigned char)(nScreenFlag / 256);
    1549             : 
    1550           0 :     memcpy(psCore->raw_data + 38, abyColorInfo[255], 3);
    1551           0 :     memcpy(psCore->raw_data + 41, abyColorInfo, (256 - 1) * 3);
    1552             : 
    1553             :     /* -------------------------------------------------------------------- */
    1554             :     /*      Set the core raw data.                                          */
    1555             :     /* -------------------------------------------------------------------- */
    1556           0 :     DGNUpdateElemCoreExtended(hDGN, psCore);
    1557             : 
    1558           0 :     return (DGNElemCore *)psCT;
    1559             : }
    1560             : 
    1561             : /************************************************************************/
    1562             : /*                     DGNCreateComplexHeaderElem()                     */
    1563             : /************************************************************************/
    1564             : 
    1565             : /**
    1566             :  * Create complex chain/shape header.
    1567             :  *
    1568             :  * The newly created element will still need to be written to file using
    1569             :  * DGNWriteElement(). Also the level and other core values will be defaulted.
    1570             :  * Use DGNUpdateElemCore() on the element before writing to set these values.
    1571             :  *
    1572             :  * The nTotLength is the sum of the size of all elements in the complex
    1573             :  * group plus 5.  The DGNCreateComplexHeaderFromGroup() can be used to build
    1574             :  * a complex element from the members more conveniently.
    1575             :  *
    1576             :  * @param hDGN the file on which the element will be written.
    1577             :  * @param nType DGNT_COMPLEX_CHAIN_HEADER or DGNT_COMPLEX_SHAPE_HEADER.
    1578             :  * depending on whether the list is open or closed (last point equal to last)
    1579             :  * or if the object represents a surface or a solid.
    1580             :  * @param nTotLength the value of the totlength field in the element.
    1581             :  * @param nNumElems the number of elements in the complex group not including
    1582             :  * the header element.
    1583             :  *
    1584             :  * @return the new element (DGNElemComplexHeader) or NULL on failure.
    1585             :  */
    1586           1 : DGNElemCore *DGNCreateComplexHeaderElem(DGNHandle hDGN, int nType,
    1587             :                                         int nTotLength, int nNumElems)
    1588             : {
    1589           1 :     unsigned char abyRawZeroLinkage[8] = {0, 0, 0, 0, 0, 0, 0, 0};
    1590             : 
    1591           1 :     CPLAssert(nType == DGNT_COMPLEX_CHAIN_HEADER ||
    1592             :               nType == DGNT_COMPLEX_SHAPE_HEADER);
    1593             : 
    1594           1 :     DGNLoadTCB(hDGN);
    1595             : 
    1596             :     /* -------------------------------------------------------------------- */
    1597             :     /*      Allocate element.                                               */
    1598             :     /* -------------------------------------------------------------------- */
    1599             :     DGNElemComplexHeader *psCH =
    1600           1 :         (DGNElemComplexHeader *)CPLCalloc(sizeof(DGNElemComplexHeader), 1);
    1601           1 :     DGNElemCore *psCore = &(psCH->core);
    1602             : 
    1603           1 :     DGNInitializeElemCore(hDGN, psCore);
    1604           1 :     psCore->complex = TRUE;
    1605           1 :     psCore->stype = DGNST_COMPLEX_HEADER;
    1606           1 :     psCore->type = nType;
    1607             : 
    1608             :     /* -------------------------------------------------------------------- */
    1609             :     /*      Set complex header specific information in the structure.       */
    1610             :     /* -------------------------------------------------------------------- */
    1611           1 :     psCH->totlength = nTotLength - 4;
    1612           1 :     psCH->numelems = nNumElems;
    1613           1 :     psCH->surftype = 0;
    1614           1 :     psCH->boundelms = 0;
    1615             : 
    1616             :     /* -------------------------------------------------------------------- */
    1617             :     /*      Setup Raw data for the complex specific portion.                */
    1618             :     /* -------------------------------------------------------------------- */
    1619           1 :     psCore->raw_bytes = 40;
    1620           1 :     psCore->raw_data = (unsigned char *)CPLCalloc(psCore->raw_bytes, 1);
    1621             : 
    1622           1 :     psCore->raw_data[36] = (unsigned char)((nTotLength - 4) % 256);
    1623           1 :     psCore->raw_data[37] = (unsigned char)((nTotLength - 4) / 256);
    1624           1 :     psCore->raw_data[38] = (unsigned char)(nNumElems % 256);
    1625           1 :     psCore->raw_data[39] = (unsigned char)(nNumElems / 256);
    1626             : 
    1627             :     /* -------------------------------------------------------------------- */
    1628             :     /*      Set the core raw data.                                          */
    1629             :     /* -------------------------------------------------------------------- */
    1630           1 :     DGNUpdateElemCoreExtended(hDGN, psCore);
    1631             : 
    1632             :     /* -------------------------------------------------------------------- */
    1633             :     /*      Elements have to be at least 48 bytes long, so we have to       */
    1634             :     /*      add a dummy bit of attribute data to fill out the length.       */
    1635             :     /* -------------------------------------------------------------------- */
    1636           1 :     DGNAddRawAttrLink(hDGN, psCore, 8, abyRawZeroLinkage);
    1637             : 
    1638           1 :     return (DGNElemCore *)psCH;
    1639             : }
    1640             : 
    1641             : /************************************************************************/
    1642             : /*                  DGNCreateComplexHeaderFromGroup()                   */
    1643             : /************************************************************************/
    1644             : 
    1645             : /**
    1646             :  * Create complex chain/shape header.
    1647             :  *
    1648             :  * This function is similar to DGNCreateComplexHeaderElem(), but it takes
    1649             :  * care of computing the total size of the set of elements being written,
    1650             :  * and collecting the bounding extents.  It also takes care of some other
    1651             :  * convenience issues, like marking all the member elements as complex, and
    1652             :  * setting the level based on the level of the member elements.
    1653             :  *
    1654             :  * @param hDGN the file on which the element will be written.
    1655             :  * @param nType DGNT_COMPLEX_CHAIN_HEADER or DGNT_COMPLEX_SHAPE_HEADER.
    1656             :  * depending on whether the list is open or closed (last point equal to last)
    1657             :  * or if the object represents a surface or a solid.
    1658             :  * @param nNumElems the number of elements in the complex group not including
    1659             :  * the header element.
    1660             :  * @param papsElems array of pointers to nNumElems elements in the complex
    1661             :  * group.  Some updates may be made to these elements.
    1662             :  *
    1663             :  * @return the new element (DGNElemComplexHeader) or NULL on failure.
    1664             :  */
    1665             : 
    1666           1 : DGNElemCore *DGNCreateComplexHeaderFromGroup(DGNHandle hDGN, int nType,
    1667             :                                              int nNumElems,
    1668             :                                              DGNElemCore **papsElems)
    1669             : 
    1670             : {
    1671           1 :     DGNLoadTCB(hDGN);
    1672             : 
    1673           1 :     if (nNumElems < 1 || papsElems == nullptr)
    1674             :     {
    1675           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1676             :                  "Need at least one element to form a complex group.");
    1677           0 :         return nullptr;
    1678             :     }
    1679             : 
    1680             :     /* -------------------------------------------------------------------- */
    1681             :     /*      Collect the total size, and bounds.                             */
    1682             :     /* -------------------------------------------------------------------- */
    1683           1 :     int nTotalLength = 5;
    1684           1 :     const int nLevel = papsElems[0]->level;
    1685           1 :     DGNPoint sMin = {0.0, 0.0, 0.0};
    1686           1 :     DGNPoint sMax = {0.0, 0.0, 0.0};
    1687             : 
    1688           3 :     for (int i = 0; i < nNumElems; i++)
    1689             :     {
    1690           2 :         nTotalLength += papsElems[i]->raw_bytes / 2;
    1691             : 
    1692           2 :         papsElems[i]->complex = TRUE;
    1693           2 :         papsElems[i]->raw_data[0] |= 0x80;
    1694             : 
    1695           2 :         if (papsElems[i]->level != nLevel)
    1696             :         {
    1697           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    1698             :                      "Not all level values matching in a complex set group!");
    1699             :         }
    1700             : 
    1701           2 :         DGNPoint sThisMin = {0.0, 0.0, 0.0};
    1702           2 :         DGNPoint sThisMax = {0.0, 0.0, 0.0};
    1703             : 
    1704           2 :         DGNGetElementExtents(hDGN, papsElems[i], &sThisMin, &sThisMax);
    1705           2 :         if (i == 0)
    1706             :         {
    1707           1 :             sMin = sThisMin;
    1708           1 :             sMax = sThisMax;
    1709             :         }
    1710             :         else
    1711             :         {
    1712           1 :             sMin.x = std::min(sMin.x, sThisMin.x);
    1713           1 :             sMin.y = std::min(sMin.y, sThisMin.y);
    1714           1 :             sMin.z = std::min(sMin.z, sThisMin.z);
    1715           1 :             sMax.x = std::max(sMax.x, sThisMax.x);
    1716           1 :             sMax.y = std::max(sMax.y, sThisMax.y);
    1717           1 :             sMax.z = std::max(sMax.z, sThisMax.z);
    1718             :         }
    1719             :     }
    1720             : 
    1721             :     /* -------------------------------------------------------------------- */
    1722             :     /*      Create the corresponding complex header.                        */
    1723             :     /* -------------------------------------------------------------------- */
    1724             :     DGNElemCore *psCH =
    1725           1 :         DGNCreateComplexHeaderElem(hDGN, nType, nTotalLength, nNumElems);
    1726           1 :     DGNUpdateElemCore(hDGN, psCH, papsElems[0]->level, psCH->graphic_group,
    1727             :                       psCH->color, psCH->weight, psCH->style);
    1728             : 
    1729           1 :     DGNWriteBounds((DGNInfo *)hDGN, psCH, &sMin, &sMax);
    1730             : 
    1731           1 :     return psCH;
    1732             : }
    1733             : 
    1734             : /************************************************************************/
    1735             : /*                     DGNCreateSolidHeaderElem()                       */
    1736             : /************************************************************************/
    1737             : 
    1738             : /**
    1739             :  * Create 3D solid/surface.
    1740             :  *
    1741             :  * The newly created element will still need to be written to file using
    1742             :  * DGNWriteElement(). Also the level and other core values will be defaulted.
    1743             :  * Use DGNUpdateElemCore() on the element before writing to set these values.
    1744             :  *
    1745             :  * The nTotLength is the sum of the size of all elements in the solid
    1746             :  * group plus 6.  The DGNCreateSolidHeaderFromGroup() can be used to build
    1747             :  * a solid element from the members more conveniently.
    1748             :  *
    1749             :  * @param hDGN the file on which the element will be written.
    1750             :  * @param nType DGNT_3DSURFACE_HEADER or DGNT_3DSOLID_HEADER.
    1751             :  * @param nSurfType the surface/solid type, one of DGNSUT_* or DGNSOT_*.
    1752             :  * @param nBoundElems the number of elements in each boundary.
    1753             :  * @param nTotLength the value of the totlength field in the element.
    1754             :  * @param nNumElems the number of elements in the solid not including
    1755             :  * the header element.
    1756             :  *
    1757             :  * @return the new element (DGNElemComplexHeader) or NULL on failure.
    1758             :  */
    1759           0 : DGNElemCore *DGNCreateSolidHeaderElem(DGNHandle hDGN, int nType, int nSurfType,
    1760             :                                       int nBoundElems, int nTotLength,
    1761             :                                       int nNumElems)
    1762             : {
    1763           0 :     CPLAssert(nType == DGNT_3DSURFACE_HEADER || nType == DGNT_3DSOLID_HEADER);
    1764             : 
    1765           0 :     DGNLoadTCB(hDGN);
    1766             : 
    1767             :     /* -------------------------------------------------------------------- */
    1768             :     /*      Allocate element.                                               */
    1769             :     /* -------------------------------------------------------------------- */
    1770             :     DGNElemComplexHeader *psCH =
    1771           0 :         (DGNElemComplexHeader *)CPLCalloc(sizeof(DGNElemComplexHeader), 1);
    1772           0 :     DGNElemCore *psCore = &(psCH->core);
    1773             : 
    1774           0 :     DGNInitializeElemCore(hDGN, psCore);
    1775           0 :     psCore->complex = TRUE;
    1776           0 :     psCore->stype = DGNST_COMPLEX_HEADER;
    1777           0 :     psCore->type = nType;
    1778             : 
    1779             :     /* -------------------------------------------------------------------- */
    1780             :     /*      Set solid header specific information in the structure.         */
    1781             :     /* -------------------------------------------------------------------- */
    1782           0 :     psCH->totlength = nTotLength - 4;
    1783           0 :     psCH->numelems = nNumElems;
    1784           0 :     psCH->surftype = nSurfType;
    1785           0 :     psCH->boundelms = nBoundElems;
    1786             : 
    1787             :     /* -------------------------------------------------------------------- */
    1788             :     /*      Setup Raw data for the solid specific portion.                  */
    1789             :     /* -------------------------------------------------------------------- */
    1790           0 :     psCore->raw_bytes = 42;
    1791             : 
    1792           0 :     psCore->raw_data = (unsigned char *)CPLCalloc(psCore->raw_bytes, 1);
    1793             : 
    1794           0 :     psCore->raw_data[36] = (unsigned char)((nTotLength - 4) % 256);
    1795           0 :     psCore->raw_data[37] = (unsigned char)((nTotLength - 4) / 256);
    1796           0 :     psCore->raw_data[38] = (unsigned char)(nNumElems % 256);
    1797           0 :     psCore->raw_data[39] = (unsigned char)(nNumElems / 256);
    1798           0 :     psCore->raw_data[40] = (unsigned char)psCH->surftype;
    1799           0 :     psCore->raw_data[41] = (unsigned char)psCH->boundelms - 1;
    1800             : 
    1801             :     /* -------------------------------------------------------------------- */
    1802             :     /*      Set the core raw data.                                          */
    1803             :     /* -------------------------------------------------------------------- */
    1804           0 :     DGNUpdateElemCoreExtended(hDGN, psCore);
    1805             : 
    1806             :     /* -------------------------------------------------------------------- */
    1807             :     /*      Elements have to be at least 48 bytes long, so we have to       */
    1808             :     /*      add a dummy bit of attribute data to fill out the length.       */
    1809             :     /* -------------------------------------------------------------------- */
    1810           0 :     unsigned char abyRawZeroLinkage[8] = {0, 0, 0, 0, 0, 0, 0, 0};
    1811           0 :     DGNAddRawAttrLink(hDGN, psCore, 8, abyRawZeroLinkage);
    1812             : 
    1813           0 :     return (DGNElemCore *)psCH;
    1814             : }
    1815             : 
    1816             : /************************************************************************/
    1817             : /*                  DGNCreateSolidHeaderFromGroup()                     */
    1818             : /************************************************************************/
    1819             : 
    1820             : /**
    1821             :  * Create 3D solid/surface header.
    1822             :  *
    1823             :  * This function is similar to DGNCreateSolidHeaderElem(), but it takes
    1824             :  * care of computing the total size of the set of elements being written,
    1825             :  * and collecting the bounding extents.  It also takes care of some other
    1826             :  * convenience issues, like marking all the member elements as complex, and
    1827             :  * setting the level based on the level of the member elements.
    1828             :  *
    1829             :  * @param hDGN the file on which the element will be written.
    1830             :  * @param nType DGNT_3DSURFACE_HEADER or DGNT_3DSOLID_HEADER.
    1831             :  * @param nSurfType the surface/solid type, one of DGNSUT_* or DGNSOT_*.
    1832             :  * @param nBoundElems the number of boundary elements.
    1833             :  * @param nNumElems the number of elements in the solid not including
    1834             :  * the header element.
    1835             :  * @param papsElems array of pointers to nNumElems elements in the solid.
    1836             :  * Some updates may be made to these elements.
    1837             :  *
    1838             :  * @return the new element (DGNElemComplexHeader) or NULL on failure.
    1839             :  */
    1840             : 
    1841           0 : DGNElemCore *DGNCreateSolidHeaderFromGroup(DGNHandle hDGN, int nType,
    1842             :                                            int nSurfType, int nBoundElems,
    1843             :                                            int nNumElems,
    1844             :                                            DGNElemCore **papsElems)
    1845             : 
    1846             : {
    1847           0 :     DGNLoadTCB(hDGN);
    1848             : 
    1849           0 :     if (nNumElems < 1 || papsElems == nullptr)
    1850             :     {
    1851           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1852             :                  "Need at least one element to form a solid.");
    1853           0 :         return nullptr;
    1854             :     }
    1855             : 
    1856             :     /* -------------------------------------------------------------------- */
    1857             :     /*      Collect the total size, and bounds.                             */
    1858             :     /* -------------------------------------------------------------------- */
    1859           0 :     const int nLevel = papsElems[0]->level;
    1860           0 :     int nTotalLength = 6;
    1861           0 :     DGNPoint sMin = {0.0, 0.0, 0.0};
    1862           0 :     DGNPoint sMax = {0.0, 0.0, 0.0};
    1863             : 
    1864           0 :     for (int i = 0; i < nNumElems; i++)
    1865             :     {
    1866           0 :         nTotalLength += papsElems[i]->raw_bytes / 2;
    1867             : 
    1868           0 :         papsElems[i]->complex = TRUE;
    1869           0 :         papsElems[i]->raw_data[0] |= 0x80;
    1870             : 
    1871           0 :         if (papsElems[i]->level != nLevel)
    1872             :         {
    1873           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    1874             :                      "Not all level values matching in a complex set group!");
    1875             :         }
    1876             : 
    1877           0 :         DGNPoint sThisMin = {0.0, 0.0, 0.0};
    1878           0 :         DGNPoint sThisMax = {0.0, 0.0, 0.0};
    1879           0 :         DGNGetElementExtents(hDGN, papsElems[i], &sThisMin, &sThisMax);
    1880           0 :         if (i == 0)
    1881             :         {
    1882           0 :             sMin = sThisMin;
    1883           0 :             sMax = sThisMax;
    1884             :         }
    1885             :         else
    1886             :         {
    1887           0 :             sMin.x = std::min(sMin.x, sThisMin.x);
    1888           0 :             sMin.y = std::min(sMin.y, sThisMin.y);
    1889           0 :             sMin.z = std::min(sMin.z, sThisMin.z);
    1890           0 :             sMax.x = std::max(sMax.x, sThisMax.x);
    1891           0 :             sMax.y = std::max(sMax.y, sThisMax.y);
    1892           0 :             sMax.z = std::max(sMax.z, sThisMax.z);
    1893             :         }
    1894             :     }
    1895             : 
    1896             :     /* -------------------------------------------------------------------- */
    1897             :     /*      Create the corresponding solid header.                          */
    1898             :     /* -------------------------------------------------------------------- */
    1899           0 :     DGNElemCore *psCH = DGNCreateSolidHeaderElem(
    1900             :         hDGN, nType, nSurfType, nBoundElems, nTotalLength, nNumElems);
    1901           0 :     DGNUpdateElemCore(hDGN, psCH, papsElems[0]->level, psCH->graphic_group,
    1902             :                       psCH->color, psCH->weight, psCH->style);
    1903             : 
    1904           0 :     DGNWriteBounds((DGNInfo *)hDGN, psCH, &sMin, &sMax);
    1905             : 
    1906           0 :     return psCH;
    1907             : }
    1908             : 
    1909             : /************************************************************************/
    1910             : /*                      DGNCreateCellHeaderElem()                       */
    1911             : /************************************************************************/
    1912             : 
    1913             : DGNElemCore CPL_DLL *
    1914           0 : DGNCreateCellHeaderElem(DGNHandle hDGN, int nTotLength, const char *pszName,
    1915             :                         short nClass, short *panLevels, DGNPoint *psRangeLow,
    1916             :                         DGNPoint *psRangeHigh, DGNPoint *psOrigin,
    1917             :                         double dfXScale, double dfYScale, double dfRotation)
    1918             : 
    1919             : /**
    1920             :  * Create cell header.
    1921             :  *
    1922             :  * The newly created element will still need to be written to file using
    1923             :  * DGNWriteElement(). Also the level and other core values will be defaulted.
    1924             :  * Use DGNUpdateElemCore() on the element before writing to set these values.
    1925             :  *
    1926             :  * Generally speaking the function DGNCreateCellHeaderFromGroup() should
    1927             :  * be used instead of this function.
    1928             :  *
    1929             :  * @param hDGN the file handle on which the element is to be written.
    1930             :  * @param nTotLength total length of cell in words not including the 38 bytes
    1931             :  * of the cell header that occur before the totlength indicator.
    1932             :  * @param nClass the class value for the cell.
    1933             :  * @param panLevels an array of shorts holding the bit mask of levels in
    1934             :  * effect for this cell.  This array should contain 4 shorts (64 bits).
    1935             :  * @param psRangeLow the cell diagonal origin in original cell file
    1936             :  * coordinates.
    1937             :  * @param psRangeHigh the cell diagonal top left corner in original cell file
    1938             :  * coordinates.
    1939             :  * @param psOrigin the origin of the cell in output file coordinates.
    1940             :  * @param dfXScale the amount of scaling applied in the X dimension in
    1941             :  * mapping from cell file coordinates to output file coordinates.
    1942             :  * @param dfYScale the amount of scaling applied in the Y dimension in
    1943             :  * mapping from cell file coordinates to output file coordinates.
    1944             :  * @param dfRotation the amount of rotation (degrees counterclockwise) in
    1945             :  * mapping from cell coordinates to output file coordinates.
    1946             :  *
    1947             :  * @return the new element (DGNElemCellHeader) or NULL on failure.
    1948             :  */
    1949             : 
    1950             : {
    1951           0 :     DGNInfo *psInfo = (DGNInfo *)hDGN;
    1952             : 
    1953           0 :     DGNLoadTCB(hDGN);
    1954             : 
    1955             :     /* -------------------------------------------------------------------- */
    1956             :     /*      Allocate element.                                               */
    1957             :     /* -------------------------------------------------------------------- */
    1958             :     DGNElemCellHeader *psCH =
    1959           0 :         (DGNElemCellHeader *)CPLCalloc(sizeof(DGNElemCellHeader), 1);
    1960           0 :     DGNElemCore *psCore = &(psCH->core);
    1961             : 
    1962           0 :     DGNInitializeElemCore(hDGN, psCore);
    1963           0 :     psCore->stype = DGNST_CELL_HEADER;
    1964           0 :     psCore->type = DGNT_CELL_HEADER;
    1965             : 
    1966             :     /* -------------------------------------------------------------------- */
    1967             :     /*      Set complex header specific information in the structure.       */
    1968             :     /* -------------------------------------------------------------------- */
    1969           0 :     psCH->totlength = nTotLength;
    1970             : 
    1971             :     /* -------------------------------------------------------------------- */
    1972             :     /*      Setup Raw data for the cell header specific portion.            */
    1973             :     /* -------------------------------------------------------------------- */
    1974           0 :     if (psInfo->dimension == 2)
    1975           0 :         psCore->raw_bytes = 92;
    1976             :     else
    1977           0 :         psCore->raw_bytes = 124;
    1978           0 :     psCore->raw_data = (unsigned char *)CPLCalloc(psCore->raw_bytes, 1);
    1979             : 
    1980           0 :     psCore->raw_data[36] = (unsigned char)(nTotLength % 256);
    1981           0 :     psCore->raw_data[37] = (unsigned char)(nTotLength / 256);
    1982             : 
    1983           0 :     DGNAsciiToRad50(pszName, (unsigned short *)(psCore->raw_data + 38));
    1984           0 :     if (strlen(pszName) > 3)
    1985           0 :         DGNAsciiToRad50(pszName + 3, (unsigned short *)(psCore->raw_data + 40));
    1986             : 
    1987           0 :     psCore->raw_data[42] = (unsigned char)(nClass % 256);
    1988           0 :     psCore->raw_data[43] = (unsigned char)(nClass / 256);
    1989             : 
    1990           0 :     memcpy(psCore->raw_data + 44, panLevels, 8);
    1991             : 
    1992           0 :     if (psInfo->dimension == 2)
    1993             :     {
    1994           0 :         DGNPointToInt(psInfo, psRangeLow, psCore->raw_data + 52);
    1995           0 :         DGNPointToInt(psInfo, psRangeHigh, psCore->raw_data + 60);
    1996             : 
    1997           0 :         DGNInverseTransformPointToInt(psInfo, psOrigin, psCore->raw_data + 84);
    1998             :     }
    1999             :     else
    2000             :     {
    2001           0 :         DGNPointToInt(psInfo, psRangeLow, psCore->raw_data + 52);
    2002           0 :         DGNPointToInt(psInfo, psRangeHigh, psCore->raw_data + 64);
    2003             : 
    2004           0 :         DGNInverseTransformPointToInt(psInfo, psOrigin, psCore->raw_data + 112);
    2005             :     }
    2006             : 
    2007             :     /* -------------------------------------------------------------------- */
    2008             :     /*      Produce a transformation matrix that approximates the           */
    2009             :     /*      requested scaling and rotation.                                 */
    2010             :     /* -------------------------------------------------------------------- */
    2011           0 :     if (psInfo->dimension == 2)
    2012             :     {
    2013             :         long anTrans[4];
    2014           0 :         double cos_a = cos(-dfRotation * M_PI / 180.0);
    2015           0 :         double sin_a = sin(-dfRotation * M_PI / 180.0);
    2016             : 
    2017           0 :         anTrans[0] = (long)(cos_a * dfXScale * 214748);
    2018           0 :         anTrans[1] = (long)(sin_a * dfYScale * 214748);
    2019           0 :         anTrans[2] = (long)(-sin_a * dfXScale * 214748);
    2020           0 :         anTrans[3] = (long)(cos_a * dfYScale * 214748);
    2021             : 
    2022           0 :         DGN_WRITE_INT32(anTrans[0], psCore->raw_data + 68);
    2023           0 :         DGN_WRITE_INT32(anTrans[1], psCore->raw_data + 72);
    2024           0 :         DGN_WRITE_INT32(anTrans[2], psCore->raw_data + 76);
    2025           0 :         DGN_WRITE_INT32(anTrans[3], psCore->raw_data + 80);
    2026             :     }
    2027             :     else
    2028             :     {
    2029             :         long anTrans[9];
    2030             : 
    2031             :         // NOTE: This is still just rotation in the plane
    2032           0 :         double cos_a = cos(-dfRotation * M_PI / 180.0);
    2033           0 :         double sin_a = sin(-dfRotation * M_PI / 180.0);
    2034           0 :         double dfZScale = 1.0;  // Should we get this from somewhere?
    2035             : 
    2036           0 :         anTrans[0] = (long)(cos_a * dfXScale * 214748);
    2037           0 :         anTrans[1] = (long)(sin_a * dfYScale * 214748);
    2038           0 :         anTrans[2] = (long)(sin_a * dfZScale * 214748);
    2039             : 
    2040           0 :         anTrans[3] = (long)(-sin_a * dfXScale * 214748);
    2041           0 :         anTrans[4] = (long)(cos_a * dfYScale * 214748);
    2042           0 :         anTrans[5] = (long)(sin_a * dfZScale * 214748);
    2043             : 
    2044           0 :         anTrans[6] = (long)(-sin_a * dfXScale * 214748);
    2045           0 :         anTrans[7] = (long)(-sin_a * dfYScale * 214748);
    2046           0 :         anTrans[8] = (long)(cos_a * dfZScale * 214748);
    2047             : 
    2048           0 :         DGN_WRITE_INT32(anTrans[0], psCore->raw_data + 76);
    2049           0 :         DGN_WRITE_INT32(anTrans[1], psCore->raw_data + 80);
    2050           0 :         DGN_WRITE_INT32(anTrans[2], psCore->raw_data + 84);
    2051           0 :         DGN_WRITE_INT32(anTrans[3], psCore->raw_data + 88);
    2052           0 :         DGN_WRITE_INT32(anTrans[4], psCore->raw_data + 92);
    2053           0 :         DGN_WRITE_INT32(anTrans[5], psCore->raw_data + 96);
    2054           0 :         DGN_WRITE_INT32(anTrans[6], psCore->raw_data + 100);
    2055           0 :         DGN_WRITE_INT32(anTrans[7], psCore->raw_data + 104);
    2056           0 :         DGN_WRITE_INT32(anTrans[8], psCore->raw_data + 108);
    2057             :     }
    2058             : 
    2059             :     /* -------------------------------------------------------------------- */
    2060             :     /*      Set the core raw data.                                          */
    2061             :     /* -------------------------------------------------------------------- */
    2062           0 :     DGNUpdateElemCoreExtended(hDGN, psCore);
    2063             : 
    2064           0 :     return (DGNElemCore *)psCH;
    2065             : }
    2066             : 
    2067             : /************************************************************************/
    2068             : /*                           DGNPointToInt()                            */
    2069             : /*                                                                      */
    2070             : /*      Convert a point directly to integer coordinates and write to    */
    2071             : /*      the indicate memory location.  Intended to be used for the      */
    2072             : /*      range section of the CELL HEADER.                               */
    2073             : /************************************************************************/
    2074             : 
    2075           0 : static void DGNPointToInt(DGNInfo *psDGN, DGNPoint *psPoint,
    2076             :                           unsigned char *pabyTarget)
    2077             : 
    2078             : {
    2079           0 :     double adfCT[3] = {psPoint->x, psPoint->y, psPoint->z};
    2080             : 
    2081           0 :     const int nIter = std::min(3, psDGN->dimension);
    2082           0 :     for (int i = 0; i < nIter; i++)
    2083             :     {
    2084           0 :         GInt32 nCTI = static_cast<GInt32>(
    2085           0 :             std::max(-2147483647.0, std::min(2147483647.0, adfCT[i])));
    2086             :         unsigned char abyCTI[4];
    2087           0 :         memcpy(abyCTI, &nCTI, sizeof(GInt32));
    2088             : 
    2089             : #ifdef WORDS_BIGENDIAN
    2090             :         pabyTarget[i * 4 + 0] = abyCTI[1];
    2091             :         pabyTarget[i * 4 + 1] = abyCTI[0];
    2092             :         pabyTarget[i * 4 + 2] = abyCTI[3];
    2093             :         pabyTarget[i * 4 + 3] = abyCTI[2];
    2094             : #else
    2095           0 :         pabyTarget[i * 4 + 3] = abyCTI[1];
    2096           0 :         pabyTarget[i * 4 + 2] = abyCTI[0];
    2097           0 :         pabyTarget[i * 4 + 1] = abyCTI[3];
    2098           0 :         pabyTarget[i * 4 + 0] = abyCTI[2];
    2099             : #endif
    2100             :     }
    2101           0 : }
    2102             : 
    2103             : /************************************************************************/
    2104             : /*                    DGNCreateCellHeaderFromGroup()                    */
    2105             : /************************************************************************/
    2106             : 
    2107             : /**
    2108             :  * Create cell header from a group of elements.
    2109             :  *
    2110             :  * The newly created element will still need to be written to file using
    2111             :  * DGNWriteElement(). Also the level and other core values will be defaulted.
    2112             :  * Use DGNUpdateElemCore() on the element before writing to set these values.
    2113             :  *
    2114             :  * This function will compute the total length, bounding box, and diagonal
    2115             :  * range values from the set of provided elements.  Note that the proper
    2116             :  * diagonal range values will only be written if 1.0 is used for the x and y
    2117             :  * scale values, and 0.0 for the rotation.  Use of other values will result
    2118             :  * in incorrect scaling handles being presented to the user in Microstation
    2119             :  * when they select the element.
    2120             :  *
    2121             :  * @param hDGN the file handle on which the element is to be written.
    2122             :  * @param nClass the class value for the cell.
    2123             :  * @param panLevels an array of shorts holding the bit mask of levels in
    2124             :  * effect for this cell.  This array should contain 4 shorts (64 bits).
    2125             :  * This array would normally be passed in as NULL, and the function will
    2126             :  * build a mask from the passed list of elements.
    2127             :  * @param psOrigin the origin of the cell in output file coordinates.
    2128             :  * @param dfXScale the amount of scaling applied in the X dimension in
    2129             :  * mapping from cell file coordinates to output file coordinates.
    2130             :  * @param dfYScale the amount of scaling applied in the Y dimension in
    2131             :  * mapping from cell file coordinates to output file coordinates.
    2132             :  * @param dfRotation the amount of rotation (degrees counterclockwise) in
    2133             :  * mapping from cell coordinates to output file coordinates.
    2134             :  *
    2135             :  * @return the new element (DGNElemCellHeader) or NULL on failure.
    2136             :  */
    2137             : 
    2138           0 : DGNElemCore *DGNCreateCellHeaderFromGroup(DGNHandle hDGN, const char *pszName,
    2139             :                                           short nClass, short *panLevels,
    2140             :                                           int nNumElems,
    2141             :                                           DGNElemCore **papsElems,
    2142             :                                           DGNPoint *psOrigin, double dfXScale,
    2143             :                                           double dfYScale, double dfRotation)
    2144             : 
    2145             : {
    2146           0 :     DGNInfo *psInfo = (DGNInfo *)hDGN;
    2147             : 
    2148           0 :     DGNLoadTCB(hDGN);
    2149             : 
    2150           0 :     if (nNumElems < 1 || papsElems == nullptr)
    2151             :     {
    2152           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2153             :                  "Need at least one element to form a cell.");
    2154           0 :         return nullptr;
    2155             :     }
    2156             : 
    2157             :     /* -------------------------------------------------------------------- */
    2158             :     /*      Collect the total size, and bounds.                             */
    2159             :     /* -------------------------------------------------------------------- */
    2160           0 :     int nTotalLength = psInfo->dimension == 2 ? 27 : 43;
    2161             :     // nLevel = papsElems[0]->level;x
    2162           0 :     DGNPoint sMin = {0.0, 0.0, 0.0};
    2163           0 :     DGNPoint sMax = {0.0, 0.0, 0.0};
    2164           0 :     unsigned char abyLevelsOccurring[8] = {0, 0, 0, 0, 0, 0, 0, 0};
    2165             : 
    2166           0 :     for (int i = 0; i < nNumElems; i++)
    2167             :     {
    2168           0 :         nTotalLength += papsElems[i]->raw_bytes / 2;
    2169             : 
    2170             :         /* mark as complex */
    2171           0 :         papsElems[i]->complex = TRUE;
    2172           0 :         papsElems[i]->raw_data[0] |= 0x80;
    2173             : 
    2174             :         /* establish level */
    2175           0 :         int nLevel = papsElems[i]->level;
    2176           0 :         nLevel = std::max(1, std::min(nLevel, 64));
    2177           0 :         abyLevelsOccurring[(nLevel - 1) >> 3] |= (0x1 << ((nLevel - 1) & 0x7));
    2178             : 
    2179           0 :         DGNPoint sThisMin = {0.0, 0.0, 0.0};
    2180           0 :         DGNPoint sThisMax = {0.0, 0.0, 0.0};
    2181           0 :         DGNGetElementExtents(hDGN, papsElems[i], &sThisMin, &sThisMax);
    2182           0 :         if (i == 0)
    2183             :         {
    2184           0 :             sMin = sThisMin;
    2185           0 :             sMax = sThisMax;
    2186             :         }
    2187             :         else
    2188             :         {
    2189           0 :             sMin.x = std::min(sMin.x, sThisMin.x);
    2190           0 :             sMin.y = std::min(sMin.y, sThisMin.y);
    2191           0 :             sMin.z = std::min(sMin.z, sThisMin.z);
    2192           0 :             sMax.x = std::max(sMax.x, sThisMax.x);
    2193           0 :             sMax.y = std::max(sMax.y, sThisMax.y);
    2194           0 :             sMax.z = std::max(sMax.z, sThisMax.z);
    2195             :         }
    2196             :     }
    2197             : 
    2198             : /* -------------------------------------------------------------------- */
    2199             : /*      It seems that the range needs to be adjusted according to       */
    2200             : /*      the rotation and scaling.                                       */
    2201             : /*                                                                      */
    2202             : /*      NOTE: Omitting code ... this is already done in                 */
    2203             : /*      DGNInverseTransformPoint() called from DGNWriteBounds().        */
    2204             : /* -------------------------------------------------------------------- */
    2205             : #ifdef notdef
    2206             :     sMin.x -= psOrigin->x;
    2207             :     sMin.y -= psOrigin->y;
    2208             :     sMin.z -= psOrigin->z;
    2209             :     sMax.x -= psOrigin->x;
    2210             :     sMax.y -= psOrigin->y;
    2211             :     sMax.z -= psOrigin->z;
    2212             : 
    2213             :     sMin.x /= ((DGNInfo *)hDGN)->scale;
    2214             :     sMin.y /= ((DGNInfo *)hDGN)->scale;
    2215             :     sMin.z /= ((DGNInfo *)hDGN)->scale;
    2216             :     sMax.x /= ((DGNInfo *)hDGN)->scale;
    2217             :     sMax.y /= ((DGNInfo *)hDGN)->scale;
    2218             :     sMax.z /= ((DGNInfo *)hDGN)->scale;
    2219             : #endif
    2220             : 
    2221             :     /* -------------------------------------------------------------------- */
    2222             :     /*      Create the corresponding cell header.                           */
    2223             :     /* -------------------------------------------------------------------- */
    2224           0 :     if (panLevels == nullptr)
    2225           0 :         panLevels = (short *)abyLevelsOccurring + 0;
    2226             : 
    2227           0 :     DGNElemCore *psCH = DGNCreateCellHeaderElem(
    2228             :         hDGN, nTotalLength, pszName, nClass, panLevels, &sMin, &sMax, psOrigin,
    2229             :         dfXScale, dfYScale, dfRotation);
    2230           0 :     DGNWriteBounds((DGNInfo *)hDGN, psCH, &sMin, &sMax);
    2231             : 
    2232           0 :     return psCH;
    2233             : }
    2234             : 
    2235             : /************************************************************************/
    2236             : /*                            DGNAddMSLink()                            */
    2237             : /************************************************************************/
    2238             : 
    2239             : /**
    2240             :  * Add a database link to element.
    2241             :  *
    2242             :  * The target element must already have raw_data loaded, and it will be
    2243             :  * resized (see DGNResizeElement()) as needed for the new attribute data.
    2244             :  * Note that the element is not written to disk immediate.  Use
    2245             :  * DGNWriteElement() for that.
    2246             :  *
    2247             :  * @param hDGN the file to which the element corresponds.
    2248             :  * @param psElement the element being updated.
    2249             :  * @param nLinkageType link type (DGNLT_*).  Usually one of DGNLT_DMRS,
    2250             :  * DGNLT_INFORMIX, DGNLT_ODBC, DGNLT_ORACLE, DGNLT_RIS, DGNLT_SYBASE,
    2251             :  * or DGNLT_XBASE.
    2252             :  * @param nEntityNum indicator of the table referenced on target database.
    2253             :  * @param nMSLink indicator of the record referenced on target table.
    2254             :  *
    2255             :  * @return -1 on failure, or the link index.
    2256             :  */
    2257             : 
    2258          44 : int DGNAddMSLink(DGNHandle hDGN, DGNElemCore *psElement, int nLinkageType,
    2259             :                  int nEntityNum, int nMSLink)
    2260             : 
    2261             : {
    2262          44 :     unsigned char abyLinkage[32] = {};
    2263          44 :     int nLinkageSize = 0;
    2264             : 
    2265          44 :     if (nLinkageType == DGNLT_DMRS)
    2266             :     {
    2267           0 :         nLinkageSize = 8;
    2268           0 :         abyLinkage[0] = 0x00;
    2269           0 :         abyLinkage[1] = 0x00;
    2270           0 :         abyLinkage[2] = (GByte)(nEntityNum % 256);
    2271           0 :         abyLinkage[3] = (GByte)(nEntityNum / 256);
    2272           0 :         abyLinkage[4] = (GByte)(nMSLink % 256);
    2273           0 :         abyLinkage[5] = (GByte)((nMSLink / 256) % 256);
    2274           0 :         abyLinkage[6] = (GByte)(nMSLink / 65536);
    2275           0 :         abyLinkage[7] = 0x01;
    2276             :     }
    2277             :     else
    2278             :     {
    2279          44 :         nLinkageSize = 16;
    2280          44 :         abyLinkage[0] = 0x07;
    2281          44 :         abyLinkage[1] = 0x10;
    2282          44 :         abyLinkage[2] = (GByte)(nLinkageType % 256);
    2283          44 :         abyLinkage[3] = (GByte)(nLinkageType / 256);
    2284          44 :         abyLinkage[4] = (GByte)(0x81);
    2285          44 :         abyLinkage[5] = (GByte)(0x0F);
    2286          44 :         abyLinkage[6] = (GByte)(nEntityNum % 256);
    2287          44 :         abyLinkage[7] = (GByte)(nEntityNum / 256);
    2288          44 :         abyLinkage[8] = (GByte)(nMSLink % 256);
    2289          44 :         abyLinkage[9] = (GByte)((nMSLink / 256) % 256);
    2290          44 :         abyLinkage[10] = (GByte)((nMSLink / 65536) % 256);
    2291          44 :         abyLinkage[11] = (GByte)(nMSLink / 16777216);
    2292          44 :         abyLinkage[12] = 0x00;
    2293          44 :         abyLinkage[13] = 0x00;
    2294          44 :         abyLinkage[14] = 0x00;
    2295          44 :         abyLinkage[15] = 0x00;
    2296             :     }
    2297             : 
    2298          88 :     return DGNAddRawAttrLink(hDGN, psElement, nLinkageSize, abyLinkage);
    2299             : }
    2300             : 
    2301             : /************************************************************************/
    2302             : /*                         DGNAddRawAttrLink()                          */
    2303             : /************************************************************************/
    2304             : 
    2305             : /**
    2306             :  * Add a raw attribute linkage to element.
    2307             :  *
    2308             :  * Given a raw data buffer, append it to this element as an attribute linkage
    2309             :  * without trying to interpret the linkage data.
    2310             :  *
    2311             :  * The target element must already have raw_data loaded, and it will be
    2312             :  * resized (see DGNResizeElement()) as needed for the new attribute data.
    2313             :  * Note that the element is not written to disk immediate.  Use
    2314             :  * DGNWriteElement() for that.
    2315             :  *
    2316             :  * This function will take care of updating the "totlength" field of
    2317             :  * complex chain or shape headers to account for the extra attribute space
    2318             :  * consumed in the header element.
    2319             :  *
    2320             :  * @param hDGN the file to which the element corresponds.
    2321             :  * @param psElement the element being updated.
    2322             :  * @param nLinkSize the size of the linkage in bytes.
    2323             :  * @param pabyRawLinkData the raw linkage data (nLinkSize bytes worth).
    2324             :  *
    2325             :  * @return -1 on failure, or the link index.
    2326             :  */
    2327             : 
    2328          45 : int DGNAddRawAttrLink(DGNHandle hDGN, DGNElemCore *psElement, int nLinkSize,
    2329             :                       unsigned char *pabyRawLinkData)
    2330             : 
    2331             : {
    2332          45 :     if (nLinkSize % 2 == 1)
    2333           0 :         nLinkSize++;
    2334             : 
    2335          45 :     if (psElement->size + nLinkSize > 768)
    2336             :     {
    2337           0 :         CPLError(CE_Failure, CPLE_ElementTooBig,
    2338             :                  "Attempt to add %d byte linkage to element exceeds maximum"
    2339             :                  " element size.",
    2340             :                  nLinkSize);
    2341           0 :         return -1;
    2342             :     }
    2343             : 
    2344             :     /* -------------------------------------------------------------------- */
    2345             :     /*      Ensure the attribute linkage bit is set.                        */
    2346             :     /* -------------------------------------------------------------------- */
    2347          45 :     psElement->properties |= DGNPF_ATTRIBUTES;
    2348             : 
    2349             :     /* -------------------------------------------------------------------- */
    2350             :     /*      Append the attribute linkage to the linkage area.               */
    2351             :     /* -------------------------------------------------------------------- */
    2352          45 :     psElement->attr_bytes += nLinkSize;
    2353          90 :     psElement->attr_data = (unsigned char *)CPLRealloc(psElement->attr_data,
    2354          45 :                                                        psElement->attr_bytes);
    2355             : 
    2356          45 :     memcpy(psElement->attr_data + (psElement->attr_bytes - nLinkSize),
    2357             :            pabyRawLinkData, nLinkSize);
    2358             : 
    2359             :     /* -------------------------------------------------------------------- */
    2360             :     /*      Grow the raw data, if we have rawdata.                          */
    2361             :     /* -------------------------------------------------------------------- */
    2362          45 :     psElement->raw_bytes += nLinkSize;
    2363          45 :     psElement->raw_data =
    2364          45 :         (unsigned char *)CPLRealloc(psElement->raw_data, psElement->raw_bytes);
    2365             : 
    2366          45 :     memcpy(psElement->raw_data + (psElement->raw_bytes - nLinkSize),
    2367             :            pabyRawLinkData, nLinkSize);
    2368             : 
    2369             :     /* -------------------------------------------------------------------- */
    2370             :     /*      If the element is a shape or chain complex header, then we      */
    2371             :     /*      need to increase the total complex group size appropriately.    */
    2372             :     /* -------------------------------------------------------------------- */
    2373          45 :     if (psElement->stype == DGNST_COMPLEX_HEADER ||
    2374          43 :         psElement->stype == DGNST_TEXT_NODE)  // compatible structures
    2375             :     {
    2376           2 :         DGNElemComplexHeader *psCT = (DGNElemComplexHeader *)psElement;
    2377             : 
    2378           2 :         psCT->totlength += (nLinkSize / 2);
    2379             : 
    2380           2 :         psElement->raw_data[36] = (unsigned char)(psCT->totlength % 256);
    2381           2 :         psElement->raw_data[37] = (unsigned char)(psCT->totlength / 256);
    2382             :     }
    2383             : 
    2384             :     /* -------------------------------------------------------------------- */
    2385             :     /*      Ensure everything is updated properly, including element        */
    2386             :     /*      length and properties.                                          */
    2387             :     /* -------------------------------------------------------------------- */
    2388          45 :     DGNUpdateElemCoreExtended(hDGN, psElement);
    2389             : 
    2390             :     /* -------------------------------------------------------------------- */
    2391             :     /*      Figure out what the linkage index is.                           */
    2392             :     /* -------------------------------------------------------------------- */
    2393          45 :     int iLinkage = 0;  // Used after for.
    2394          46 :     for (;; iLinkage++)
    2395             :     {
    2396          91 :         if (DGNGetLinkage(hDGN, psElement, iLinkage, nullptr, nullptr, nullptr,
    2397          91 :                           nullptr) == nullptr)
    2398          45 :             break;
    2399             :     }
    2400             : 
    2401          45 :     return iLinkage - 1;
    2402             : }
    2403             : 
    2404             : /************************************************************************/
    2405             : /*                        DGNAddShapeFileInfo()                         */
    2406             : /************************************************************************/
    2407             : 
    2408             : /**
    2409             :  * Add a shape fill attribute linkage.
    2410             :  *
    2411             :  * The target element must already have raw_data loaded, and it will be
    2412             :  * resized (see DGNResizeElement()) as needed for the new attribute data.
    2413             :  * Note that the element is not written to disk immediate.  Use
    2414             :  * DGNWriteElement() for that.
    2415             :  *
    2416             :  * @param hDGN the file to which the element corresponds.
    2417             :  * @param psElement the element being updated.
    2418             :  * @param nColor fill color (color index from palette).
    2419             :  *
    2420             :  * @return -1 on failure, or the link index.
    2421             :  */
    2422             : 
    2423           0 : int DGNAddShapeFillInfo(DGNHandle hDGN, DGNElemCore *psElement, int nColor)
    2424             : 
    2425             : {
    2426           0 :     unsigned char abyFillInfo[16] = {0x07, 0x10, 0x41, 0x00, 0x02, 0x08,
    2427             :                                      0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
    2428             :                                      0x00, 0x00, 0x00, 0x00};
    2429             : 
    2430           0 :     abyFillInfo[8] = (unsigned char)nColor;
    2431             : 
    2432             :     // coverity[overrun-buffer-arg]
    2433           0 :     return DGNAddRawAttrLink(hDGN, psElement, 16, abyFillInfo);
    2434             : }

Generated by: LCOV version 1.14