#include "hpdf.h"
#include "hpdf_consts.h"
#include "DocOutput.h"
#include "Util.h"
#include <fstream>
#include <map>
#include <ctime>


HPDF_Font fontStyles[5];
extern const float fontSizes[6];


static bool fileExists(const char *fileName)
{
  if (!fileName)
    return false;
  return std::ifstream(fileName).good();
}


struct DocImage
{
	HPDF_Image image;
};


struct DocOutputPage::Pimpl
{
  DocOutputDevice* parent;
	HPDF_Doc         pdf;
	HPDF_Page        page;
  int              activeFontType;
  float            activeFontSize;
  int              fontType;
  float            fontSize;

  // URI state
  float            startLinkX;
  float            startLinkY;
  std::string      linkUri;

  void UpdateFont();
};


DocOutputPage::DocOutputPage(DocOutputDevice* a_parent) : m_pimpl(std::make_unique<Pimpl>())
{
	m_pimpl->parent = a_parent;
	m_pimpl->pdf = a_parent->doc();
	m_pimpl->page = HPDF_AddPage(m_pimpl->pdf);
  m_pimpl->activeFontType = (FontType)-1;
  m_pimpl->activeFontSize = -1.0;
  m_pimpl->fontType = FT_Normal;
  m_pimpl->fontSize = 12.0;

  HPDF_Page_BeginText(m_pimpl->page);
}


DocOutputPage::~DocOutputPage()
{
	HPDF_Page_EndText(m_pimpl->page);
}


HPDF_Doc DocOutputPage::doc()
{
	return m_pimpl->pdf;
}


HPDF_Page DocOutputPage::page()
{
	return m_pimpl->page;
}


float DocOutputPage::width()
{
	return HPDF_Page_GetWidth(m_pimpl->page);
}


float DocOutputPage::height()
{
	return HPDF_Page_GetHeight(m_pimpl->page);
}


float DocOutputPage::textWidth(const char* str)
{
  if (!str)
    return 0.0;
  m_pimpl->UpdateFont();
	return HPDF_Page_TextWidth(m_pimpl->page, str);
}


bool DocOutputPage::imageSize(const char* fileName, float& width, float& height)
{
  width = 0.0;
  height = 0.0;
  if (!fileExists(fileName))
  {
    printf("image file does not exist: %s\n", fileName ? fileName : "null");
    return false;
  }

  // file exists, we can assume from here that fileName is not null
  bool res;
  HPDF_Image img = m_pimpl->parent->loadImage(fileName, res);
  if (!res)
  {
    printf("image file couldn't be loaded: %s\n", fileName);
    return false;
  }

  HPDF_Point pnt;
  HPDF_STATUS status = HPDF_Image_GetSize2(img, &pnt);
  if (status != HPDF_OK)
  {
    printf("image size couldn't be determined: %s\n", fileName);
    return false;
  }

  width = pnt.x;
  height = pnt.y;
  return true;
}


void DocOutputPage::Pimpl::UpdateFont()
{
  if (fontType != activeFontType || fontSize != activeFontSize)
  {
    HPDF_Page_SetFontAndSize(page, fontStyles[fontType], fontSize);
    activeFontType = fontType;
    activeFontSize = fontSize;
  }
}


void DocOutputPage::setFontType(FontType font)
{
  m_pimpl->fontType = (int)font;
}


void DocOutputPage::setFontSize(float size)
{
  m_pimpl->fontSize = size;
}


void DocOutputPage::setAlpha(float a)
{
  HPDF_Page_EndText(m_pimpl->page);
  setAlphaPenFill(a, a);
  HPDF_Page_BeginText(m_pimpl->page);
}

  
void DocOutputPage::setAlphaPenFill(float penAlpha, float fillAlpha)
{
  HPDF_ExtGState extState = HPDF_CreateExtGState(m_pimpl->pdf);
  HPDF_ExtGState_SetAlphaStroke(extState, penAlpha);
  HPDF_ExtGState_SetAlphaFill(extState, fillAlpha);
  HPDF_ExtGState_SetBlendMode(extState, (HPDF_BlendMode)0);
  HPDF_Page_SetExtGState(m_pimpl->page, extState);
}


void DocOutputPage::setGradient(const std::vector<HPDF_GradientStop>& stops, float p1x, float p1y, float p2x, float p2y)
{
  HPDF_Shading shading = HPDF_LinearGradient_New(m_pimpl->pdf, stops.data(), stops.size(), p1x, p1y, p2x, p2y);  
  HPDF_Page_SetShading(m_pimpl->page, shading);
}


void DocOutputPage::drawTriangles(float* points, int numPoints, unsigned char* colors, int nc_comps)
{
  assert(numPoints >= 3);
  const float radius = 0.f;
  HPDF_REAL bbox[4];
  HPDF_Page_GSave(m_pimpl->page);

  bbox[0] = static_cast<HPDF_REAL>(points[0]);
  bbox[1] = static_cast<HPDF_REAL>(points[0]);
  bbox[2] = static_cast<HPDF_REAL>(points[1]);
  bbox[3] = static_cast<HPDF_REAL>(points[1]);
  for (int i = 1; i < numPoints; ++i)
  {
    bbox[0] = std::min(bbox[0], static_cast<HPDF_REAL>(points[i * 2]));
    bbox[1] = std::max(bbox[1], static_cast<HPDF_REAL>(points[i * 2]));
    bbox[2] = std::min(bbox[2], static_cast<HPDF_REAL>(points[i * 2 + 1]));
    bbox[3] = std::max(bbox[3], static_cast<HPDF_REAL>(points[i * 2 + 1]));
  }
  bbox[0] -= radius;
  bbox[1] += radius;
  bbox[2] -= radius;
  bbox[3] += radius;

  HPDF_Shading shading = HPDF_Shading_New(m_pimpl->pdf,
      HPDF_SHADING_FREE_FORM_TRIANGLE_MESH, HPDF_CS_DEVICE_RGB, bbox[0], bbox[1], bbox[2], bbox[3]);

  // First triangle
  for (int ptIdx = 0; ptIdx < 3; ++ptIdx)
  {
    const float* pt = points + ptIdx * 2;
    const unsigned char* color = colors + ptIdx * nc_comps;
    HPDF_Shading_AddVertexRGB(shading, HPDF_FREE_FORM_TRI_MESH_EDGEFLAG_NO_CONNECTION, pt[0], pt[1],
      color[0], color[1], color[2]);
  }

  // Fan-out additional verts
  for (int ptIdx = 3; ptIdx < numPoints; ++ptIdx)
  {
    const float* pt = points + ptIdx * 2;
    const unsigned char* color = colors + ptIdx * nc_comps;
    HPDF_Shading_AddVertexRGB(
      shading, HPDF_FREE_FORM_TRI_MESH_EDGEFLAG_AC, pt[0], pt[1], color[0], color[1], color[2]);
  }

  HPDF_Page_SetShading(m_pimpl->page, shading);
  HPDF_Page_GRestore(m_pimpl->page);
}

/*
void DocOutputPage::setColor(float r, float g, float b)
{
	HPDF_Page_SetRGBFill(m_pimpl->page, r, g, b);
}
*/

void DocOutputPage::setFillColor(unsigned int color)
{
	float r = ((color >> 16) & 0xff) / 255.0f;
  float g = ((color >>  8) & 0xff) / 255.0f;
  float b = ((color >>  0) & 0xff) / 255.0f;
	HPDF_Page_SetRGBFill(m_pimpl->page, r, g, b);
}


void DocOutputPage::setPenColor(unsigned int color)
{
	float r = ((color >> 16) & 0xff) / 255.0f;
  float g = ((color >>  8) & 0xff) / 255.0f;
  float b = ((color >>  0) & 0xff) / 255.0f;
	HPDF_Page_SetRGBStroke(m_pimpl->page, r, g, b);
}


void DocOutputPage::setPenWidth(float width)
{
	HPDF_Page_SetLineWidth(m_pimpl->page, width);
}


void DocOutputPage::setPenStyle(unsigned int style)
{
  // TODO: not implemented
}


void DocOutputPage::drawText(float x, float y, const char* str)
{
  if (!str)
    return;
  m_pimpl->UpdateFont();
	HPDF_Page_TextOut(m_pimpl->page, x, height() - y, str);
}


void DocOutputPage::drawLine(float x1, float y1, float x2, float y2)
{
	HPDF_Page_EndText(m_pimpl->page);

  //HPDF_Page_SetLineWidth(m_pimpl->page, 2.0);
	HPDF_Page_MoveTo(m_pimpl->page, x1, height() - y1);
	HPDF_Page_LineTo(m_pimpl->page, x2, height() - y2);
	HPDF_Page_Stroke(m_pimpl->page);

  HPDF_Page_BeginText(m_pimpl->page);
}


void DocOutputPage::drawRect(float x1, float y1, float w, float h)
{
	HPDF_Page_EndText(m_pimpl->page);
	
	HPDF_Page_Rectangle(m_pimpl->page, x1, height() - y1 - h, w, h);
	HPDF_Page_FillStroke(m_pimpl->page);

  HPDF_Page_BeginText(m_pimpl->page);
}


void DocOutputPage::drawPolygon(float* pnts, int count)
{
  if (!pnts)
    return;
	HPDF_Page_EndText(m_pimpl->page);

  HPDF_Page_MoveTo(m_pimpl->page, pnts[0], height() - pnts[1]);
  for (int i = 1; i < count; i++)
	  HPDF_Page_LineTo(m_pimpl->page, pnts[i*2+0], height() - pnts[i*2+1]);
	HPDF_Page_ClosePathFillStroke(m_pimpl->page);

  HPDF_Page_BeginText(m_pimpl->page);
}


void DocOutputPage::drawImage(const char* fileName, float x1, float y1, float w, float h)
{
  if (!fileExists(fileName))
  {
    printf("image file does not exist: %s\n", fileName ? fileName : "null");
    return;
  }

  // file exists, we can assume from here that fileName is not null
  bool res;
  HPDF_Image img = m_pimpl->parent->loadImage(fileName, res);
  if (!res)
  {
    printf("image file couldn't be loaded: %s\n", fileName);
    return;
  }

	HPDF_Page_EndText(m_pimpl->page);

	HPDF_Page_DrawImage(m_pimpl->page, img, x1, height() - y1 - h, w, h);

  HPDF_Page_BeginText(m_pimpl->page);
}


void DocOutputPage::drawCircle(float x, float y, float radius)
{
	HPDF_Page_EndText(m_pimpl->page);

  HPDF_Page_Circle(m_pimpl->page, x, height() - y, radius);
	HPDF_Page_FillStroke(m_pimpl->page);

  HPDF_Page_BeginText(m_pimpl->page);
}


void DocOutputPage::startLink(const char* url, float x, float y)
{
  m_pimpl->linkUri = url;
  m_pimpl->startLinkX = x;
  m_pimpl->startLinkY = y;
}


void DocOutputPage::endLink(float x, float y)
{
	HPDF_Page_EndText(m_pimpl->page);

  HPDF_Rect rect = { m_pimpl->startLinkX, height() - m_pimpl->startLinkY, x, height() - y };
  HPDF_Page_CreateURILinkAnnot(m_pimpl->page, rect, m_pimpl->linkUri.c_str());

  HPDF_Page_BeginText(m_pimpl->page);
}


void DocOutputPage::drawCheckBox(bool checked, float x, float y, float w, float h)
{
	HPDF_Page_EndText(m_pimpl->page);

  HPDF_Rect rect = { x, height() - y, x + w, height() - (y + h) };
  HPDF_Page_CreateCheckBoxAnnot(m_pimpl->page, rect, checked);

  HPDF_Page_BeginText(m_pimpl->page);
}


struct DocOutputDevice::Pimpl
{
	HPDF_Doc                          pdf;
  HPDF_Error_Handler                errorFn;
  std::map<std::string, HPDF_Image> imageCache;
};


#ifdef _WIN32
#include <windows.h> // for OutputDebugStringA
static void error_handler(HPDF_STATUS error_no, HPDF_STATUS detail_no, void * /*user_data*/, int line, const char* file)
{
	// DocOutputDevice* context = (DocOutputDevice*)user_data;
	auto sizeRequired = size_t(_scprintf("%s(%i): error %04X: detail_no=%u\n", file, line, (HPDF_UINT)error_no, (HPDF_UINT)detail_no) + 1);
	auto a = (char*)_alloca(sizeRequired);
	sprintf_s(a, sizeRequired, "%s(%i): error %04X: detail_no=%u\n", file, line, (HPDF_UINT)error_no, (HPDF_UINT)detail_no);
	OutputDebugStringA(a);
	_freea(a);
	// delete context; // Can't do this, it could be on the stack as is the case currently
	exit(-1);
}
#else

static void error_handler(HPDF_STATUS error_no, HPDF_STATUS detail_no, void * /*user_data*/, int line, const char* file)
{
	// DocOutputDevice* context = (DocOutputDevice*)user_data;
	fprintf(stderr, "%s(%i): error %04X: detail_no=%u\n", file, line, (HPDF_UINT)error_no, (HPDF_UINT)detail_no);
	// delete context; // Can't do this, it could be on the stack as is the case currently

  //exit(-1);

  assert(false);
}

#endif


DocOutputDevice::DocOutputDevice(HPDF_Error_Handler errorFn) : m_pimpl(std::make_unique<Pimpl>())
{
	m_pimpl->pdf = 0;
	m_pimpl->errorFn = (errorFn) ? errorFn : error_handler;
  reset();
}


DocOutputDevice::~DocOutputDevice()
{
	HPDF_Free(m_pimpl->pdf);
}


void DocOutputDevice::reset()
{
  m_pimpl->imageCache.clear();

  if (m_pimpl->pdf)
  {
    HPDF_Free(m_pimpl->pdf);
  }
	m_pimpl->pdf = HPDF_New(m_pimpl->errorFn, this);
	if (!m_pimpl->pdf)
	{
		printf("error: cannot create PdfDoc object\n");
		return;
	}

  //HPDF_UseUTFEncodings(m_pimpl->pdf);
  //HPDF_SetCurrentEncoder(m_pimpl->pdf, "UTF-8");



  // HPDF_SetCompressionMode (pdf, HPDF_COMP_IMAGE | HPDF_COMP_METADATA);
  
  HPDF_UseCNSEncodings(m_pimpl->pdf);
  HPDF_UseKREncodings(m_pimpl->pdf);
  
  HPDF_UseUTFEncodings(m_pimpl->pdf);
  HPDF_SetCurrentEncoder(m_pimpl->pdf, "Modern-UTF8-H");
  
//  HPDF_UseCNSFonts(m_pimpl->pdf);
//  HPDF_UseKRFonts(m_pimpl->pdf);

  //HPDF_UseUTFEncodings



  /*
  // ../3rdParty/libharu/include/hpdf_types.h:    HPDF_INFO_SUBJECT,
  // ../3rdParty/libharu/include/hpdf_types.h:    HPDF_INFO_KEYWORDS,
  */
  time_t tm = time(&tm);
  struct tm* t = gmtime(&tm);
  int offsetMinutes = static_cast<int>(t->tm_gmtoff / 60);
  HPDF_Date date = { 1900 + t->tm_year, 1 + t->tm_mon, t->tm_mday, t->tm_hour, t->tm_min,
                     t->tm_sec, 'Z', offsetMinutes / 60, offsetMinutes % 60 };

  if (HPDF_SetInfoDateAttr(m_pimpl->pdf, HPDF_INFO_CREATION_DATE, date) != HPDF_OK)
	{
		printf("error: cannot set date on PdfDoc object\n");
		return;
	}

  if (HPDF_SetInfoAttr(m_pimpl->pdf, HPDF_INFO_PRODUCER, "WickedDocs PDF Generator") != HPDF_OK)
	{
		printf("error: cannot set producer on PdfDoc object\n");
		return;
	}

  if (HPDF_SetInfoAttr(m_pimpl->pdf, HPDF_INFO_CREATOR, "WickedDocs") != HPDF_OK)
	{
		printf("error: cannot set creator on PdfDoc object\n");
		return;
	}
	
  HPDF_SetCompressionMode(m_pimpl->pdf, HPDF_COMP_ALL);

  // Makes file larger
  //HPDF_SetPageMode(m_pimpl->pdf, HPDF_PAGE_MODE_USE_OUTLINE);

  /*
  // TODO: password
  */

	//fontStyles[0] = HPDF_GetFont(context->pdf, "David", NULL);
#ifdef _WIN32
	fontStyles[0] = HPDF_GetFont(m_pimpl->pdf, "Helvetica", NULL);
	fontStyles[1] = HPDF_GetFont(m_pimpl->pdf, "Helvetica-Oblique", NULL);
	fontStyles[2] = HPDF_GetFont(m_pimpl->pdf, "Helvetica-Bold", NULL);
	fontStyles[3] = HPDF_GetFont(m_pimpl->pdf, "Helvetica-BoldOblique", NULL);
#else
/*
  const char* detail_font_name = HPDF_LoadTTFontFromFile2(m_pimpl->pdf, "/System/Library/Fonts/Avenir Next.ttc", 7, true);
  if (detail_font_name)
    printf("detailed_font_name: -%s-\n", detail_font_name);
  else
    printf("detailed_font_name not found\n");
  fontStyles[0] = HPDF_GetFont(m_pimpl->pdf, detail_font_name, NULL);//"UTF-8");
*/
  fontStyles[0] = HPDF_GetFont(m_pimpl->pdf, HPDF_LoadTTFontFromFile2(m_pimpl->pdf, "/System/Library/Fonts/Avenir Next.ttc", 7, HPDF_FONTOPT_EMBEDDING), NULL);// "Modern-UTF32-H");
  // fontStyles[0] = HPDF_GetFont(m_pimpl->pdf, HPDF_LoadTTFontFromFile2(m_pimpl->pdf, "/System/Library/Fonts/Avenir Next.ttc", 7, HPDF_FONTOPT_EMBEDDING), NULL);
  fontStyles[1] = HPDF_GetFont(m_pimpl->pdf, HPDF_LoadTTFontFromFile2(m_pimpl->pdf, "/System/Library/Fonts/Avenir Next.ttc", 4, HPDF_FONTOPT_EMBEDDING), NULL);
  fontStyles[2] = HPDF_GetFont(m_pimpl->pdf, HPDF_LoadTTFontFromFile2(m_pimpl->pdf, "/System/Library/Fonts/Avenir Next.ttc", 0, HPDF_FONTOPT_EMBEDDING), NULL);
  fontStyles[3] = HPDF_GetFont(m_pimpl->pdf, HPDF_LoadTTFontFromFile2(m_pimpl->pdf, "/System/Library/Fonts/Avenir Next.ttc", 1, HPDF_FONTOPT_EMBEDDING), NULL);
#endif

  fontStyles[4] = HPDF_GetFont(m_pimpl->pdf, "Courier", NULL);
	
  
  //HPDF_Font helv = HPDF_GetFont(m_pimpl->pdf, "Helvetica", NULL);//"Modern-UTF8-H");

const char* fontNames[] = {
//"Times.ttc",
//"Avenir Next.ttc",
//"Apple Color Emoji.ttc",
"AquaKana.ttc",
"ArabicUIDisplay.ttc",
"GeezaPro.ttc",
"Helvetica.ttc",
"Hiragino Sans GB.ttc",
"Kohinoor.ttc",
"KohinoorBangla.ttc",
"KohinoorGujarati.ttc",
"KohinoorTelugu.ttc",
"MuktaMahee.ttc",
"NotoNastaliq.ttc",
"NotoSansArmenian.ttc",
"NotoSansKannada.ttc",
"NotoSansMyanmar.ttc",
"NotoSansOriya.ttc",
//"PingFang.ttc",
//"STHeiti Light.ttc",
//"STHeiti Medium.ttc",
"Thonburi.ttc",
//"Times.ttc",
"ヒラギノ明朝 ProN.ttc",
"ヒラギノ丸ゴ ProN W4.ttc",
//"ヒラギノ角ゴシック W0.ttc",
//"ヒラギノ角ゴシック W1.ttc",
//"ヒラギノ角ゴシック W2.ttc",
//"ヒラギノ角ゴシック W3.ttc",
//"ヒラギノ角ゴシック W4.ttc",
//"ヒラギノ角ゴシック W5.ttc",
//"ヒラギノ角ゴシック W6.ttc",
//"ヒラギノ角ゴシック W7.ttc",
//"ヒラギノ角ゴシック W8.ttc",
//"ヒラギノ角ゴシック W9.ttc",
};

  HPDF_INT ttopt = HPDF_FONTOPT_EMBEDDING;
  std::string fnt = "/System/Library/Fonts/Times.ttc";
  std::string detailName = HPDF_LoadTTFontFromFile2(m_pimpl->pdf, fnt.c_str(), 0, ttopt);
  printf("detail name: -%s-\n", detailName.c_str());
  HPDF_Font fallback = HPDF_GetFont(m_pimpl->pdf, detailName.c_str(), NULL);

  for (const auto fontName : fontNames)
  {
    fnt = "/System/Library/Fonts/";
    fnt += fontName;
        printf("About to load %s!\n", fnt.c_str());
    detailName = HPDF_LoadTTFontFromFile2(m_pimpl->pdf, fnt.c_str(), 0, ttopt);
    printf("detail name: -%s-\n", detailName.c_str());
    HPDF_Font nextFont = HPDF_GetFont(m_pimpl->pdf, detailName.c_str(), NULL);
    HPDF_Font_SetReliefFont(nextFont, fallback);
    fallback = nextFont;
  }
  //HPDF_Font helv = HPDF_GetFont(m_pimpl->pdf, "Helvetica", NULL);//"Modern-UTF8-H");

  HPDF_Font_SetReliefFont(fontStyles[0], fallback);
  HPDF_Font_SetReliefFont(fontStyles[1], fallback);
  HPDF_Font_SetReliefFont(fontStyles[2], fallback);
  HPDF_Font_SetReliefFont(fontStyles[3], fallback);
  /*
*/
  
  printf("HERE1!!!\n");
  //HPDF_Font_SetReliefFont(fontStyles[4], helv);
  printf("HERE2!!!\n");

#if 0
   /* Korean */
   detail_font_name = HPDF_LoadTTFontFromFile2 (pdf, "C:\\Windows\\Fonts\\gulim.ttc", 1, HPDF_FONTOPT_EMBEDDING /* | HPDF_FONTOPT_WITH_CID_MAP */);
   detail_font = HPDF_GetFont (pdf, detail_font_name, "UTF-8");
   /* Simplified Chinese, Traditional Chinese, Japanese */
   relief_font = detail_font;
   detail_font_name = HPDF_LoadTTFontFromFile2 (pdf, "C:\\Windows\\Fonts\\simsun.ttc", 1, HPDF_FONTOPT_EMBEDDING /* | HPDF_FONTOPT_WITH_CID_MAP */);
   detail_font = HPDF_GetFont (pdf, detail_font_name, "UTF-8");
   HPDF_Font_SetReliefFont (detail_font, relief_font);
   /* Devanagari */
   relief_font = detail_font;
   detail_font_name = HPDF_LoadTTFontFromFile (pdf, "C:\\Windows\\Fonts\\mangal.ttf", HPDF_FONTOPT_EMBEDDING /* | HPDF_FONTOPT_WITH_CID_MAP */);
   detail_font = HPDF_GetFont (pdf, detail_font_name, "Ancient-UTF8-H");
   HPDF_Font_SetReliefFont (detail_font, relief_font);
   /* Thai, Armenian */
   relief_font = detail_font;
   detail_font_name = HPDF_LoadTTFontFromFile (pdf, "C:\\Windows\\Fonts\\tahoma.ttf", HPDF_FONTOPT_EMBEDDING /* | HPDF_FONTOPT_WITH_CID_MAP */);
   detail_font = HPDF_GetFont (pdf, detail_font_name, "Ancient-UTF8-H");
   HPDF_Font_SetReliefFont (detail_font, relief_font);
   /* Latin, Cyrillic, Greek, Arabic, Hebrew */
   relief_font = detail_font;
   detail_font_name = HPDF_LoadTTFontFromFile (pdf, "C:\\Windows\\Fonts\\times.ttf", HPDF_FONTOPT_EMBEDDING /* | HPDF_FONTOPT_WITH_CID_MAP */);
   detail_font = HPDF_GetFont (pdf, detail_font_name, "Ancient-UTF8-H");
   HPDF_Font_SetReliefFont (detail_font, relief_font);
#endif




#if 0
   HPDF_Font detail_font, detail_font_v, relief_font;
   const char *detail_font_name;

   /* Devanagari */
   detail_font_name = HPDF_LoadTTFontFromFile (pdf, "C:\\Windows\\Fonts\\mangal.ttf", ttopt);
   detail_font = HPDF_GetFont (pdf, detail_font_name, "Ancient-UTF8-H");
   detail_font_v = HPDF_GetFont (pdf, detail_font_name, "Ancient-UTF16-H");

   /* Thai, Armenian */
   detail_font_name = HPDF_LoadTTFontFromFile (pdf, "C:\\Windows\\Fonts\\tahoma.ttf", ttopt);
   relief_font = detail_font;
   detail_font = HPDF_GetFont (pdf, detail_font_name, "Ancient-UTF8-H");
   HPDF_Font_SetReliefFont (detail_font, relief_font);
   
   relief_font = detail_font_v;
   detail_font_v = HPDF_GetFont (pdf, detail_font_name, "Ancient-UTF16-H");
   HPDF_Font_SetReliefFont (detail_font_v, relief_font);


   /* Korean */
   detail_font_name = HPDF_LoadTTFontFromFile2 (pdf, "C:\\Windows\\Fonts\\gulim.ttc", 1, ttopt);
   relief_font = detail_font;
   detail_font = HPDF_GetFont (pdf, detail_font_name, "UniKS-UTF8-H");
   HPDF_Font_SetReliefFont (detail_font, relief_font);

   /* Simplified Chinese, Traditional Chinese, Japanese */
   detail_font_name = HPDF_LoadTTFontFromFile2 (pdf, "C:\\Windows\\Fonts\\simsun.ttc", 1, ttopt);
   relief_font = detail_font;
   detail_font = HPDF_GetFont (pdf, detail_font_name, "UniGB-UTF8-H");
   HPDF_Font_SetReliefFont (detail_font, relief_font);

   /* Latin, Cyrillic, Greek, Arabic, Hebrew */
   detail_font_name = HPDF_LoadTTFontFromFile (pdf, "C:\\Windows\\Fonts\\times.ttf", ttopt);
   relief_font = detail_font;
   detail_font = HPDF_GetFont (pdf, detail_font_name, "Ancient-UTF8-H");
   HPDF_Font_SetReliefFont (detail_font, relief_font);
#endif



}


DocOutputPage* DocOutputDevice::newPage()
{
	return new DocOutputPage(this);
}


HPDF_Doc DocOutputDevice::doc()
{
  return m_pimpl->pdf;
}


HPDF_Image DocOutputDevice::loadImage(const char* fileName, bool& result)
{
  if (m_pimpl->imageCache.count(fileName) == 0)
  {
    auto* mmgr = m_pimpl->pdf->mmgr;

    //if (reinterpret_cast<size_t>(mmgr) < 10)
    {
      printf("\n\n\n\n\n\nXXXCXCDFDF HERE   %zx\n", reinterpret_cast<size_t>(mmgr));
    }


    // TODO: detect and handle different image file types
    // HPDF_LoadJpegImageFromFile
    HPDF_Image img = HPDF_LoadPngImageFromFile2(m_pimpl->pdf, fileName);

    HPDF_STATUS errorNo = HPDF_Error_GetCode(&m_pimpl->pdf->error);
    if (errorNo != HPDF_OK)
    {
      HPDF_Error_Reset(&m_pimpl->pdf->error);
      result = false;
      return 0;
    }

    m_pimpl->imageCache.emplace(fileName, img);
  }
  result = true;
  return m_pimpl->imageCache.at(fileName);
}


void DocOutputDevice::setAuthor(std::string author)
{
  if (!author.empty() && HPDF_SetInfoAttr(m_pimpl->pdf, HPDF_INFO_AUTHOR, author.c_str()) != HPDF_OK)
	{
		printf("error: cannot set author on PdfDoc object\n");
	}
}


void DocOutputDevice::setTitle(std::string title, std::string subTitle)
{
  if (!title.empty() && HPDF_SetInfoAttr(m_pimpl->pdf, HPDF_INFO_TITLE, title.c_str()) != HPDF_OK)
	{
		printf("error: cannot set title on PdfDoc object\n");
	}

  if (!subTitle.empty() && HPDF_SetInfoAttr(m_pimpl->pdf, HPDF_INFO_SUBJECT, subTitle.c_str()) != HPDF_OK)
	{
		printf("error: cannot set subtitle/subject on PdfDoc object\n");
	}
}


void DocOutputDevice::setPassword(std::string password)
{
  if (!password.empty() && HPDF_SetPassword(m_pimpl->pdf, password.c_str(), "user") != HPDF_OK)
  {
		printf("error: cannot set password on PdfDoc object\n");
	}
}


void DocOutputDevice::finalize(char* outputBuffer, size_t* size)
{
  if (!outputBuffer || !size)
    return; // TODO: raise error
  HPDF_UINT32 siz = *size;
  HPDF_GetContents(m_pimpl->pdf, (HPDF_BYTE*)outputBuffer, &siz);
  *size = siz;
}


void DocOutputDevice::finalize(const char* outputFileName)
{
  if (!outputFileName)
    return; // TODO: raise error
  //printf("saving to %s\n", outputFileName);
  HPDF_SaveToFile(m_pimpl->pdf, outputFileName);
}

