//
// PDF dictionary functions for PDFio.
//
// Copyright © 2021-2026 by Michael R Sweet.
//
// Licensed under Apache License v2.0.  See the file "LICENSE" for more
// information.
//

#include "pdfio-private.h"


//
// Local functions...
//

static int	compare_pairs(_pdfio_pair_t *a, _pdfio_pair_t *b);


//
// 'pdfioDictClear()' - Remove a key/value pair from a dictionary.
//
// @since PDFio v1.4@
//

bool					// O - `true` if cleared, `false` otherwise
pdfioDictClear(pdfio_dict_t *dict,	// I - Dictionary
               const char   *key)	// I - Key
{
  size_t	idx;			// Index into pairs
  _pdfio_pair_t	*pair,			// Current pair
		pkey;			// Search key


  PDFIO_DEBUG("pdfioDictClear(dict=%p, key=\"%s\")\n", (void *)dict, key);

  if (!dict || !key)
    return (false);

  // See if the key is already set...
  if (dict->num_pairs > 0)
  {
    pkey.key = key;

    if ((pair = (_pdfio_pair_t *)bsearch(&pkey, dict->pairs, dict->num_pairs, sizeof(_pdfio_pair_t), (int (*)(const void *, const void *))compare_pairs)) != NULL)
    {
      // Yes, remove it...
      if (pair->value.type == PDFIO_VALTYPE_BINARY)
        free(pair->value.value.binary.data);

      idx = (size_t)(pair - dict->pairs);
      dict->num_pairs --;

      if (idx < dict->num_pairs)
        memmove(pair, pair + 1, (dict->num_pairs - idx) * sizeof(_pdfio_pair_t));

      return (true);
    }
  }

  return (false);
}


//
// 'pdfioDictCopy()' - Copy a dictionary to a PDF file.
//

pdfio_dict_t *				// O - New dictionary
pdfioDictCopy(pdfio_file_t *pdf,	// I - PDF file
              pdfio_dict_t *dict)	// I - Original dictionary
{
  pdfio_dict_t		*ndict;		// New dictionary
  size_t		i;		// Looping var
  _pdfio_pair_t		*p;		// Current source pair
  const char		*key;		// Current destination key
  _pdfio_value_t	v;		// Current destination value


  PDFIO_DEBUG("pdfioDictCopy(pdf=%p, dict=%p(%p))\n", (void *)pdf, (void *)dict, dict ? (void *)dict->pdf : NULL);

  // Range check input...
  if (!pdf || !dict)
    return (NULL);

  // Create the new dictionary...
  if ((ndict = pdfioDictCreate(pdf)) == NULL)
    return (NULL);

  // Pre-allocate the pairs array to make this a little faster...
  if ((ndict->pairs = (_pdfio_pair_t *)malloc(dict->num_pairs * sizeof(_pdfio_pair_t))) == NULL)
    return (NULL);			// Let pdfioFileClose do the cleanup...

  ndict->alloc_pairs = dict->num_pairs;

  // Copy and add each of the source dictionary's key/value pairs...
  for (i = dict->num_pairs, p = dict->pairs; i > 0; i --, p ++)
  {
    PDFIO_DEBUG("pdfioDictCopy: key=\"%s\", value.type=%d\n", p->key, p->value.type);

    if (!strcmp(p->key, "Length") && p->value.type == PDFIO_VALTYPE_INDIRECT && dict->pdf != pdf)
    {
      // Don't use indirect stream lengths for copied objects...
      pdfio_obj_t *lenobj = pdfioFileFindObj(dict->pdf, p->value.value.indirect.number);
					// Length object

      v.type = PDFIO_VALTYPE_NUMBER;
      if (lenobj)
      {
        if (lenobj->value.type == PDFIO_VALTYPE_NONE)
        {
          if (!_pdfioObjLoad(lenobj))
          {
            PDFIO_DEBUG("pdfioDictCopy: Unable to copy value of \"%s\", returning NULL.\n", p->key);
            return (NULL);
          }
        }

	v.value.number = lenobj->value.value.number;
      }
      else
      {
        v.value.number = 0.0;
      }

      PDFIO_DEBUG("pdfioDictCopy: Length is %.0f.\n", v.value.number);
    }
    else if (!_pdfioValueCopy(pdf, &v, dict->pdf, &p->value))
    {
      PDFIO_DEBUG("pdfioDictCopy: Unable to copy value of \"%s\", returning NULL.\n", p->key);
      return (NULL);			// Let pdfioFileClose do the cleanup...
    }

    if (_pdfioStringIsAllocated(dict->pdf, p->key))
      key = pdfioStringCreate(pdf, p->key);
    else
      key = p->key;

    if (!key)
      return (NULL);			// Let pdfioFileClose do the cleanup...

    // Cannot fail since we already allocated space for the pairs...
    _pdfioDictSetValue(ndict, key, &v);
  }

  // Successfully copied the dictionary, so return it...
  return (ndict);
}


//
// 'pdfioDictCreate()' - Create a dictionary to hold key/value pairs.
//

pdfio_dict_t *				// O - New dictionary
pdfioDictCreate(pdfio_file_t *pdf)	// I - PDF file
{
  pdfio_dict_t	*dict;			// New dictionary


  if (!pdf)
    return (NULL);

  if ((dict = (pdfio_dict_t *)calloc(1, sizeof(pdfio_dict_t))) == NULL)
    return (NULL);

  dict->pdf = pdf;

  if (pdf->num_dicts >= pdf->alloc_dicts)
  {
    pdfio_dict_t **temp = (pdfio_dict_t **)realloc(pdf->dicts, (pdf->alloc_dicts + 16) * sizeof(pdfio_dict_t *));

    if (!temp)
    {
      free(dict);
      return (NULL);
    }

    pdf->dicts       = temp;
    pdf->alloc_dicts += 16;
  }

  pdf->dicts[pdf->num_dicts ++] = dict;

  return (dict);
}


//
// '_pdfioDictDecrypt()' - Decrypt the values in a dictionary.
//

bool					// O - `true` on success, `false` on error
_pdfioDictDecrypt(pdfio_file_t *pdf,	// I - PDF file
                  pdfio_obj_t  *obj,	// I - Object
                  pdfio_dict_t *dict,	// I - Dictionary
                  size_t       depth)	// I - Depth
{
  size_t	i;			// Looping var
  _pdfio_pair_t	*pair;			// Current pair


  for (i = dict->num_pairs, pair = dict->pairs; i > 0; i --, pair ++)
  {
    if (strcmp(pair->key, "ID") && !_pdfioValueDecrypt(pdf, obj, &pair->value, depth + 1))
      return (false);
  }

  return (true);
}


//
// '_pdfioDictDebug()' - Dump a dictionary to stderr.
//

void
_pdfioDictDebug(pdfio_dict_t *dict,	// I - Dictionary
                FILE         *fp)	// I - Output file
{
  size_t	i;			// Looping var
  _pdfio_pair_t	*pair;			// Current pair


  if (!dict)
    return;

  for (i = dict->num_pairs, pair = dict->pairs; i > 0; i --, pair ++)
  {
    fprintf(fp, "/%s", pair->key);
    _pdfioValueDebug(&pair->value, fp);
  }
}


//
// '_pdfioDictDelete()' - Free the memory used by a dictionary.
//

void
_pdfioDictDelete(pdfio_dict_t *dict)	// I - Dictionary
{
  if (dict)
  {
    size_t	i;			// Looping var
    _pdfio_pair_t *pair;		// Current pair

    for (i = dict->num_pairs, pair = dict->pairs; i > 0; i --, pair ++)
    {
      if (pair->value.type == PDFIO_VALTYPE_BINARY)
        free(pair->value.value.binary.data);
    }

    free(dict->pairs);
  }

  free(dict);
}


//
// 'pdfioDictGetArray()' - Get a key array value from a dictionary.
//

pdfio_array_t *				// O - Value
pdfioDictGetArray(pdfio_dict_t *dict,	// I - Dictionary
                  const char   *key)	// I - Key
{
  _pdfio_value_t *value = _pdfioDictGetValue(dict, key);


  if (value && value->type == PDFIO_VALTYPE_ARRAY)
    return (value->value.array);
  else
    return (NULL);
}


//
// 'pdfioDictGetBinary()' - Get a key binary string value from a dictionary.
//

unsigned char *				// O - Value
pdfioDictGetBinary(pdfio_dict_t *dict,	// I - Dictionary
                   const char   *key,	// I - Key
                   size_t       *length)// O - Length of value
{
  _pdfio_value_t *value = _pdfioDictGetValue(dict, key);


  if (!length)
    return (NULL);

  if (value && value->type == PDFIO_VALTYPE_BINARY)
  {
    *length = value->value.binary.datalen;
    return (value->value.binary.data);
  }
  else if (value && value->type == PDFIO_VALTYPE_STRING)
  {
    *length = strlen(value->value.string);
    return ((unsigned char *)value->value.string);
  }
  else
  {
    *length = 0;
    return (NULL);
  }
}


//
// 'pdfioDictGetBoolean()' - Get a key boolean value from a dictionary.
//

bool					// O - Value
pdfioDictGetBoolean(pdfio_dict_t *dict,	// I - Dictionary
                    const char   *key)	// I - Key
{
  _pdfio_value_t *value = _pdfioDictGetValue(dict, key);


  if (value && value->type == PDFIO_VALTYPE_BOOLEAN)
    return (value->value.boolean);
  else
    return (false);
}


//
// 'pdfioDictGetDate()' - Get a date value from a dictionary.
//

time_t					// O - Value
pdfioDictGetDate(pdfio_dict_t *dict,	// I - Dictionary
                 const char   *key)	// I - Key
{
  _pdfio_value_t *value = _pdfioDictGetValue(dict, key);


  if (value && value->type == PDFIO_VALTYPE_DATE)
    return (value->value.date);
  else
    return (0);
}


//
// 'pdfioDictGetDict()' - Get a key dictionary value from a dictionary.
//

pdfio_dict_t *				// O - Value
pdfioDictGetDict(pdfio_dict_t *dict,	// I - Dictionary
                 const char   *key)	// I - Key
{
  _pdfio_value_t *value = _pdfioDictGetValue(dict, key);


  if (value && value->type == PDFIO_VALTYPE_DICT)
    return (value->value.dict);
  else
    return (NULL);
}


//
// 'pdfioDictGetKey()' - Get the key for the specified pair.
//
// @since PDFio v1.4@
//

const char *				// O - Key for specified pair
pdfioDictGetKey(pdfio_dict_t *dict,	// I - Dictionary
                size_t       n)		// I - Pair index (`0`-based)
{
  return ((dict && n < dict->num_pairs) ? dict->pairs[n].key : NULL);
}


//
// 'pdfioDictGetName()' - Get a key name value from a dictionary.
//

const char *				// O - Value
pdfioDictGetName(pdfio_dict_t *dict,	// I - Dictionary
                 const char   *key)	// I - Key
{
  _pdfio_value_t *value = _pdfioDictGetValue(dict, key);


  if (value && value->type == PDFIO_VALTYPE_NAME)
    return (value->value.name);
  else
    return (NULL);
}


//
// 'pdfioDictGetNumPairs()' - Get the number of key/value pairs in a dictionary.
//
// @since PDFio v1.4@
//

size_t					// O - Number of pairs
pdfioDictGetNumPairs(pdfio_dict_t *dict)// I - Dictionary
{
  return (dict ? dict->num_pairs : 0);
}


//
// 'pdfioDictGetNumber()' - Get a key number value from a dictionary.
//

double					// O - Value
pdfioDictGetNumber(pdfio_dict_t *dict,	// I - Dictionary
                   const char   *key)	// I - Key
{
  _pdfio_value_t *value = _pdfioDictGetValue(dict, key);


  if (value && value->type == PDFIO_VALTYPE_NUMBER)
    return (value->value.number);
  else
    return (0.0);
}


//
// 'pdfioDictGetObj()' - Get a key indirect object value from a dictionary.
//

pdfio_obj_t *				// O - Value
pdfioDictGetObj(pdfio_dict_t *dict,	// I - Dictionary
                const char   *key)	// I - Key
{
  _pdfio_value_t *value = _pdfioDictGetValue(dict, key);


  if (value && value->type == PDFIO_VALTYPE_INDIRECT)
    return (pdfioFileFindObj(dict->pdf, value->value.indirect.number));
  else
    return (NULL);
}


//
// 'pdfioDictGetRect()' - Get a key rectangle value from a dictionary.
//

pdfio_rect_t *				// O - Rectangle
pdfioDictGetRect(pdfio_dict_t *dict,	// I - Dictionary
                 const char   *key,	// I - Key
                 pdfio_rect_t *rect)	// I - Rectangle
{
  _pdfio_value_t *value = _pdfioDictGetValue(dict, key);


  if (value && value->type == PDFIO_VALTYPE_ARRAY && pdfioArrayGetSize(value->value.array) == 4)
  {
    rect->x1 = pdfioArrayGetNumber(value->value.array, 0);
    rect->y1 = pdfioArrayGetNumber(value->value.array, 1);
    rect->x2 = pdfioArrayGetNumber(value->value.array, 2);
    rect->y2 = pdfioArrayGetNumber(value->value.array, 3);
    return (rect);
  }
  else
  {
    memset(rect, 0, sizeof(pdfio_rect_t));
    return (NULL);
  }
}


//
// 'pdfioDictGetString()' - Get a key string value from a dictionary.
//

const char *				// O - Value
pdfioDictGetString(pdfio_dict_t *dict,	// I - Dictionary
                   const char   *key)	// I - Key
{
  _pdfio_value_t *value = _pdfioDictGetValue(dict, key);


  PDFIO_DEBUG("pdfioDictGetString(dict=%p, key=\"%s\")\n", (void *)dict, key);
  PDFIO_DEBUG("pdfioDictGetString: value=%p(type=%d)\n", (void *)value, value ? value->type : 0);

  if (value && value->type == PDFIO_VALTYPE_STRING)
  {
    PDFIO_DEBUG("pdfioDictGetString: Returning \"%s\".\n", value->value.string);
    return (value->value.string);
  }
  else if (value && value->type == PDFIO_VALTYPE_BINARY && value->value.binary.datalen < 4096)
  {
    // Convert binary string to regular string...
    char	temp[4096];		// Temporary UTF-8 string

    if (!(value->value.binary.datalen & 1) && (!memcmp(value->value.binary.data, "\376\377", 2) || !memcmp(value->value.binary.data, "\377\376", 2)))
    {
      // Copy UTF-16...
      PDFIO_DEBUG("pdfioDictGetString: Converting UTF-16 to UTF-8 string.\n");

      _pdfio_utf16cpy(temp, value->value.binary.data, value->value.binary.datalen, sizeof(temp));
    }
    else
    {
      // Copy as-is...
      memcpy(temp, value->value.binary.data, value->value.binary.datalen);
      temp[value->value.binary.datalen] = '\0';
    }

    free(value->value.binary.data);
    value->type         = PDFIO_VALTYPE_STRING;
    value->value.string = pdfioStringCreate(dict->pdf, temp);

    PDFIO_DEBUG("pdfioDictGetString: Returning \"%s\".\n", value->value.string);

    return (value->value.string);
  }
  else
  {
    PDFIO_DEBUG("pdfioDictGetString: Returning NULL.\n");
    return (NULL);
  }
}


//
// 'pdfioDictGetType()' - Get a key value type from a dictionary.
//

pdfio_valtype_t				// O - Value type
pdfioDictGetType(pdfio_dict_t *dict,	// I - Dictionary
                 const char   *key)	// I - Key
{
  _pdfio_value_t *value = _pdfioDictGetValue(dict, key);


  return (value ? value->type : PDFIO_VALTYPE_NONE);
}


//
// '_pdfioDictGetValue()' - Get a key value from a dictionary.
//

_pdfio_value_t *			// O - Value or `NULL` on error
_pdfioDictGetValue(pdfio_dict_t *dict,	// I - Dictionary
                   const char   *key)	// I - Key
{
  _pdfio_pair_t	temp,			// Search key
		*match;			// Matching key pair


  PDFIO_DEBUG("_pdfioDictGetValue(dict=%p, key=\"%s\")\n", (void *)dict, key);

  if (!dict || !dict->num_pairs || !key)
  {
    PDFIO_DEBUG("_pdfioDictGetValue: Returning NULL.\n");
    return (NULL);
  }

  temp.key = key;

  if ((match = bsearch(&temp, dict->pairs, dict->num_pairs, sizeof(_pdfio_pair_t), (int (*)(const void *, const void *))compare_pairs)) != NULL)
  {
    PDFIO_DEBUG("_pdfioDictGetValue: Match, returning ");
    PDFIO_DEBUG_VALUE(&(match->value));
    PDFIO_DEBUG(".\n");
    return (&(match->value));
  }
  else
  {
    PDFIO_DEBUG("_pdfioDictGetValue: No match, returning NULL.\n");
    return (NULL);
  }
}


//
// 'pdfioDictIterateKeys()' - Iterate the keys in a dictionary.
//
// This function iterates the keys in a dictionary, calling the supplied
// function "cb":
//
// ```
// bool
// my_dict_cb(pdfio_dict_t *dict, const char *key, void *cb_data)
// {
// ... "key" contains the dictionary key ...
// ... return true to continue or false to stop ...
// }
// ```
//
// The iteration continues as long as the callback returns `true` or all keys
// have been iterated.
//
// @since PDFio v1.1@
//

void
pdfioDictIterateKeys(
    pdfio_dict_t    *dict,		// I - Dictionary
    pdfio_dict_cb_t cb,			// I - Callback function
    void            *cb_data)		// I - Callback data
{
  size_t	i;			// Looping var
  _pdfio_pair_t	*pair;			// Current pair


  // Range check input...
  if (!dict || !cb)
    return;

  for (i = dict->num_pairs, pair = dict->pairs; i > 0; i --, pair ++)
  {
    if (!(cb)(dict, pair->key, cb_data))
      break;
  }
}


//
// '_pdfioDictRead()' - Read a dictionary from a PDF file.
//
// At this point we've seen the initial "<<"...
//

pdfio_dict_t *				// O - New dictionary
_pdfioDictRead(pdfio_file_t   *pdf,	// I - PDF file
               pdfio_obj_t    *obj,	// I - Object, if any
               _pdfio_token_t *tb,	// I - Token buffer/stack
               size_t         depth)	// I - Depth of dictionary
{
  pdfio_dict_t		*dict;		// New dictionary
  char			key[256];	// Dictionary key
  _pdfio_value_t	value;		// Dictionary value


  PDFIO_DEBUG("_pdfioDictRead(pdf=%p, obj=%p, tb=%p, depth=%lu)\n", (void *)pdf, (void *)obj, (void *)tb, (unsigned long)depth);

  // Create a dictionary and start reading...
  if ((dict = pdfioDictCreate(pdf)) == NULL)
    return (NULL);

  while (_pdfioTokenGet(tb, key, sizeof(key)))
  {
    // Get the next key or end-of-dictionary...
    if (!strcmp(key, ">>"))
    {
      // End of dictionary...
      PDFIO_DEBUG("_pdfioDictRead: Returning dictionary value...\n");
      return (dict);
    }
    else if (key[0] != '/')
    {
      _pdfioFileError(pdf, "Invalid dictionary contents.");
      break;
    }

    // Then get the next value...
    PDFIO_DEBUG("_pdfioDictRead: Reading value for '%s'.\n", key + 1);

    if (!_pdfioValueRead(pdf, obj, tb, &value, depth))
    {
      _pdfioFileError(pdf, "Missing value for dictionary key '%s'.", key + 1);
      break;
    }
    else if (_pdfioDictGetValue(dict, key + 1))
    {
      // Issue 118: Discard duplicate key/value pairs...
      _pdfioValueDelete(&value);
      if (_pdfioFileError(pdf, "WARNING: Discarding value for duplicate dictionary key '%s'.", key + 1))
        continue;
      else
        break;
    }
    else if (!_pdfioDictSetValue(dict, pdfioStringCreate(pdf, key + 1), &value))
      break;

    PDFIO_DEBUG("_pdfioDictRead: Set %s.\n", key);
  }

  // Dictionary is invalid - pdfioFileClose will free the memory, return NULL
  // to indicate an error...
  return (NULL);
}


//
// 'pdfioDictSetArray()' - Set a key array in a dictionary.
//

bool					// O - `true` on success, `false` on failure
pdfioDictSetArray(pdfio_dict_t  *dict,	// I - Dictionary
                  const char    *key,	// I - Key
                  pdfio_array_t *value)	// I - Value
{
  _pdfio_value_t temp;			// New value


  // Range check input...
  if (!dict || !key || !value)
    return (false);

  // Set the key/value pair...
  temp.type        = PDFIO_VALTYPE_ARRAY;
  temp.value.array = value;

  return (_pdfioDictSetValue(dict, key, &temp));
}


//
// 'pdfioDictSetBinary()' - Set a key binary string in a dictionary.
//


bool					// O - `true` on success, `false` on failure
pdfioDictSetBinary(
    pdfio_dict_t        *dict,		// I - Dictionary
    const char          *key,		// I - Key
    const unsigned char *value,		// I - Value
    size_t              valuelen)	// I - Length of value
{
  _pdfio_value_t temp;			// New value


  // Range check input...
  if (!dict || !key || !value || !valuelen)
    return (false);

  // Set the key/value pair...
  temp.type                 = PDFIO_VALTYPE_BINARY;
  temp.value.binary.datalen = valuelen;

  if ((temp.value.binary.data = (unsigned char *)malloc(valuelen)) == NULL)
    return (false);

  memcpy(temp.value.binary.data, value, valuelen);

  if (!_pdfioDictSetValue(dict, key, &temp))
  {
    free(temp.value.binary.data);
    return (false);
  }

  return (true);
}


//
// 'pdfioDictSetBoolean()' - Set a key boolean in a dictionary.
//

bool					// O - `true` on success, `false` on failure
pdfioDictSetBoolean(pdfio_dict_t *dict,	// I - Dictionary
                    const char   *key,	// I - Key
                    bool         value)	// I - Value
{
  _pdfio_value_t temp;			// New value


  // Range check input...
  if (!dict || !key)
    return (false);

  // Set the key/value pair...
  temp.type          = PDFIO_VALTYPE_BOOLEAN;
  temp.value.boolean = value;

  return (_pdfioDictSetValue(dict, key, &temp));
}


//
// 'pdfioDictSetDate()' - Set a date value in a dictionary.
//

bool					// O - `true` on success, `false` on failure
pdfioDictSetDate(pdfio_dict_t *dict,	// I - Dictionary
                 const char   *key,	// I - Key
                 time_t       value)	// I - Value
{
  _pdfio_value_t temp;			// New value


  // Range check input...
  if (!dict || !key)
    return (false);

  // Set the key/value pair...
  temp.type       = PDFIO_VALTYPE_DATE;
  temp.value.date = value;

  return (_pdfioDictSetValue(dict, key, &temp));
}


//
// 'pdfioDictSetDict()' - Set a key dictionary in a dictionary.
//

bool					// O - `true` on success, `false` on failure
pdfioDictSetDict(pdfio_dict_t *dict,	// I - Dictionary
                 const char   *key,	// I - Key
                 pdfio_dict_t *value)	// I - Value
{
  _pdfio_value_t temp;			// New value


  // Range check input...
  if (!dict || !key || !value)
    return (false);

  // Set the key/value pair...
  temp.type       = PDFIO_VALTYPE_DICT;
  temp.value.dict = value;

  return (_pdfioDictSetValue(dict, key, &temp));
}


//
// 'pdfioDictSetName()' - Set a key name in a dictionary.
//

bool					// O - `true` on success, `false` on failure
pdfioDictSetName(pdfio_dict_t  *dict,	// I - Dictionary
                 const char    *key,	// I - Key
                 const char    *value)	// I - Value
{
  _pdfio_value_t temp;			// New value


  // Range check input...
  if (!dict || !key || !value)
    return (false);

  // Set the key/value pair...
  temp.type       = PDFIO_VALTYPE_NAME;
  temp.value.name = value;

  return (_pdfioDictSetValue(dict, key, &temp));
}


//
// 'pdfioDictSetNull()' - Set a key null in a dictionary.
//

bool					// O - `true` on success, `false` on failure
pdfioDictSetNull(pdfio_dict_t *dict,	// I - Dictionary
		 const char   *key)	// I - Key
{
  _pdfio_value_t temp;			// New value


  // Range check input...
  if (!dict || !key)
    return (false);

  // Set the key/value pair...
  temp.type = PDFIO_VALTYPE_NULL;

  return (_pdfioDictSetValue(dict, key, &temp));
}


//
// 'pdfioDictSetNumber()' - Set a key number in a dictionary.
//

bool					// O - `true` on success, `false` on failure
pdfioDictSetNumber(pdfio_dict_t  *dict,	// I - Dictionary
                   const char    *key,	// I - Key
                   double        value)	// I - Value
{
  _pdfio_value_t temp;			// New value


  // Range check input...
  if (!dict || !key)
    return (false);

  // Set the key/value pair...
  temp.type         = PDFIO_VALTYPE_NUMBER;
  temp.value.number = value;

  return (_pdfioDictSetValue(dict, key, &temp));
}


//
// 'pdfioDictSetObj()' - Set a key indirect object reference in a dictionary.
//

bool					// O - `true` on success, `false` on failure
pdfioDictSetObj(pdfio_dict_t *dict,	// I - Dictionary
                const char    *key,	// I - Key
                pdfio_obj_t   *value)	// I - Value
{
  _pdfio_value_t temp;			// New value


  // Range check input...
  if (!dict || !key || !value)
    return (false);

  // Set the key/value pair...
  temp.type                      = PDFIO_VALTYPE_INDIRECT;
  temp.value.indirect.number     = value->number;
  temp.value.indirect.generation = value->generation;

  return (_pdfioDictSetValue(dict, key, &temp));
}


//
// 'pdfioDictSetRect()' - Set a key rectangle in a dictionary.
//

bool					// O - `true` on success, `false` on failure
pdfioDictSetRect(pdfio_dict_t *dict,	// I - Dictionary
                 const char   *key,	// I - Key
                 pdfio_rect_t *value)	// I - Value
{
  _pdfio_value_t temp;			// New value


  // Range check input...
  if (!dict || !key || !value)
    return (false);

  // Set the key/value pair...
  temp.type        = PDFIO_VALTYPE_ARRAY;
  temp.value.array = pdfioArrayCreate(dict->pdf);

  pdfioArrayAppendNumber(temp.value.array, value->x1);
  pdfioArrayAppendNumber(temp.value.array, value->y1);
  pdfioArrayAppendNumber(temp.value.array, value->x2);
  pdfioArrayAppendNumber(temp.value.array, value->y2);

  return (_pdfioDictSetValue(dict, key, &temp));
}


//
// 'pdfioDictSetString()' - Set a key literal string in a dictionary.
//

bool					// O - `true` on success, `false` on failure
pdfioDictSetString(pdfio_dict_t  *dict,	// I - Dictionary
                  const char     *key,	// I - Key
                  const char     *value)// I - Value
{
  _pdfio_value_t temp;			// New value


  // Range check input...
  if (!dict || !key || !value)
    return (false);

  // Set the key/value pair...
  temp.type         = PDFIO_VALTYPE_STRING;
  temp.value.string = value;

  return (_pdfioDictSetValue(dict, key, &temp));
}


//
// 'pdfioDictSetStringf()' - Set a key formatted string in a dictionary.
//

bool					// O - `true` on success, `false` on failure
pdfioDictSetStringf(
    pdfio_dict_t  *dict,		// I - Dictionary
    const char    *key,			// I - Key
    const char    *format,		// I - `printf`-style format string
    ...)				// I - Additional arguments as needed
{
  char		buffer[8192];		// String buffer
  va_list	ap;			// Argument list


  // Range check input...
  if (!dict || !key || !format)
    return (false);

  // Set the key/value pair...
  va_start(ap, format);
  vsnprintf(buffer, sizeof(buffer), format, ap);
  va_end(ap);

  return (pdfioDictSetString(dict, key, buffer));
}


//
// '_pdfioDictSetValue()' - Set a key value in a dictionary.
//

bool					// O - `true` on success, `false` on failure
_pdfioDictSetValue(
    pdfio_dict_t   *dict,		// I - Dictionary
    const char     *key,		// I - Key
    _pdfio_value_t *value)		// I - Value
{
  _pdfio_pair_t	*pair;			// Current pair


  PDFIO_DEBUG("_pdfioDictSetValue(dict=%p, key=\"%s\", value=%p)\n", (void *)dict, key, (void *)value);

  // See if the key is already set...
  if (dict->num_pairs > 0)
  {
    _pdfio_pair_t	pkey;		// Search key

    pkey.key = key;

    if ((pair = (_pdfio_pair_t *)bsearch(&pkey, dict->pairs, dict->num_pairs, sizeof(_pdfio_pair_t), (int (*)(const void *, const void *))compare_pairs)) != NULL)
    {
      // Yes, replace the value...
      PDFIO_DEBUG("_pdfioDictSetValue: Replacing existing value.\n");
      if (pair->value.type == PDFIO_VALTYPE_BINARY)
        free(pair->value.value.binary.data);
      pair->value = *value;
      return (true);
    }
  }

  // Nope, add a pair...
  if (dict->num_pairs >= dict->alloc_pairs)
  {
    // Expand the dictionary...
    _pdfio_pair_t *temp = (_pdfio_pair_t *)realloc(dict->pairs, (dict->alloc_pairs + 8) * sizeof(_pdfio_pair_t));

    if (!temp)
    {
      PDFIO_DEBUG("_pdfioDictSetValue: Out of memory.\n");
      return (false);
    }

    dict->pairs       = temp;
    dict->alloc_pairs += 8;
  }

  pair = dict->pairs + dict->num_pairs;
  dict->num_pairs ++;

  pair->key   = key;
  pair->value = *value;

  // Re-sort the dictionary and return...
  if (dict->num_pairs > 1 && compare_pairs(pair - 1, pair) > 0)
    qsort(dict->pairs, dict->num_pairs, sizeof(_pdfio_pair_t), (int (*)(const void *, const void *))compare_pairs);

#ifdef DEBUG
  PDFIO_DEBUG("_pdfioDictSetValue(%p): %lu pairs\n", (void *)dict, (unsigned long)dict->num_pairs);
//  PDFIO_DEBUG("_pdfioDictSetValue(%p): ", (void *)dict);
//  PDFIO_DEBUG_DICT(dict);
//  PDFIO_DEBUG("\n");
#endif // DEBUG

  return (true);
}


//
// '_pdfioDictWrite()' - Write a dictionary to a PDF file.
//

bool					// O - `true` on success, `false` on failure
_pdfioDictWrite(pdfio_dict_t *dict,	// I - Dictionary
		pdfio_obj_t  *obj,	// I - Object, if any
                off_t        *length)	// I - Offset to length value
{
  pdfio_file_t	*pdf = dict->pdf;	// PDF file
  size_t	i;			// Looping var
  _pdfio_pair_t	*pair;			// Current key/value pair


  if (length)
    *length = 0;

  // Dictionaries are bounded by "<<" and ">>"...
  if (!_pdfioFilePuts(pdf, "<<"))
    return (false);

  // Write all of the key/value pairs...
  for (i = dict->num_pairs, pair = dict->pairs; i > 0; i --, pair ++)
  {
    if (!_pdfioFilePrintf(pdf, "%N", pair->key))
      return (false);

    if (length && !strcmp(pair->key, "Length") && pair->value.type == PDFIO_VALTYPE_NUMBER && pair->value.value.number <= 0.0)
    {
      // Writing an object dictionary with an undefined length
      *length = _pdfioFileTell(pdf) + 1;
      if (!_pdfioFilePuts(pdf, " 9999999999"))
        return (false);
    }
    else if (!_pdfioValueWrite(pdf, obj, &pair->value, NULL))
      return (false);
  }

  // Close it up...
  return (_pdfioFilePuts(pdf, ">>"));
}


//
// 'compare_pairs()' - Compare the keys for two pairs.
//

static int				// O - Result of comparison
compare_pairs(_pdfio_pair_t *a,		// I - First pair
              _pdfio_pair_t *b)		// I - Second pair
{
  return (strcmp(a->key, b->key));
}
