Blame src/lib/libmspub_utils.cpp

rpm-build 9243a4
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
rpm-build 9243a4
/*
rpm-build 9243a4
 * This file is part of the libmspub project.
rpm-build 9243a4
 *
rpm-build 9243a4
 * This Source Code Form is subject to the terms of the Mozilla Public
rpm-build 9243a4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
rpm-build 9243a4
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
rpm-build 9243a4
 */
rpm-build 9243a4
rpm-build 9243a4
#include "libmspub_utils.h"
rpm-build 9243a4
rpm-build 9243a4
#include <cstdarg>
rpm-build 9243a4
#include <cstring>
rpm-build 9243a4
#include <string.h> // for memcpy
rpm-build 9243a4
rpm-build 9243a4
#include <unicode/ucnv.h>
rpm-build 9243a4
#include <unicode/utypes.h>
rpm-build 9243a4
rpm-build 9243a4
#include <zlib.h>
rpm-build 9243a4
rpm-build 9243a4
#define ZLIB_CHUNK 16384
rpm-build 9243a4
rpm-build 9243a4
namespace libmspub
rpm-build 9243a4
{
rpm-build 9243a4
rpm-build 9243a4
#ifdef DEBUG
rpm-build 9243a4
rpm-build 9243a4
void debugPrint(const char *const format, ...)
rpm-build 9243a4
{
rpm-build 9243a4
  va_list args;
rpm-build 9243a4
  va_start(args, format);
rpm-build 9243a4
  std::vfprintf(stderr, format, args);
rpm-build 9243a4
  va_end(args);
rpm-build 9243a4
}
rpm-build 9243a4
rpm-build 9243a4
#endif
rpm-build 9243a4
rpm-build 9243a4
using std::strcmp;
rpm-build 9243a4
const char *windowsCharsetNameByOriginalCharset(const char *name)
rpm-build 9243a4
{
rpm-build 9243a4
  if (strcmp(name, "Shift_JIS") == 0)
rpm-build 9243a4
  {
rpm-build 9243a4
    return "windows-932";
rpm-build 9243a4
  }
rpm-build 9243a4
  if (strcmp(name, "GB18030") == 0)
rpm-build 9243a4
  {
rpm-build 9243a4
    return "windows-936";
rpm-build 9243a4
  }
rpm-build 9243a4
  if (strcmp(name, "Big5") == 0)
rpm-build 9243a4
  {
rpm-build 9243a4
    return "windows-950";
rpm-build 9243a4
  }
rpm-build 9243a4
  if (strcmp(name, "ISO-8859-1") == 0)
rpm-build 9243a4
  {
rpm-build 9243a4
    return "windows-1252";
rpm-build 9243a4
  }
rpm-build 9243a4
  if (strcmp(name, "ISO-8859-2") == 0)
rpm-build 9243a4
  {
rpm-build 9243a4
    return "windows-1250";
rpm-build 9243a4
  }
rpm-build 9243a4
  if (strcmp(name, "windows-1251") == 0)
rpm-build 9243a4
  {
rpm-build 9243a4
    return "windows-1251";
rpm-build 9243a4
  }
rpm-build 9243a4
  if (strcmp(name, "windows-1256") == 0)
rpm-build 9243a4
  {
rpm-build 9243a4
    return "windows-1256";
rpm-build 9243a4
  }
rpm-build 9243a4
  return nullptr;
rpm-build 9243a4
}
rpm-build 9243a4
rpm-build 9243a4
const char *mimeByImgType(ImgType type)
rpm-build 9243a4
{
rpm-build 9243a4
  switch (type)
rpm-build 9243a4
  {
rpm-build 9243a4
  case PNG:
rpm-build 9243a4
    return "image/png";
rpm-build 9243a4
  case JPEG:
rpm-build 9243a4
    return "image/jpeg";
rpm-build 9243a4
  case DIB:
rpm-build 9243a4
    return "image/bmp";
rpm-build 9243a4
  case PICT:
rpm-build 9243a4
    return "image/pict";
rpm-build 9243a4
  case WMF:
rpm-build 9243a4
    return "image/wmf";
rpm-build 9243a4
  case EMF:
rpm-build 9243a4
    return "image/emf";
rpm-build 9243a4
  case TIFF:
rpm-build 9243a4
    return "image/tiff";
rpm-build 9243a4
  default:
rpm-build 9243a4
    MSPUB_DEBUG_MSG(("Unknown image type %d passed to mimeByImgType!\n", type));
rpm-build 9243a4
    return nullptr;
rpm-build 9243a4
  }
rpm-build 9243a4
}
rpm-build 9243a4
rpm-build 9243a4
void rotateCounter(double &x, double &y, double centerX, double centerY, short rotation)
rpm-build 9243a4
{
rpm-build 9243a4
  double vecX = x - centerX;
rpm-build 9243a4
  double vecY = centerY - y;
rpm-build 9243a4
  double sinTheta = sin(rotation * M_PI / 180.);
rpm-build 9243a4
  double cosTheta = cos(rotation * M_PI / 180.);
rpm-build 9243a4
  double newVecX = cosTheta * vecX - sinTheta * vecY;
rpm-build 9243a4
  double newVecY = sinTheta * vecX + cosTheta * vecY;
rpm-build 9243a4
  x = centerX + newVecX;
rpm-build 9243a4
  y = centerY - newVecY;
rpm-build 9243a4
}
rpm-build 9243a4
rpm-build 9243a4
double doubleModulo(double x, double y)
rpm-build 9243a4
{
rpm-build 9243a4
  if (y <= 0) // y <= 0 doesn't make sense
rpm-build 9243a4
  {
rpm-build 9243a4
    return x;
rpm-build 9243a4
  }
rpm-build 9243a4
  while (x < 0)
rpm-build 9243a4
  {
rpm-build 9243a4
    x += y;
rpm-build 9243a4
  }
rpm-build 9243a4
  while (x >= y)
rpm-build 9243a4
  {
rpm-build 9243a4
    x -= y;
rpm-build 9243a4
  }
rpm-build 9243a4
  return x;
rpm-build 9243a4
}
rpm-build 9243a4
rpm-build 9243a4
double toFixedPoint(int fp)
rpm-build 9243a4
{
rpm-build 9243a4
  unsigned short fractionalPart = ((unsigned short) fp) & 0xFFFF;
rpm-build 9243a4
  short integralPart = fp >> 16;
rpm-build 9243a4
  return integralPart + fractionalPart / 65536.;
rpm-build 9243a4
}
rpm-build 9243a4
rpm-build 9243a4
double readFixedPoint(librevenge::RVNGInputStream *input)
rpm-build 9243a4
{
rpm-build 9243a4
  return toFixedPoint(readS32(input));
rpm-build 9243a4
}
rpm-build 9243a4
rpm-build 9243a4
void flipIfNecessary(double &x, double &y, double centerX, double centerY, bool flipVertical, bool flipHorizontal)
rpm-build 9243a4
{
rpm-build 9243a4
  double vecX = x - centerX;
rpm-build 9243a4
  double vecY = centerY - y;
rpm-build 9243a4
  if (flipVertical)
rpm-build 9243a4
  {
rpm-build 9243a4
    y = centerY + vecY;
rpm-build 9243a4
  }
rpm-build 9243a4
  if (flipHorizontal)
rpm-build 9243a4
  {
rpm-build 9243a4
    x = centerX - vecX;
rpm-build 9243a4
  }
rpm-build 9243a4
}
rpm-build 9243a4
rpm-build 9243a4
unsigned correctModulo(int x, unsigned n) // returns the canonical representation of x in Z/nZ
rpm-build 9243a4
//difference with C++ % operator is that this never returns negative values.
rpm-build 9243a4
{
rpm-build 9243a4
  if (x < 0)
rpm-build 9243a4
  {
rpm-build 9243a4
    int result = x % (int)n;
rpm-build 9243a4
    //sign of result is implementation defined
rpm-build 9243a4
    if (result < 0)
rpm-build 9243a4
    {
rpm-build 9243a4
      return n + result;
rpm-build 9243a4
    }
rpm-build 9243a4
    return result;
rpm-build 9243a4
  }
rpm-build 9243a4
  return x % n;
rpm-build 9243a4
}
rpm-build 9243a4
rpm-build 9243a4
librevenge::RVNGBinaryData inflateData(librevenge::RVNGBinaryData deflated)
rpm-build 9243a4
{
rpm-build 9243a4
  librevenge::RVNGBinaryData inflated;
rpm-build 9243a4
  unsigned char out[ZLIB_CHUNK];
rpm-build 9243a4
  const unsigned char *data = deflated.getDataBuffer();
rpm-build 9243a4
  z_stream strm;
rpm-build 9243a4
  int ret;
rpm-build 9243a4
  strm.zalloc = Z_NULL;
rpm-build 9243a4
  strm.zfree = Z_NULL;
rpm-build 9243a4
  strm.opaque = Z_NULL;
rpm-build 9243a4
  strm.avail_in = 0;
rpm-build 9243a4
  strm.next_in = Z_NULL;
rpm-build 9243a4
  if (inflateInit2(&strm,-MAX_WBITS) != Z_OK)
rpm-build 9243a4
  {
rpm-build 9243a4
    return librevenge::RVNGBinaryData();
rpm-build 9243a4
  }
rpm-build 9243a4
  int have;
rpm-build 9243a4
  unsigned left = deflated.size();
rpm-build 9243a4
  do
rpm-build 9243a4
  {
rpm-build 9243a4
    strm.avail_in = ZLIB_CHUNK > left ? left : ZLIB_CHUNK;
rpm-build 9243a4
    strm.next_in = (unsigned char *)data;
rpm-build 9243a4
    do
rpm-build 9243a4
    {
rpm-build 9243a4
      strm.avail_out = ZLIB_CHUNK;
rpm-build 9243a4
      strm.next_out = out;
rpm-build 9243a4
      ret = inflate(&strm, Z_NO_FLUSH);
rpm-build 9243a4
      if (ret < 0 || ret == Z_NEED_DICT)
rpm-build 9243a4
      {
rpm-build 9243a4
        inflateEnd(&strm;;
rpm-build 9243a4
        return librevenge::RVNGBinaryData();
rpm-build 9243a4
      }
rpm-build 9243a4
      have = ZLIB_CHUNK - strm.avail_out;
rpm-build 9243a4
      inflated.append(out, have);
rpm-build 9243a4
    }
rpm-build 9243a4
    while (strm.avail_out == 0);
rpm-build 9243a4
    data += ZLIB_CHUNK > left ? left : ZLIB_CHUNK;
rpm-build 9243a4
    left -= ZLIB_CHUNK > left ? left : ZLIB_CHUNK;
rpm-build 9243a4
  }
rpm-build 9243a4
  while (ret != Z_STREAM_END);
rpm-build 9243a4
  inflateEnd(&strm;;
rpm-build 9243a4
  return inflated;
rpm-build 9243a4
}
rpm-build 9243a4
rpm-build 9243a4
namespace
rpm-build 9243a4
{
rpm-build 9243a4
rpm-build 9243a4
static void _appendUCS4(librevenge::RVNGString &text, unsigned ucs4Character)
rpm-build 9243a4
{
rpm-build 9243a4
  unsigned char first;
rpm-build 9243a4
  int len;
rpm-build 9243a4
  if (ucs4Character < 0x80)
rpm-build 9243a4
  {
rpm-build 9243a4
    first = 0;
rpm-build 9243a4
    len = 1;
rpm-build 9243a4
  }
rpm-build 9243a4
  else if (ucs4Character < 0x800)
rpm-build 9243a4
  {
rpm-build 9243a4
    first = 0xc0;
rpm-build 9243a4
    len = 2;
rpm-build 9243a4
  }
rpm-build 9243a4
  else if (ucs4Character < 0x10000)
rpm-build 9243a4
  {
rpm-build 9243a4
    first = 0xe0;
rpm-build 9243a4
    len = 3;
rpm-build 9243a4
  }
rpm-build 9243a4
  else if (ucs4Character < 0x200000)
rpm-build 9243a4
  {
rpm-build 9243a4
    first = 0xf0;
rpm-build 9243a4
    len = 4;
rpm-build 9243a4
  }
rpm-build 9243a4
  else if (ucs4Character < 0x4000000)
rpm-build 9243a4
  {
rpm-build 9243a4
    first = 0xf8;
rpm-build 9243a4
    len = 5;
rpm-build 9243a4
  }
rpm-build 9243a4
  else
rpm-build 9243a4
  {
rpm-build 9243a4
    first = 0xfc;
rpm-build 9243a4
    len = 6;
rpm-build 9243a4
  }
rpm-build 9243a4
rpm-build 9243a4
  char outbuf[7] = { 0 };
rpm-build 9243a4
  int i;
rpm-build 9243a4
  for (i = len - 1; i > 0; --i)
rpm-build 9243a4
  {
rpm-build 9243a4
    outbuf[i] = char((ucs4Character & 0x3f) | 0x80);
rpm-build 9243a4
    ucs4Character >>= 6;
rpm-build 9243a4
  }
rpm-build 9243a4
  outbuf[0] = char((ucs4Character & 0xff) | first);
rpm-build 9243a4
  outbuf[len] = '\0';
rpm-build 9243a4
rpm-build 9243a4
  text.append(outbuf);
rpm-build 9243a4
}
rpm-build 9243a4
rpm-build 9243a4
} // anonymous namespace
rpm-build 9243a4
rpm-build 9243a4
#define MSPUB_NUM_ELEMENTS(array) sizeof(array)/sizeof(array[0])
rpm-build 9243a4
rpm-build 9243a4
uint8_t readU8(librevenge::RVNGInputStream *input)
rpm-build 9243a4
{
rpm-build 9243a4
  if (!input || input->isEnd())
rpm-build 9243a4
  {
rpm-build 9243a4
    MSPUB_DEBUG_MSG(("Something bad happened here!"));
rpm-build 9243a4
    if (input)
rpm-build 9243a4
    {
rpm-build 9243a4
      MSPUB_DEBUG_MSG((" Tell: %ld\n", input->tell()));
rpm-build 9243a4
    }
rpm-build 9243a4
    throw EndOfStreamException();
rpm-build 9243a4
  }
rpm-build 9243a4
  unsigned long numBytesRead;
rpm-build 9243a4
  uint8_t const *p = input->read(sizeof(uint8_t), numBytesRead);
rpm-build 9243a4
rpm-build 9243a4
  if (p && numBytesRead == sizeof(uint8_t))
rpm-build 9243a4
    return *(uint8_t const *)(p);
rpm-build 9243a4
  throw EndOfStreamException();
rpm-build 9243a4
}
rpm-build 9243a4
rpm-build 9243a4
uint16_t readU16(librevenge::RVNGInputStream *input)
rpm-build 9243a4
{
rpm-build 9243a4
  auto p0 = (uint16_t)readU8(input);
rpm-build 9243a4
  auto p1 = (uint16_t)readU8(input);
rpm-build 9243a4
  return (uint16_t)(p0|(p1<<8));
rpm-build 9243a4
}
rpm-build 9243a4
rpm-build 9243a4
uint32_t readU32(librevenge::RVNGInputStream *input)
rpm-build 9243a4
{
rpm-build 9243a4
  auto p0 = (uint32_t)readU8(input);
rpm-build 9243a4
  auto p1 = (uint32_t)readU8(input);
rpm-build 9243a4
  auto p2 = (uint32_t)readU8(input);
rpm-build 9243a4
  auto p3 = (uint32_t)readU8(input);
rpm-build 9243a4
  return (uint32_t)(p0|(p1<<8)|(p2<<16)|(p3<<24));
rpm-build 9243a4
}
rpm-build 9243a4
rpm-build 9243a4
int8_t readS8(librevenge::RVNGInputStream *input)
rpm-build 9243a4
{
rpm-build 9243a4
  return (int8_t)readU8(input);
rpm-build 9243a4
}
rpm-build 9243a4
rpm-build 9243a4
int16_t readS16(librevenge::RVNGInputStream *input)
rpm-build 9243a4
{
rpm-build 9243a4
  return (int16_t)readU16(input);
rpm-build 9243a4
}
rpm-build 9243a4
rpm-build 9243a4
int32_t readS32(librevenge::RVNGInputStream *input)
rpm-build 9243a4
{
rpm-build 9243a4
  return (int32_t)readU32(input);
rpm-build 9243a4
}
rpm-build 9243a4
rpm-build 9243a4
uint64_t readU64(librevenge::RVNGInputStream *input)
rpm-build 9243a4
{
rpm-build 9243a4
  auto p0 = (uint64_t)readU8(input);
rpm-build 9243a4
  auto p1 = (uint64_t)readU8(input);
rpm-build 9243a4
  auto p2 = (uint64_t)readU8(input);
rpm-build 9243a4
  auto p3 = (uint64_t)readU8(input);
rpm-build 9243a4
  auto p4 = (uint64_t)readU8(input);
rpm-build 9243a4
  auto p5 = (uint64_t)readU8(input);
rpm-build 9243a4
  auto p6 = (uint64_t)readU8(input);
rpm-build 9243a4
  auto p7 = (uint64_t)readU8(input);
rpm-build 9243a4
  return (uint64_t)(p0|(p1<<8)|(p2<<16)|(p3<<24)|(p4<<32)|(p5<<40)|(p6<<48)|(p7<<56));
rpm-build 9243a4
}
rpm-build 9243a4
rpm-build 9243a4
void readNBytes(librevenge::RVNGInputStream *input, unsigned long length, std::vector<unsigned char> &out)
rpm-build 9243a4
{
rpm-build 9243a4
  if (length == 0)
rpm-build 9243a4
  {
rpm-build 9243a4
    MSPUB_DEBUG_MSG(("Attempt to read 0 bytes!"));
rpm-build 9243a4
    return;
rpm-build 9243a4
  }
rpm-build 9243a4
rpm-build 9243a4
  unsigned long numBytesRead = 0;
rpm-build 9243a4
  const unsigned char *tmpBuffer = input->read(length, numBytesRead);
rpm-build 9243a4
  if (numBytesRead != length)
rpm-build 9243a4
  {
rpm-build 9243a4
    out.clear();
rpm-build 9243a4
    return;
rpm-build 9243a4
  }
rpm-build 9243a4
  out = std::vector<unsigned char>(numBytesRead);
rpm-build 9243a4
  memcpy(out.data(), tmpBuffer, numBytesRead);
rpm-build 9243a4
  return;
rpm-build 9243a4
}
rpm-build 9243a4
rpm-build 9243a4
unsigned long getLength(librevenge::RVNGInputStream *const input)
rpm-build 9243a4
{
rpm-build 9243a4
  if (!input)
rpm-build 9243a4
    throw EndOfStreamException();
rpm-build 9243a4
rpm-build 9243a4
  const long orig = input->tell();
rpm-build 9243a4
rpm-build 9243a4
  unsigned long end = 0;
rpm-build 9243a4
rpm-build 9243a4
  if (0 == input->seek(0, librevenge::RVNG_SEEK_END))
rpm-build 9243a4
  {
rpm-build 9243a4
    end = static_cast<unsigned long>(input->tell());
rpm-build 9243a4
  }
rpm-build 9243a4
  else
rpm-build 9243a4
  {
rpm-build 9243a4
    // RVNG_SEEK_END does not work. Use the harder way.
rpm-build 9243a4
    if (0 != input->seek(0, librevenge::RVNG_SEEK_SET))
rpm-build 9243a4
      throw EndOfStreamException();
rpm-build 9243a4
    while (!input->isEnd())
rpm-build 9243a4
    {
rpm-build 9243a4
      readU8(input);
rpm-build 9243a4
      ++end;
rpm-build 9243a4
    }
rpm-build 9243a4
  }
rpm-build 9243a4
rpm-build 9243a4
  if (0 != input->seek(orig, librevenge::RVNG_SEEK_SET))
rpm-build 9243a4
    throw EndOfStreamException();
rpm-build 9243a4
rpm-build 9243a4
  return end;
rpm-build 9243a4
}
rpm-build 9243a4
rpm-build 9243a4
#define SURROGATE_VALUE(h,l) (((h) - 0xd800) * 0x400 + (l) - 0xdc00 + 0x10000)
rpm-build 9243a4
rpm-build 9243a4
rpm-build 9243a4
void appendCharacters(librevenge::RVNGString &text, const std::vector<unsigned char> &characters,
rpm-build 9243a4
                      const char *encoding)
rpm-build 9243a4
{
rpm-build 9243a4
  if (characters.empty())
rpm-build 9243a4
  {
rpm-build 9243a4
    MSPUB_DEBUG_MSG(("Attempt to append 0 characters!"));
rpm-build 9243a4
    return;
rpm-build 9243a4
  }
rpm-build 9243a4
rpm-build 9243a4
  UErrorCode status = U_ZERO_ERROR;
rpm-build 9243a4
  UConverter *conv = nullptr;
rpm-build 9243a4
  conv = ucnv_open(encoding, &status);
rpm-build 9243a4
  if (U_SUCCESS(status))
rpm-build 9243a4
  {
rpm-build 9243a4
    // ICU documentation claims that character-by-character processing is faster "for small amounts of data" and "'normal' charsets"
rpm-build 9243a4
    // (in any case, it is more convenient :) )
rpm-build 9243a4
    const auto *src = (const char *)characters.data();
rpm-build 9243a4
    const char *srcLimit = (const char *)src + characters.size();
rpm-build 9243a4
    while (src < srcLimit)
rpm-build 9243a4
    {
rpm-build 9243a4
      auto ucs4Character = (uint32_t)ucnv_getNextUChar(conv, &src, srcLimit, &status);
rpm-build 9243a4
      if (U_SUCCESS(status))
rpm-build 9243a4
      {
rpm-build 9243a4
        _appendUCS4(text, ucs4Character);
rpm-build 9243a4
      }
rpm-build 9243a4
    }
rpm-build 9243a4
  }
rpm-build 9243a4
  if (conv)
rpm-build 9243a4
  {
rpm-build 9243a4
    ucnv_close(conv);
rpm-build 9243a4
  }
rpm-build 9243a4
}
rpm-build 9243a4
rpm-build 9243a4
bool stillReading(librevenge::RVNGInputStream *input, unsigned long until)
rpm-build 9243a4
{
rpm-build 9243a4
  if (input->isEnd())
rpm-build 9243a4
    return false;
rpm-build 9243a4
  if (input->tell() < 0)
rpm-build 9243a4
    return false;
rpm-build 9243a4
  if ((unsigned long)input->tell() >= until)
rpm-build 9243a4
    return false;
rpm-build 9243a4
  return true;
rpm-build 9243a4
}
rpm-build 9243a4
rpm-build 9243a4
}
rpm-build 9243a4
rpm-build 9243a4
/* vim:set shiftwidth=2 softtabstop=2 expandtab: */