From 54d82e5796115c5f5dc750e7875a28d7cc9d2d4a Mon Sep 17 00:00:00 2001
From: maximiliank <kleinert.max@gmail.com>
Date: Mon, 22 Dec 2014 12:50:20 +0100
Subject: [PATCH] Added vertical alignment options to HPDF_TextRect functions.
 Capable of multiple lines.

---
 include/hpdf.h              |  2 +-
 include/hpdf_textlinelist.h | 45 ++++++++++++++++++++++++++
 include/hpdf_types.h        | 11 ++++---
 src/CMakeLists.txt          |  1 +
 src/hpdf_page_operator.c    | 64 +++++++++++++++++++++++++++++++------
 src/hpdf_textlinelist.c     | 61 +++++++++++++++++++++++++++++++++++
 6 files changed, 169 insertions(+), 15 deletions(-)
 create mode 100644 include/hpdf_textlinelist.h
 create mode 100644 src/hpdf_textlinelist.c

diff --git a/include/hpdf.h b/include/hpdf.h
index 1cf0dd9..2dca446 100644
--- a/include/hpdf.h
+++ b/include/hpdf.h
@@ -1569,7 +1569,7 @@ HPDF_Page_TextRect  (HPDF_Page            page,
                      HPDF_REAL            right,
                      HPDF_REAL            bottom,
                      const char          *text,
-                     HPDF_TextAlignment   align,
+                     HPDF_UINT   align,
                      HPDF_UINT           *len);
 
 
diff --git a/include/hpdf_textlinelist.h b/include/hpdf_textlinelist.h
new file mode 100644
index 0000000..a99c157
--- /dev/null
+++ b/include/hpdf_textlinelist.h
@@ -0,0 +1,45 @@
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include "hpdf_types.h"
+#include "hpdf_consts.h"
+
+/**
+ * The TextLine struct,
+ * contains item and the pointers that point to previous TextLine/next TextLine.
+ */
+typedef struct TextLine {
+    const char* item;
+    HPDF_UINT len, num_rest;
+    HPDF_BOOL LineBreak;
+    HPDF_REAL rw;
+    struct TextLine* next;
+} TextLine;
+/**
+ * The LinkedList struct, contains the pointers that
+ * point to first TextLine and last TextLine, the size of the LinkedList,
+ * and the function pointers.
+ */
+typedef struct LinkedList {
+    TextLine* head;
+    TextLine* tail;
+    // size of this LinkedList
+    HPDF_UINT size;
+
+    // add item after tail
+    void (*Append) (struct LinkedList*, const char*, HPDF_UINT, HPDF_UINT, HPDF_REAL, HPDF_BOOL);
+
+    // get first item
+    TextLine* (*getFirst) (struct LinkedList*);
+
+    // free allocated memory
+    void (*Delete) (struct LinkedList*);
+
+    // create a TextLine with item
+    TextLine* (*createTextLine) (const char*, HPDF_UINT, HPDF_UINT, HPDF_REAL, HPDF_BOOL);
+} LinkedList;
+
+void Append (LinkedList* _this, const char *str, HPDF_UINT len, HPDF_UINT num_rest, HPDF_REAL rw, HPDF_BOOL LineBreak);
+void Delete (LinkedList* _this);
+LinkedList createLinkedList ();
+TextLine* createTextLine (const char *str, HPDF_UINT len, HPDF_UINT num_rest, HPDF_REAL rw, HPDF_BOOL LineBreak);
diff --git a/include/hpdf_types.h b/include/hpdf_types.h
index a35fe8f..ae6fcb6 100644
--- a/include/hpdf_types.h
+++ b/include/hpdf_types.h
@@ -561,10 +561,13 @@ typedef enum _HPDF_ByteType {
 
 
 typedef enum _HPDF_TextAlignment {
-    HPDF_TALIGN_LEFT = 0,
-    HPDF_TALIGN_RIGHT,
-    HPDF_TALIGN_CENTER,
-    HPDF_TALIGN_JUSTIFY
+    HPDF_TALIGN_LEFT = 0x00,
+    HPDF_TALIGN_RIGHT = 0x02,
+    HPDF_TALIGN_CENTER = 0x04,
+    HPDF_TALIGN_JUSTIFY = 0x08,
+    HPDF_TALIGN_TOP = 0x10,
+    HPDF_TALIGN_BOTTOM = 0x20,
+    HPDF_TALIGN_MIDDLE = 0x40
 } HPDF_TextAlignment;
 
 /*----------------------------------------------------------------------------*/
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 9d2a604..d98d5b3 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -65,6 +65,7 @@ set(
 	hpdf_3dmeasure.c
 	hpdf_exdata.c
 	hpdf_encoder_utf.c
+    hpdf_textlinelist.c
 )
 
 # =======================================================================
diff --git a/src/hpdf_page_operator.c b/src/hpdf_page_operator.c
index 23f5920..396fadb 100644
--- a/src/hpdf_page_operator.c
+++ b/src/hpdf_page_operator.c
@@ -20,6 +20,8 @@
 #include "hpdf_pages.h"
 #include "hpdf.h"
 
+#include "hpdf_textlinelist.h"
+
 static const HPDF_Point INIT_POS = {0, 0};
 static const HPDF_DashMode INIT_MODE = {{0, 0, 0, 0, 0, 0, 0, 0}, 0, 0};
 
@@ -2432,7 +2434,7 @@ HPDF_Page_TextRect  (HPDF_Page            page,
                      HPDF_REAL            right,
                      HPDF_REAL            bottom,
                      const char          *text,
-                     HPDF_TextAlignment   align,
+                     HPDF_UINT   align,
                      HPDF_UINT           *len
                      )
 {
@@ -2473,17 +2475,15 @@ HPDF_Page_TextRect  (HPDF_Page            page,
         HPDF_Page_SetTextLeading (page, (bbox.top - bbox.bottom) / 1000 *
                 attr->gstate->font_size);
 
-    top = top - bbox.top / 1000 * attr->gstate->font_size +
-                attr->gstate->text_leading;
-    bottom = bottom - bbox.bottom / 1000 * attr->gstate->font_size;
-
     if (align == HPDF_TALIGN_JUSTIFY) {
         save_char_space = attr->gstate->char_space;
         attr->gstate->char_space = 0;
     }
 
+    /* Create LinkedList of Text lines */
+    LinkedList list = createLinkedList();
+
     for (;;) {
-        HPDF_REAL x, y;
         HPDF_UINT line_len, tmp_len;
         HPDF_REAL rw;
         HPDF_BOOL LineBreak;
@@ -2507,6 +2507,48 @@ HPDF_Page_TextRect  (HPDF_Page            page,
                 LineBreak = HPDF_TRUE;
             }
         }
+        list.Append(&list, ptr, tmp_len, num_rest, rw, LineBreak);
+        ptr += line_len;
+    }
+
+    HPDF_REAL total_height = list.size * attr->gstate->text_leading;
+
+    HPDF_REAL correction =  bbox.bottom / 1000 * attr->gstate->font_size;
+
+    if ((align & HPDF_TALIGN_MIDDLE) == HPDF_TALIGN_MIDDLE)
+    {
+            HPDF_REAL adjustment = 0.5 * (top - bottom - total_height);
+            top = top - adjustment - correction;
+            bottom = bottom + adjustment - correction;
+    }
+    else if ((align & HPDF_TALIGN_BOTTOM) == HPDF_TALIGN_BOTTOM)
+    {
+        bottom = bottom - correction;
+        top = bottom + total_height;
+    }
+    else // HPDF_TALIGN_TOP or default
+    {
+        top = top - correction;
+        bottom = top - total_height;
+    }
+    // remove vertical alignment flags
+    align &= ~(HPDF_TALIGN_TOP|HPDF_TALIGN_MIDDLE|HPDF_TALIGN_BOTTOM);
+
+
+    TextLine* textline = list.getFirst(&list);
+
+    while (textline != NULL)
+    {
+        HPDF_REAL x, y;
+        HPDF_UINT tmp_len;
+        HPDF_REAL rw;
+        HPDF_BOOL LineBreak;
+
+        const char* _ptr = textline->item;
+        tmp_len = textline->len;
+        num_rest = textline->num_rest;
+        rw = textline->rw;
+        LineBreak = textline->LineBreak;
 
         switch (align) {
 
@@ -2552,7 +2594,7 @@ HPDF_Page_TextRect  (HPDF_Page            page,
                     HPDF_UINT i = 0;
                     HPDF_UINT num_char = 0;
                     HPDF_Encoder encoder = ((HPDF_FontAttr)attr->gstate->font->attr)->encoder;
-                    const char *tmp_ptr = ptr;
+                    const char *tmp_ptr = _ptr;
                     HPDF_Encoder_SetParseText (encoder, &state, (HPDF_BYTE *)tmp_ptr, tmp_len);
                     while (*tmp_ptr) {
                         HPDF_ByteType btype = HPDF_Encoder_ByteType (encoder, &state);
@@ -2580,20 +2622,22 @@ HPDF_Page_TextRect  (HPDF_Page            page,
                 }
         }
 
-        if (InternalShowTextNextLine (page, ptr, tmp_len) != HPDF_OK)
+        if (InternalShowTextNextLine (page, _ptr, tmp_len) != HPDF_OK)
             return HPDF_CheckError (page->error);
 
         if (num_rest <= 0)
             break;
 
-        if (attr->text_pos.y - attr->gstate->text_leading < bottom) {
+        if (attr->text_pos.y - attr->gstate->text_leading - bottom < -1e-4)  {
             is_insufficient_space = HPDF_TRUE;
             break;
         }
 
-        ptr += line_len;
+        textline = textline->next;
     }
 
+    list.Delete(&list);
+
     if (char_space_changed && save_char_space != attr->gstate->char_space) {
         if ((ret = HPDF_Page_SetCharSpace (page, save_char_space)) != HPDF_OK)
             return ret;
diff --git a/src/hpdf_textlinelist.c b/src/hpdf_textlinelist.c
new file mode 100644
index 0000000..c4cea81
--- /dev/null
+++ b/src/hpdf_textlinelist.c
@@ -0,0 +1,61 @@
+#include "hpdf_textlinelist.h"
+/** add item to tail
+ */
+void Append (LinkedList* _this, const char *str, HPDF_UINT len, HPDF_UINT num_rest, HPDF_REAL rw, HPDF_BOOL LineBreak) {
+    TextLine* newTextLine = _this->createTextLine(str, len, num_rest, rw, LineBreak);
+    TextLine* head = _this->head;
+    TextLine* tail = _this->tail;
+    // list is empty
+    if (head == NULL)
+        _this->head = newTextLine;
+    else { // has item(s)
+        TextLine* lastTextLine = tail;
+        if (tail == NULL) // only head TextLine
+            lastTextLine = head;
+        lastTextLine->next = newTextLine;
+        _this->tail = newTextLine;
+    }
+    _this->size++;
+}
+/** get item from head
+ */
+TextLine* getFirst (LinkedList* _this) {
+    return _this->head;
+}
+/** free allocated memory
+ */
+void Delete (LinkedList* _this) {
+    TextLine* tmp;
+    TextLine* textline = _this->head;
+    while( textline != NULL)
+    {
+        tmp = textline->next;
+        free(textline);
+        textline = tmp;
+    }
+}
+/** create a LinkedList
+ */
+LinkedList createLinkedList () {
+    LinkedList list;
+    list.head = NULL;
+    list.tail = NULL;
+    list.size = 0;
+    list.Append = &Append;
+    list.getFirst = &getFirst;
+    list.createTextLine = &createTextLine;
+    list.Delete = &Delete;
+    return list;
+}
+/** create a TextLine
+ */
+TextLine* createTextLine (const char *str, HPDF_UINT len, HPDF_UINT num_rest, HPDF_REAL rw, HPDF_BOOL LineBreak) {
+    TextLine* textline = (TextLine*) malloc (sizeof(TextLine));
+    textline->item = str;
+    textline->len = len;
+    textline->num_rest = num_rest;
+    textline->rw = rw;
+    textline->LineBreak = LineBreak;
+    textline->next = NULL;
+    return textline;
+}
-- 
2.21.1 (Apple Git-122.3)

