JNIEXPORT void JNICALL Java_com_draekko_libharu_PdfPage_setSize
    (JNIEnv *env, jobject obj, jobject sizeEnum, jobject directionEnum) {

    haru_setup_error_handler(env, __func__);

    jclass PageSize = env->FindClass("com/draekko/libharu/PdfPage$PageSize");
    jmethodID PageSize_getNameMethod = env->GetMethodID(PageSize, "name", "()Ljava/lang/String;");
    jstring size_value = (jstring)env->CallObjectMethod(sizeEnum, PageSize_getNameMethod);
    const char* size_str = env->GetStringUTFChars(size_value, 0);

    jclass PageDirection = env->FindClass("com/draekko/libharu/PdfPage$PageDirection");
    jmethodID PageDirection_getNameMethod = env->GetMethodID(PageDirection, "name", "()Ljava/lang/String;");
    jstring direction_value = (jstring)env->CallObjectMethod(directionEnum, PageDirection_getNameMethod);
    const char* direction_str = env->GetStringUTFChars(direction_value, 0);

    HPDF_PageSizes size;
    if (strcmp(size_str, "LETTER") == 0) {
        size = HPDF_PAGE_SIZE_LETTER;
    } else if (strcmp(size_str, "LEGAL") == 0) {
        size = HPDF_PAGE_SIZE_LEGAL;
    } else if (strcmp(size_str, "A3") == 0) {
        size = HPDF_PAGE_SIZE_A3;
    } else if (strcmp(size_str, "A4") == 0) {
        size = HPDF_PAGE_SIZE_A4;
    } else if (strcmp(size_str, "A5") == 0) {
        size = HPDF_PAGE_SIZE_A5;
    } else if (strcmp(size_str, "B4") == 0) {
        size = HPDF_PAGE_SIZE_B4;
    } else if (strcmp(size_str, "B5") == 0) {
        size = HPDF_PAGE_SIZE_B5;
    } else if (strcmp(size_str, "EXECUTIVE") == 0) {
        size = HPDF_PAGE_SIZE_EXECUTIVE;
    } else if (strcmp(size_str, "US4x6") == 0) {
        size = HPDF_PAGE_SIZE_US4x6;
    } else if (strcmp(size_str, "US4x8") == 0) {
        size = HPDF_PAGE_SIZE_US4x8;
    } else if (strcmp(size_str, "US5x7") == 0) {
        size = HPDF_PAGE_SIZE_US5x7;
    } else if (strcmp(size_str, "COMM10") == 0) {
        size = HPDF_PAGE_SIZE_COMM10;
    } else {
        haru_throw_exception("Unsupported page size.");
    }

    HPDF_PageDirection direction;
    if (strcmp(direction_str, "PORTRAIT") == 0) {
        direction = HPDF_PAGE_PORTRAIT;
    } else if (strcmp(direction_str, "LANDSCAPE") == 0) {
        direction = HPDF_PAGE_LANDSCAPE;
    } else {
        haru_throw_exception("Unsupported orientation.");
    }

    HPDF_Page page = get_HPDF_Page(env, obj); 
    HPDF_Page_SetSize(page, size, direction);

    env->ReleaseStringUTFChars(size_value, size_str);
    env->ReleaseStringUTFChars(direction_value, direction_str);
    haru_clear_error_handler();
}
JNIEXPORT void JNICALL Java_org_libharu_Page_setLineCap
  (JNIEnv *env, jobject obj, jobject cap)
{
  haru_setup_error_handler(env, __func__);

  jclass LineCap = env->FindClass("org/libharu/Page$LineCap");
  jmethodID getNameMethod = env->GetMethodID(LineCap, "name", "()Ljava/lang/String;");
  jstring cap_value = (jstring)env->CallObjectMethod(cap, getNameMethod);
  const char* cap_str = env->GetStringUTFChars(cap_value, 0);

  HPDF_Page page = get_HPDF_Page(env, obj); 
  if (strcmp(cap_str, "BUTT_END") == 0) {
    HPDF_Page_SetLineCap(page, HPDF_BUTT_END);
  } else if (strcmp(cap_str, "ROUND_END") == 0) {
    HPDF_Page_SetLineCap(page, HPDF_ROUND_END);
  } else if (strcmp(cap_str, "PROJECTING_SQUARE_END") == 0) {
    // Note the typo square -> scuare
    HPDF_Page_SetLineCap(page, HPDF_PROJECTING_SCUARE_END);
  } else {
    haru_throw_exception("Unsupported Cap style.");
  }

  env->ReleaseStringUTFChars(cap_value, cap_str);
  haru_clear_error_handler();
}
JNIEXPORT void JNICALL Java_com_draekko_libharu_PdfPage_setTextRenderingMode
  (JNIEnv *env, jobject obj, jobject cap) {

    haru_setup_error_handler(env, __func__);

    jclass RenderingMode = env->FindClass("com/draekko/libharu/PdfPage$RenderingMode");
    jmethodID getNameMethod = env->GetMethodID(RenderingMode, "name", "()Ljava/lang/String;");
    jstring renderMode = (jstring)env->CallObjectMethod(renderMode, getNameMethod);
    const char* mode_str = env->GetStringUTFChars(renderMode, 0);

    HPDF_TextRenderingMode mode;
    HPDF_Page page = get_HPDF_Page(env, obj); 
    if (strcmp(mode_str, "RENDER_MODE_FILL") == 0) {
        HPDF_Page_SetTextRenderingMode(page, HPDF_FILL);
    } else if (strcmp(mode_str, "RENDER_MODE_STROKE") == 0) {
        HPDF_Page_SetTextRenderingMode(page, HPDF_STROKE);
    } else if (strcmp(mode_str, "RENDER_MODE_FILL_THEN_STROKE") == 0) {
        HPDF_Page_SetTextRenderingMode(page, HPDF_FILL_THEN_STROKE);
    } else if (strcmp(mode_str, "RENDER_MODE_INVISIBLE") == 0) {
        HPDF_Page_SetTextRenderingMode(page, HPDF_INVISIBLE);
    } else if (strcmp(mode_str, "RENDER_MODE_FILL_CLIPPING") == 0) {
        HPDF_Page_SetTextRenderingMode(page, HPDF_FILL_CLIPPING);
    } else if (strcmp(mode_str, "RENDER_MODE_STROKE_CLIPPING") == 0) {
        HPDF_Page_SetTextRenderingMode(page, HPDF_STROKE_CLIPPING);
    } else if (strcmp(mode_str, "RENDER_MODE_FILL_STROKE_CLIPPING") == 0) {
        HPDF_Page_SetTextRenderingMode(page, HPDF_FILL_STROKE_CLIPPING);
    } else if (strcmp(mode_str, "RENDER_MODE__CLIPPING") == 0) {
        HPDF_Page_SetTextRenderingMode(page, HPDF_CLIPPING);
    } else {
        haru_throw_exception("Unsupported Rendering Mode.");
    }

    env->ReleaseStringUTFChars(renderMode, mode_str);
    haru_clear_error_handler();
}
JNIEXPORT void JNICALL Java_org_libharu_Document_construct
  (JNIEnv *env, jobject obj)
{
  haru_setup_error_handler(env, __func__);
  HPDF_Doc pdf = HPDF_New(haru_error_handler, NULL);
  set_HPDF_Doc(env, obj, pdf);
  if (pdf == NULL)
    haru_throw_exception("Failed to create new PDF document.");
  haru_clear_error_handler();
}
JNIEXPORT void JNICALL Java_org_libharu_Document_saveToFile
  (JNIEnv *env, jobject obj, jstring filename)
{
  haru_setup_error_handler(env, __func__);
  const char *str = (char*)env->GetStringUTFChars(filename, NULL);
  if (str == NULL) return;

  HPDF_Doc pdf = get_HPDF_Doc(env, obj);
  HPDF_STATUS rc = HPDF_SaveToFile(pdf, str);

  env->ReleaseStringUTFChars(filename, str);
  if (rc == HPDF_OK)
    ;
  else if (rc == HPDF_INVALID_DOCUMENT)
    haru_throw_exception("An invalid document handle is set.");
  else if (rc == HPDF_FAILD_TO_ALLOC_MEM)
    haru_throw_exception("Memory allocation failed.");
  else if (rc == HPDF_FILE_IO_ERROR)
    haru_throw_exception("An error occurred while processing file I/O.");
  else 
    haru_throw_exception("Unknown return code.");
  haru_clear_error_handler();
}
JNIEXPORT jint JNICALL Java_com_draekko_libharu_PdfPage_textRect
  (JNIEnv *env, jobject obj, jfloat jleft, jfloat jtop, jfloat jright, jfloat jbottom, jstring jtext, jobject jalignmentEnum) {

    haru_setup_error_handler(env, __func__);

    jclass TextRectAlign = env->FindClass("com/draekko/libharu/PdfPage$TextRectAlign");
    jmethodID TextRectAlign_getNameMethod = env->GetMethodID(TextRectAlign, "name", "()Ljava/lang/String;");
    jstring alignment_value = (jstring)env->CallObjectMethod(jalignmentEnum, TextRectAlign_getNameMethod);
    const char* alignment_str = env->GetStringUTFChars(alignment_value, 0);

    const char *text = env->GetStringUTFChars(jtext, 0);
    HPDF_UINT  *len;
    HPDF_REAL  left = jleft;
    HPDF_REAL  top = jtop;
    HPDF_REAL  right = jright;
    HPDF_REAL  bottom = jbottom;
    HPDF_TextAlignment align;
    HPDF_INT   value;

    if (strcmp(alignment_str, "TALIGN_LEFT") == 0) {
        align = HPDF_TALIGN_LEFT;
    } else if (strcmp(alignment_str, "TALIGN_RIGHT") == 0) {
        align = HPDF_TALIGN_RIGHT;
    } else if (strcmp(alignment_str, "TALIGN_CENTER") == 0) {
        align = HPDF_TALIGN_CENTER;
    } else if (strcmp(alignment_str, "TALIGN_JUSTIFY") == 0) {
        align = HPDF_TALIGN_JUSTIFY;
    } else {
        haru_throw_exception("Unsupported alignment.");
    }

    HPDF_Page page = get_HPDF_Page(env, obj);
    HPDF_Page_TextRect (page, left, top, right, bottom, text, align, len);

    env->ReleaseStringUTFChars(jtext, text);

    if (len == NULL) {
        value = -1;
    } else if (len < 0) {
        value = 0;
    } else {
        value = (long)len;
    }

    haru_clear_error_handler();

    return value;
}
JNIEXPORT void JNICALL Java_org_libharu_Page_setLineJoin
  (JNIEnv *env, jobject obj, jobject join)
{
  haru_setup_error_handler(env, __func__);

  jclass LineJoin = env->FindClass("org/libharu/Page$LineJoin");
  jmethodID getNameMethod = env->GetMethodID(LineJoin, "name", "()Ljava/lang/String;");
  jstring join_value = (jstring)env->CallObjectMethod(join, getNameMethod);
  const char* join_str = env->GetStringUTFChars(join_value, 0);

  HPDF_Page page = get_HPDF_Page(env, obj); 
  if (strcmp(join_str, "MITER_JOIN") == 0)      HPDF_Page_SetLineJoin(page, HPDF_MITER_JOIN);
  else if (strcmp(join_str, "ROUND_JOIN") == 0) HPDF_Page_SetLineJoin(page, HPDF_ROUND_JOIN);
  else if (strcmp(join_str, "BEVEL_JOIN") == 0) HPDF_Page_SetLineJoin(page, HPDF_BEVEL_JOIN);
  else haru_throw_exception("Unsupported line join.");

  env->ReleaseStringUTFChars(join_value, join_str);
  haru_clear_error_handler();
}
JNIEXPORT void JNICALL Java_org_libharu_Document_setCompressionMode
  (JNIEnv *env, jobject obj, jobject compression)
{
  haru_setup_error_handler(env, __func__);
  jclass CompressionMode = env->FindClass("org/libharu/Document$CompressionMode");
  jmethodID getNameMethod = env->GetMethodID(CompressionMode, "name", "()Ljava/lang/String;");
  jstring comp_value = (jstring)env->CallObjectMethod(compression, getNameMethod);
  const char* comp_str = env->GetStringUTFChars(comp_value, 0);

  HPDF_UINT mode;
  if (strcmp(comp_str, "COMP_NONE") == 0)          mode = HPDF_COMP_NONE;
  else if (strcmp(comp_str, "COMP_TEXT") == 0)     mode = HPDF_COMP_TEXT; 
  else if (strcmp(comp_str, "COMP_IMAGE") == 0)    mode = HPDF_COMP_IMAGE; 
  else if (strcmp(comp_str, "COMP_METADATA") == 0) mode = HPDF_COMP_METADATA;
  else if (strcmp(comp_str, "COMP_ALL") == 0)      mode = HPDF_COMP_ALL;
  else haru_throw_exception("Unknown compression mode.");

  HPDF_Doc pdf = get_HPDF_Doc(env, obj);
  HPDF_SetCompressionMode (pdf, mode);
  haru_clear_error_handler();
}