From fc1c711cb5f769546c6b31cc688bf0ee7f0c1dbc Mon Sep 17 00:00:00 2001
From: Sune Vuorela <sune@vuorela.dk>
Date: Thu, 1 Feb 2024 19:11:03 +0000
Subject: [PATCH] More unicode vectors; fewer raw pointers

Conflicts:-qt5/tests/check_utf8document.cpp and qt6/tests/check_utf8document.cpp
           files are not presented in this code version. We ignore them.
          -In file utils/pdfsig.cc TextStringToUTF8 API don't exist
           in this code version. We ignore them.

CVE: CVE-2024-6239
Upstream-Status: Backport [https://gitlab.freedesktop.org/poppler/poppler/-/commit/fc1c711cb5f769546c6b31cc688bf0ee7f0c1dbc]

Signed-off-by: Yogita Urade <yogita.urade@windriver.com>
---
 cpp/poppler-toc.cpp                  |   5 +-
 glib/poppler-document.cc             |   3 +-
 poppler/CharCodeToUnicode.cc         | 113 +++++++++------------------
 poppler/CharCodeToUnicode.h          |  12 ++-
 poppler/DateInfo.cc                  |  10 +--
 poppler/JSInfo.cc                    |   9 +--
 poppler/Outline.cc                   |  12 +--
 poppler/Outline.h                    |   7 +-
 poppler/TextOutputDev.cc             |   8 +-
 poppler/UTF.cc                       |  78 ++++++++----------
 poppler/UTF.h                        |  11 ++-
 qt5/src/poppler-outline.cc           |   2 +-
 qt5/src/poppler-private.cc           |  13 ++-
 qt5/src/poppler-private.h            |   1 +
 qt5/tests/check_internal_outline.cpp |   6 +-
 qt5/tests/check_utf_conversion.cpp   |  24 +++---
 qt6/src/poppler-outline.cc           |   2 +-
 qt6/src/poppler-private.cc           |   5 ++
 qt6/src/poppler-private.h            |   1 +
 qt6/tests/check_internal_outline.cpp |   6 +-
 qt6/tests/check_utf_conversion.cpp   |  24 +++---
 utils/HtmlFonts.cc                   |   4 +-
 utils/HtmlFonts.h                    |   2 +-
 utils/HtmlOutputDev.cc               |  38 ++++-----
 utils/HtmlOutputDev.h                |   2 +-
 utils/pdfinfo.cc                     |   8 +-
 utils/pdftohtml.cc                   |  25 +++---
 27 files changed, 180 insertions(+), 251 deletions(-)

diff --git a/cpp/poppler-toc.cpp b/cpp/poppler-toc.cpp
index c79abde..ed3a983 100644
--- a/cpp/poppler-toc.cpp
+++ b/cpp/poppler-toc.cpp
@@ -61,9 +61,8 @@ toc_item_private::~toc_item_private()
 
 void toc_item_private::load(const OutlineItem *item)
 {
-    const Unicode *title_unicode = item->getTitle();
-    const int title_length = item->getTitleLength();
-    title = detail::unicode_to_ustring(title_unicode, title_length);
+    const std::vector<Unicode> &title_unicode = item->getTitle();
+    title = detail::unicode_to_ustring(title_unicode.data(), title_unicode.size());
     is_open = item->isOpen();
 }
 
diff --git a/glib/poppler-document.cc b/glib/poppler-document.cc
index 7796f5a..be8cea1 100644
--- a/glib/poppler-document.cc
+++ b/glib/poppler-document.cc
@@ -2773,7 +2773,8 @@ PopplerAction *poppler_index_iter_get_action(PopplerIndexIter *iter)
     item = (*iter->items)[iter->index];
     link_action = item->getAction();
 
-    title = unicode_to_char(item->getTitle(), item->getTitleLength());
+    const std::vector<Unicode> &itemTitle = item->getTitle();
+    title = unicode_to_char(itemTitle.data(), itemTitle.size());
 
     action = _poppler_action_new(iter->document, link_action, title);
     g_free(title);
diff --git a/poppler/CharCodeToUnicode.cc b/poppler/CharCodeToUnicode.cc
index cd00937..d9ef019 100644
--- a/poppler/CharCodeToUnicode.cc
+++ b/poppler/CharCodeToUnicode.cc
@@ -38,6 +38,7 @@
 
 #include <cstdio>
 #include <cstring>
+#include <functional>
 #include "goo/glibc.h"
 #include "goo/gmem.h"
 #include "goo/gfile.h"
@@ -51,13 +52,6 @@
 
 //------------------------------------------------------------------------
 
-struct CharCodeToUnicodeString
-{
-    CharCode c;
-    Unicode *u;
-    int len;
-};
-
 //------------------------------------------------------------------------
 
 static int getCharFromString(void *data)
@@ -162,7 +156,7 @@ CharCodeToUnicode *CharCodeToUnicode::parseCIDToUnicode(const char *fileName, co
     }
     fclose(f);
 
-    ctu = new CharCodeToUnicode(collection->toStr(), mapA, mapLenA, true, nullptr, 0, 0);
+    ctu = new CharCodeToUnicode(collection->toStr(), mapA, mapLenA, true, {});
     gfree(mapA);
     return ctu;
 }
@@ -171,8 +165,7 @@ CharCodeToUnicode *CharCodeToUnicode::parseUnicodeToUnicode(const GooString *fil
 {
     FILE *f;
     Unicode *mapA;
-    CharCodeToUnicodeString *sMapA;
-    CharCode size, oldSize, len, sMapSizeA, sMapLenA;
+    CharCode size, oldSize, len;
     char buf[256];
     char *tok;
     Unicode u0;
@@ -192,8 +185,7 @@ CharCodeToUnicode *CharCodeToUnicode::parseUnicodeToUnicode(const GooString *fil
     mapA = (Unicode *)gmallocn(size, sizeof(Unicode));
     memset(mapA, 0, size * sizeof(Unicode));
     len = 0;
-    sMapA = nullptr;
-    sMapSizeA = sMapLenA = 0;
+    std::vector<CharCodeToUnicodeString> sMapA;
 
     line = 0;
     while (getLine(buf, sizeof(buf), f)) {
@@ -230,17 +222,12 @@ CharCodeToUnicode *CharCodeToUnicode::parseUnicodeToUnicode(const GooString *fil
             mapA[u0] = uBuf[0];
         } else {
             mapA[u0] = 0;
-            if (sMapLenA == sMapSizeA) {
-                sMapSizeA += 16;
-                sMapA = (CharCodeToUnicodeString *)greallocn(sMapA, sMapSizeA, sizeof(CharCodeToUnicodeString));
-            }
-            sMapA[sMapLenA].c = u0;
-            sMapA[sMapLenA].u = (Unicode *)gmallocn(n, sizeof(Unicode));
+            std::vector<Unicode> u;
+            u.reserve(n);
             for (i = 0; i < n; ++i) {
-                sMapA[sMapLenA].u[i] = uBuf[i];
+                u.push_back(uBuf[i]);
             }
-            sMapA[sMapLenA].len = n;
-            ++sMapLenA;
+            sMapA.push_back({ u0, std::move(u) });
         }
         if (u0 >= len) {
             len = u0 + 1;
@@ -248,7 +235,7 @@ CharCodeToUnicode *CharCodeToUnicode::parseUnicodeToUnicode(const GooString *fil
     }
     fclose(f);
 
-    ctu = new CharCodeToUnicode(fileName->toStr(), mapA, len, true, sMapA, sMapLenA, sMapSizeA);
+    ctu = new CharCodeToUnicode(fileName->toStr(), mapA, len, true, std::move(sMapA));
     gfree(mapA);
     gfree(uBuf);
     return ctu;
@@ -256,7 +243,7 @@ CharCodeToUnicode *CharCodeToUnicode::parseUnicodeToUnicode(const GooString *fil
 
 CharCodeToUnicode *CharCodeToUnicode::make8BitToUnicode(Unicode *toUnicode)
 {
-    return new CharCodeToUnicode({}, toUnicode, 256, true, nullptr, 0, 0);
+    return new CharCodeToUnicode({}, toUnicode, 256, true, {});
 }
 
 CharCodeToUnicode *CharCodeToUnicode::parseCMap(const GooString *buf, int nBits)
@@ -512,25 +499,18 @@ void CharCodeToUnicode::addMapping(CharCode code, char *uStr, int n, int offset)
             map[code] = 0xfffd;
         }
     } else {
-        if (sMapLen >= sMapSize) {
-            sMapSize = sMapSize + 16;
-            sMap = (CharCodeToUnicodeString *)greallocn(sMap, sMapSize, sizeof(CharCodeToUnicodeString));
-        }
         map[code] = 0;
-        sMap[sMapLen].c = code;
         int utf16Len = n / 4;
-        Unicode *utf16 = (Unicode *)gmallocn(utf16Len, sizeof(Unicode));
+        std::vector<Unicode> utf16(utf16Len);
+        utf16.resize(utf16Len);
         for (j = 0; j < utf16Len; ++j) {
             if (!parseHex(uStr + j * 4, 4, &utf16[j])) {
-                gfree(utf16);
                 error(errSyntaxWarning, -1, "Illegal entry in ToUnicode CMap");
                 return;
             }
         }
         utf16[utf16Len - 1] += offset;
-        sMap[sMapLen].len = UTF16toUCS4(utf16, utf16Len, &sMap[sMapLen].u);
-        gfree(utf16);
-        ++sMapLen;
+        sMap.push_back({ code, UTF16toUCS4(utf16.data(), utf16.size()) });
     }
 }
 
@@ -561,8 +541,6 @@ CharCodeToUnicode::CharCodeToUnicode()
 {
     map = nullptr;
     mapLen = 0;
-    sMap = nullptr;
-    sMapLen = sMapSize = 0;
     refCnt = 1;
     isIdentity = false;
 }
@@ -576,13 +554,11 @@ CharCodeToUnicode::CharCodeToUnicode(const std::optional<std::string> &tagA) : t
     for (i = 0; i < mapLen; ++i) {
         map[i] = 0;
     }
-    sMap = nullptr;
-    sMapLen = sMapSize = 0;
     refCnt = 1;
     isIdentity = false;
 }
 
-CharCodeToUnicode::CharCodeToUnicode(const std::optional<std::string> &tagA, Unicode *mapA, CharCode mapLenA, bool copyMap, CharCodeToUnicodeString *sMapA, int sMapLenA, int sMapSizeA) : tag(tagA)
+CharCodeToUnicode::CharCodeToUnicode(const std::optional<std::string> &tagA, Unicode *mapA, CharCode mapLenA, bool copyMap, std::vector<CharCodeToUnicodeString> &&sMapA) : tag(tagA)
 {
     mapLen = mapLenA;
     if (copyMap) {
@@ -591,9 +567,7 @@ CharCodeToUnicode::CharCodeToUnicode(const std::optional<std::string> &tagA, Uni
     } else {
         map = mapA;
     }
-    sMap = sMapA;
-    sMapLen = sMapLenA;
-    sMapSize = sMapSizeA;
+    sMap = std::move(sMapA);
     refCnt = 1;
     isIdentity = false;
 }
@@ -601,12 +575,6 @@ CharCodeToUnicode::CharCodeToUnicode(const std::optional<std::string> &tagA, Uni
 CharCodeToUnicode::~CharCodeToUnicode()
 {
     gfree(map);
-    if (sMap) {
-        for (int i = 0; i < sMapLen; ++i) {
-            gfree(sMap[i].u);
-        }
-        gfree(sMap);
-    }
 }
 
 void CharCodeToUnicode::incRefCnt()
@@ -628,7 +596,8 @@ bool CharCodeToUnicode::match(const GooString *tagA)
 
 void CharCodeToUnicode::setMapping(CharCode c, Unicode *u, int len)
 {
-    int i, j;
+    size_t i;
+    int j;
 
     if (!map || isIdentity) {
         return;
@@ -636,28 +605,26 @@ void CharCodeToUnicode::setMapping(CharCode c, Unicode *u, int len)
     if (len == 1) {
         map[c] = u[0];
     } else {
-        for (i = 0; i < sMapLen; ++i) {
+        std::optional<std::reference_wrapper<CharCodeToUnicodeString>> element;
+        for (i = 0; i < sMap.size(); ++i) {
             if (sMap[i].c == c) {
-                gfree(sMap[i].u);
+                sMap[i].u.clear();
+                element = std::ref(sMap[i]);
                 break;
             }
         }
-        if (i == sMapLen) {
-            if (sMapLen == sMapSize) {
-                sMapSize += 8;
-                sMap = (CharCodeToUnicodeString *)greallocn(sMap, sMapSize, sizeof(CharCodeToUnicodeString));
-            }
-            ++sMapLen;
+        if (!element) {
+            sMap.emplace_back(CharCodeToUnicodeString { c, {} });
+            element = std::ref(sMap.back());
         }
         map[c] = 0;
-        sMap[i].c = c;
-        sMap[i].len = len;
-        sMap[i].u = (Unicode *)gmallocn(len, sizeof(Unicode));
+        element->get().c = c;
+        element->get().u.reserve(len);
         for (j = 0; j < len; ++j) {
             if (UnicodeIsValid(u[j])) {
-                sMap[i].u[j] = u[j];
+                element->get().u.push_back(u[j]);
             } else {
-                sMap[i].u[j] = 0xfffd;
+                element->get().u.push_back(0xfffd);
             }
         }
     }
@@ -665,8 +632,6 @@ void CharCodeToUnicode::setMapping(CharCode c, Unicode *u, int len)
 
 int CharCodeToUnicode::mapToUnicode(CharCode c, Unicode const **u) const
 {
-    int i;
-
     if (isIdentity) {
         map[0] = (Unicode)c;
         *u = map;
@@ -679,10 +644,10 @@ int CharCodeToUnicode::mapToUnicode(CharCode c, Unicode const **u) const
         *u = &map[c];
         return 1;
     }
-    for (i = sMapLen - 1; i >= 0; --i) { // in reverse so CMap takes precedence
-        if (sMap[i].c == c) {
-            *u = sMap[i].u;
-            return sMap[i].len;
+    for (auto i = sMap.size(); i > 0; --i) { // in reverse so CMap takes precedence
+        if (sMap[i - 1].c == c) {
+            *u = sMap[i - 1].u.data();
+            return sMap[i - 1].u.size();
         }
     }
     return 0;
@@ -704,24 +669,24 @@ int CharCodeToUnicode::mapToCharCode(const Unicode *u, CharCode *c, int usize) c
         }
         *c = 'x';
     } else {
-        int i, j;
+        size_t j;
         // for each entry in the sMap
-        for (i = 0; i < sMapLen; i++) {
+        for (const auto &element : sMap) {
             // if the entry's unicode length isn't the same are usize, the strings
             // are obviously different
-            if (sMap[i].len != usize) {
+            if (element.u.size() != size_t(usize)) {
                 continue;
             }
             // compare the string char by char
-            for (j = 0; j < sMap[i].len; j++) {
-                if (sMap[i].u[j] != u[j]) {
+            for (j = 0; j < element.u.size(); j++) {
+                if (element.u[j] != u[j]) {
                     break;
                 }
             }
 
             // we have the same strings
-            if (j == sMap[i].len) {
-                *c = sMap[i].c;
+            if (j == element.u.size()) {
+                *c = element.c;
                 return 1;
             }
         }
diff --git a/poppler/CharCodeToUnicode.h b/poppler/CharCodeToUnicode.h
index 596d44d..9aa2571 100644
--- a/poppler/CharCodeToUnicode.h
+++ b/poppler/CharCodeToUnicode.h
@@ -33,11 +33,11 @@
 
 #include <atomic>
 #include <optional>
+#include <vector>
 
 #include "poppler-config.h"
 #include "CharTypes.h"
 
-struct CharCodeToUnicodeString;
 class GooString;
 
 //------------------------------------------------------------------------
@@ -100,18 +100,22 @@ public:
     CharCode getLength() const { return mapLen; }
 
 private:
+    struct CharCodeToUnicodeString
+    {
+        CharCode c;
+        std::vector<Unicode> u;
+    };
     bool parseCMap1(int (*getCharFunc)(void *), void *data, int nBits);
     void addMapping(CharCode code, char *uStr, int n, int offset);
     void addMappingInt(CharCode code, Unicode u);
     CharCodeToUnicode();
     explicit CharCodeToUnicode(const std::optional<std::string> &tagA);
-    CharCodeToUnicode(const std::optional<std::string> &tagA, Unicode *mapA, CharCode mapLenA, bool copyMap, CharCodeToUnicodeString *sMapA, int sMapLenA, int sMapSizeA);
+    CharCodeToUnicode(const std::optional<std::string> &tagA, Unicode *mapA, CharCode mapLenA, bool copyMap, std::vector<CharCodeToUnicodeString> &&sMapA);
 
     const std::optional<std::string> tag;
     Unicode *map;
     CharCode mapLen;
-    CharCodeToUnicodeString *sMap;
-    int sMapLen, sMapSize;
+    std::vector<CharCodeToUnicodeString> sMap;
     std::atomic_int refCnt;
     bool isIdentity;
 };
diff --git a/poppler/DateInfo.cc b/poppler/DateInfo.cc
index 36e9804..d62cc78 100644
--- a/poppler/DateInfo.cc
+++ b/poppler/DateInfo.cc
@@ -36,16 +36,14 @@
 /* See PDF Reference 1.3, Section 3.8.2 for PDF Date representation */
 bool parseDateString(const GooString *date, int *year, int *month, int *day, int *hour, int *minute, int *second, char *tz, int *tzHour, int *tzMinute)
 {
-    Unicode *u;
-    int len = TextStringToUCS4(date->toStr(), &u);
+    std::vector<Unicode> u = TextStringToUCS4(date->toStr());
     GooString s;
-    for (int i = 0; i < len; i++) {
+    for (auto &c : u) {
         // Ignore any non ASCII characters
-        if (u[i] < 128) {
-            s.append(u[i]);
+        if (c < 128) {
+            s.append(c);
         }
     }
-    gfree(u);
     const char *dateString = s.c_str();
 
     if (strlen(dateString) < 2) {
diff --git a/poppler/JSInfo.cc b/poppler/JSInfo.cc
index bc6992c..6607085 100644
--- a/poppler/JSInfo.cc
+++ b/poppler/JSInfo.cc
@@ -38,20 +38,17 @@ JSInfo::~JSInfo() { }
 
 void JSInfo::printJS(const GooString *js)
 {
-    Unicode *u = nullptr;
     char buf[8];
-    int i, n, len;
 
     if (!js || !js->c_str()) {
         return;
     }
 
-    len = TextStringToUCS4(js->toStr(), &u);
-    for (i = 0; i < len; i++) {
-        n = uniMap->mapUnicode(u[i], buf, sizeof(buf));
+    std::vector<Unicode> u = TextStringToUCS4(js->toStr());
+    for (auto &c : u) {
+        int n = uniMap->mapUnicode(c, buf, sizeof(buf));
         fwrite(buf, 1, n, file);
     }
-    gfree(u);
 }
 
 void JSInfo::scanLinkAction(LinkAction *link, const char *action)
diff --git a/poppler/Outline.cc b/poppler/Outline.cc
index 4c68be9..086c104 100644
--- a/poppler/Outline.cc
+++ b/poppler/Outline.cc
@@ -407,15 +407,12 @@ OutlineItem::OutlineItem(const Dict *dict, Ref refA, OutlineItem *parentA, XRef
     parent = parentA;
     xref = xrefA;
     doc = docA;
-    title = nullptr;
     kids = nullptr;
 
     obj1 = dict->lookup("Title");
     if (obj1.isString()) {
         const GooString *s = obj1.getString();
-        titleLen = TextStringToUCS4(s->toStr(), &title);
-    } else {
-        titleLen = 0;
+        title = TextStringToUCS4(s->toStr());
     }
 
     obj1 = dict->lookup("Dest");
@@ -446,9 +443,6 @@ OutlineItem::~OutlineItem()
         delete kids;
         kids = nullptr;
     }
-    if (title) {
-        gfree(title);
-    }
 }
 
 std::vector<OutlineItem *> *OutlineItem::readItemList(OutlineItem *parent, const Object *firstItemRef, XRef *xrefA, PDFDoc *docA)
@@ -494,11 +488,9 @@ void OutlineItem::open()
 
 void OutlineItem::setTitle(const std::string &titleA)
 {
-    gfree(title);
-
     Object dict = xref->fetch(ref);
     GooString *g = new GooString(titleA);
-    titleLen = TextStringToUCS4(g->toStr(), &title);
+    title = TextStringToUCS4(g->toStr());
     dict.dictSet("Title", Object(g));
     xref->setModifiedObject(&dict, ref);
 }
diff --git a/poppler/Outline.h b/poppler/Outline.h
index a301604..af431f6 100644
--- a/poppler/Outline.h
+++ b/poppler/Outline.h
@@ -27,6 +27,7 @@
 #define OUTLINE_H
 
 #include <memory>
+#include <vector>
 #include "Object.h"
 #include "CharTypes.h"
 #include "poppler_private_export.h"
@@ -91,9 +92,8 @@ public:
     OutlineItem(const OutlineItem &) = delete;
     OutlineItem &operator=(const OutlineItem &) = delete;
     static std::vector<OutlineItem *> *readItemList(OutlineItem *parent, const Object *firstItemRef, XRef *xrefA, PDFDoc *docA);
-    const Unicode *getTitle() const { return title; }
+    const std::vector<Unicode> &getTitle() const { return title; }
     void setTitle(const std::string &titleA);
-    int getTitleLength() const { return titleLen; }
     bool setPageDest(int i);
     // OutlineItem keeps the ownership of the action
     const LinkAction *getAction() const { return action.get(); }
@@ -112,8 +112,7 @@ private:
     OutlineItem *parent;
     PDFDoc *doc;
     XRef *xref;
-    Unicode *title;
-    int titleLen;
+    std::vector<Unicode> title;
     std::unique_ptr<LinkAction> action;
     bool startsOpen;
     std::vector<OutlineItem *> *kids; // nullptr if this item is closed or has no kids
diff --git a/poppler/TextOutputDev.cc b/poppler/TextOutputDev.cc
index 439143c..5d75900 100644
--- a/poppler/TextOutputDev.cc
+++ b/poppler/TextOutputDev.cc
@@ -5661,15 +5661,11 @@ void ActualText::end(const GfxState *state)
     // extents of all the glyphs inside the span
 
     if (actualTextNBytes) {
-        Unicode *uni = nullptr;
-        int length;
-
         // now that we have the position info for all of the text inside
         // the marked content span, we feed the "ActualText" back through
         // text->addChar()
-        length = TextStringToUCS4(actualText->toStr(), &uni);
-        text->addChar(state, actualTextX0, actualTextY0, actualTextX1 - actualTextX0, actualTextY1 - actualTextY0, 0, actualTextNBytes, uni, length);
-        gfree(uni);
+        std::vector<Unicode> uni = TextStringToUCS4(actualText->toStr());
+        text->addChar(state, actualTextX0, actualTextY0, actualTextX1 - actualTextX0, actualTextY1 - actualTextY0, 0, actualTextNBytes, uni.data(), uni.size());
     }
 
     delete actualText;
diff --git a/poppler/UTF.cc b/poppler/UTF.cc
index 9b1bf95..eb542eb 100644
--- a/poppler/UTF.cc
+++ b/poppler/UTF.cc
@@ -42,65 +42,52 @@ bool UnicodeIsValid(Unicode ucs4)
     return (ucs4 < 0x110000) && ((ucs4 & 0xfffff800) != 0xd800) && (ucs4 < 0xfdd0 || ucs4 > 0xfdef) && ((ucs4 & 0xfffe) != 0xfffe);
 }
 
-int UTF16toUCS4(const Unicode *utf16, int utf16Len, Unicode **ucs4_out)
+std::vector<Unicode> UTF16toUCS4(const Unicode *utf16, int utf16Len)
 {
-    int i, n, len;
-    Unicode *u;
-
     // count characters
-    len = 0;
-    for (i = 0; i < utf16Len; i++) {
+    int len = 0;
+    for (int i = 0; i < utf16Len; i++) {
         if (utf16[i] >= 0xd800 && utf16[i] < 0xdc00 && i + 1 < utf16Len && utf16[i + 1] >= 0xdc00 && utf16[i + 1] < 0xe000) {
             i++; /* surrogate pair */
         }
         len++;
     }
-    if (ucs4_out == nullptr) {
-        return len;
-    }
-
-    u = (Unicode *)gmallocn(len, sizeof(Unicode));
-    n = 0;
+    std::vector<Unicode> u;
+    u.reserve(len);
     // convert string
-    for (i = 0; i < utf16Len; i++) {
+    for (int i = 0; i < utf16Len; i++) {
         if (utf16[i] >= 0xd800 && utf16[i] < 0xdc00) { /* surrogate pair */
             if (i + 1 < utf16Len && utf16[i + 1] >= 0xdc00 && utf16[i + 1] < 0xe000) {
                 /* next code is a low surrogate */
-                u[n] = (((utf16[i] & 0x3ff) << 10) | (utf16[i + 1] & 0x3ff)) + 0x10000;
+                u.push_back((((utf16[i] & 0x3ff) << 10) | (utf16[i + 1] & 0x3ff)) + 0x10000);
                 ++i;
             } else {
                 /* missing low surrogate
                    replace it with REPLACEMENT CHARACTER (U+FFFD) */
-                u[n] = 0xfffd;
+                u.push_back(0xfffd);
             }
         } else if (utf16[i] >= 0xdc00 && utf16[i] < 0xe000) {
             /* invalid low surrogate
                replace it with REPLACEMENT CHARACTER (U+FFFD) */
-            u[n] = 0xfffd;
+            u.push_back(0xfffd);
         } else {
-            u[n] = utf16[i];
+            u.push_back(utf16[i]);
         }
-        if (!UnicodeIsValid(u[n])) {
-            u[n] = 0xfffd;
+        if (!UnicodeIsValid(u.back())) {
+            u.back() = 0xfffd;
         }
-        n++;
     }
-    *ucs4_out = u;
-    return len;
+    return u;
 }
 
-int TextStringToUCS4(const std::string &textStr, Unicode **ucs4)
+std::vector<Unicode> TextStringToUCS4(const std::string &textStr)
 {
-    int i, len;
-    const char *s;
-    Unicode *u;
     bool isUnicode, isUnicodeLE;
 
-    len = textStr.size();
-    s = textStr.c_str();
+    int len = textStr.size();
+    const std::string &s = textStr;
     if (len == 0) {
-        *ucs4 = nullptr;
-        return 0;
+        return {};
     }
 
     if (GooString::hasUnicodeMarker(textStr)) {
@@ -115,30 +102,30 @@ int TextStringToUCS4(const std::string &textStr, Unicode **ucs4)
     }
 
     if (isUnicode || isUnicodeLE) {
-        Unicode *utf16;
         len = len / 2 - 1;
         if (len > 0) {
-            utf16 = new Unicode[len];
-            for (i = 0; i < len; i++) {
+            std::vector<Unicode> utf16;
+            utf16.reserve(len);
+            for (int i = 0; i < len; i++) {
                 if (isUnicode) {
-                    utf16[i] = (s[2 + i * 2] & 0xff) << 8 | (s[3 + i * 2] & 0xff);
+                    utf16.push_back((s[2 + i * 2] & 0xff) << 8 | (s[3 + i * 2] & 0xff));
                 } else { // UnicodeLE
-                    utf16[i] = (s[3 + i * 2] & 0xff) << 8 | (s[2 + i * 2] & 0xff);
+                    utf16.push_back((s[3 + i * 2] & 0xff) << 8 | (s[2 + i * 2] & 0xff));
                 }
             }
-            len = UTF16toUCS4(utf16, len, &u);
-            delete[] utf16;
+            return UTF16toUCS4(utf16.data(), utf16.size());
+
         } else {
-            u = nullptr;
+            return {};
         }
     } else {
-        u = (Unicode *)gmallocn(len, sizeof(Unicode));
-        for (i = 0; i < len; i++) {
-            u[i] = pdfDocEncoding[s[i] & 0xff];
+        std::vector<Unicode> u;
+        u.reserve(len);
+        for (int i = 0; i < len; i++) {
+            u.push_back(pdfDocEncoding[s[i] & 0xff]);
         }
+        return u;
     }
-    *ucs4 = u;
-    return len;
 }
 
 bool UnicodeIsWhitespace(Unicode ucs4)
@@ -549,7 +536,10 @@ void unicodeToAscii7(const Unicode *in, int len, Unicode **ucs4_out, int *out_le
         }
     }
 
-    *out_len = TextStringToUCS4(str, ucs4_out);
+    std::vector<Unicode> ucs4 = TextStringToUCS4(str);
+    *out_len = ucs4.size();
+    *ucs4_out = (Unicode *)gmallocn(ucs4.size(), sizeof(Unicode));
+    memcpy(*ucs4_out, ucs4.data(), ucs4.size() * sizeof(Unicode));
 
     if (indices) {
         idx[k] = in_idx[len];
diff --git a/poppler/UTF.h b/poppler/UTF.h
index 626c686..bfc5f65 100644
--- a/poppler/UTF.h
+++ b/poppler/UTF.h
@@ -19,6 +19,7 @@
 #include <cstdint>
 #include <climits>
 #include <memory>
+#include <vector>
 
 #include "goo/GooString.h"
 #include "CharTypes.h"
@@ -27,16 +28,14 @@
 // Convert a UTF-16 string to a UCS-4
 //   utf16      - utf16 bytes
 //   utf16_len  - number of UTF-16 characters
-//   ucs4_out   - if not NULL, allocates and returns UCS-4 string. Free with gfree.
 //   returns number of UCS-4 characters
-int UTF16toUCS4(const Unicode *utf16, int utf16Len, Unicode **ucs4_out);
+std::vector<Unicode> UTF16toUCS4(const Unicode *utf16, int utf16Len);
 
 // Convert a PDF Text String to UCS-4
 //   s          - PDF text string
-//   ucs4       - if the number of UCS-4 characters is > 0, allocates and
-//                returns UCS-4 string. Free with gfree.
-//   returns number of UCS-4 characters
-int POPPLER_PRIVATE_EXPORT TextStringToUCS4(const std::string &textStr, Unicode **ucs4);
+//   returns UCS-4 characters
+// Convert a PDF text string to UCS-4
+std::vector<Unicode> POPPLER_PRIVATE_EXPORT TextStringToUCS4(const std::string &textStr);
 
 // check if UCS-4 character is valid
 bool UnicodeIsValid(Unicode ucs4);
diff --git a/qt5/src/poppler-outline.cc b/qt5/src/poppler-outline.cc
index 5ff7e37..2f6ef2d 100644
--- a/qt5/src/poppler-outline.cc
+++ b/qt5/src/poppler-outline.cc
@@ -78,7 +78,7 @@ QString OutlineItem::name() const
 
     if (name.isEmpty()) {
         if (const ::OutlineItem *data = m_data->data) {
-            name = unicodeToQString(data->getTitle(), data->getTitleLength());
+            name = unicodeToQString(data->getTitle());
         }
     }
 
diff --git a/qt5/src/poppler-private.cc b/qt5/src/poppler-private.cc
index 228d8e8..54df79f 100644
--- a/qt5/src/poppler-private.cc
+++ b/qt5/src/poppler-private.cc
@@ -94,6 +94,11 @@ QString unicodeToQString(const Unicode *u, int len)
     return QString::fromUtf8(convertedStr.c_str(), convertedStr.getLength());
 }
 
+QString unicodeToQString(const std::vector<Unicode> &u)
+{
+    return unicodeToQString(u.data(), u.size());
+}
+
 QString UnicodeParsedString(const GooString *s1)
 {
     return (s1) ? UnicodeParsedString(s1->toStr()) : QString();
@@ -266,13 +271,7 @@ void DocumentData::addTocChildren(QDomDocument *docSyn, QDomNode *parent, const
         // iterate over every object in 'items'
 
         // 1. create element using outlineItem's title as tagName
-        QString name;
-        const Unicode *uniChar = outlineItem->getTitle();
-        int titleLength = outlineItem->getTitleLength();
-        name = unicodeToQString(uniChar, titleLength);
-        if (name.isEmpty()) {
-            continue;
-        }
+        QString name = unicodeToQString(outlineItem->getTitle());
 
         QDomElement item = docSyn->createElement(name);
         parent->appendChild(item);
diff --git a/qt5/src/poppler-private.h b/qt5/src/poppler-private.h
index 39dfb6b..bba5bd7 100644
--- a/qt5/src/poppler-private.h
+++ b/qt5/src/poppler-private.h
@@ -73,6 +73,7 @@ namespace Poppler {
 
 /* borrowed from kpdf */
 POPPLER_QT5_EXPORT QString unicodeToQString(const Unicode *u, int len);
+POPPLER_QT5_EXPORT QString unicodeToQString(const std::vector<Unicode> &u);
 
 POPPLER_QT5_EXPORT QString UnicodeParsedString(const GooString *s1);
 
diff --git a/qt5/tests/check_internal_outline.cpp b/qt5/tests/check_internal_outline.cpp
index c12b604..5db6bf4 100644
--- a/qt5/tests/check_internal_outline.cpp
+++ b/qt5/tests/check_internal_outline.cpp
@@ -56,10 +56,10 @@ void TestInternalOutline::testCreateOutline()
 
 static std::string getTitle(const OutlineItem *item)
 {
-    const Unicode *u = item->getTitle();
+    std::vector<Unicode> u = item->getTitle();
     std::string s;
-    for (int i = 0; i < item->getTitleLength(); i++) {
-        s.append(1, (char)u[i]);
+    for (auto &c : u) {
+        s.append(1, (char)(c));
     }
     return s;
 }
diff --git a/qt5/tests/check_utf_conversion.cpp b/qt5/tests/check_utf_conversion.cpp
index 73c684e..b00f080 100644
--- a/qt5/tests/check_utf_conversion.cpp
+++ b/qt5/tests/check_utf_conversion.cpp
@@ -133,16 +133,13 @@ void TestUTFConversion::testUnicodeToAscii7()
     // malloc() always returns 8-byte aligned memory addresses.
     GooString *goo = Poppler::QStringToUnicodeGooString(QString::fromUtf8("®©©©©©©©©©©©©©©©©©©©©")); // clazy:exclude=qstring-allocations
 
-    Unicode *in;
-    const int in_len = TextStringToUCS4(goo->toStr(), &in);
+    const std::vector<Unicode> in = TextStringToUCS4(goo->toStr());
 
     delete goo;
 
     int in_norm_len;
     int *in_norm_idx;
-    Unicode *in_norm = unicodeNormalizeNFKC(in, in_len, &in_norm_len, &in_norm_idx, true);
-
-    free(in);
+    Unicode *in_norm = unicodeNormalizeNFKC(in.data(), in.size(), &in_norm_len, &in_norm_idx, true);
 
     Unicode *out;
     int out_len;
@@ -174,25 +171,24 @@ void TestUTFConversion::testUnicodeLittleEndian()
     // Let's assert both GooString's are different
     QVERIFY(GooUTF16LE != GooUTF16BE);
 
-    Unicode *UCS4fromLE, *UCS4fromBE;
-    const int len1 = TextStringToUCS4(GooUTF16LE, &UCS4fromLE);
-    const int len2 = TextStringToUCS4(GooUTF16BE, &UCS4fromBE);
+    const std::vector<Unicode> UCS4fromLE = TextStringToUCS4(GooUTF16LE);
+    const std::vector<Unicode> UCS4fromBE = TextStringToUCS4(GooUTF16BE);
 
     // len is 4 because TextStringToUCS4() removes the two leading Byte Order Mark (BOM) code points
-    QCOMPARE(len1, len2);
-    QCOMPARE(len1, 4);
+    QCOMPARE(UCS4fromLE.size(), UCS4fromBE.size());
+    QCOMPARE(UCS4fromLE.size(), 4);
 
     // Check that now after conversion, UCS4fromLE and UCS4fromBE are now the same
-    for (int i = 0; i < len1; i++) {
+    for (size_t i = 0; i < UCS4fromLE.size(); i++) {
         QCOMPARE(UCS4fromLE[i], UCS4fromBE[i]);
     }
 
     const QString expected = QString::fromUtf8("HI!☑"); // clazy:exclude=qstring-allocations
 
     // Do some final verifications, checking the strings to be "HI!"
-    QVERIFY(*UCS4fromLE == *UCS4fromBE);
-    QVERIFY(compare(UCS4fromLE, expected.utf16(), len1));
-    QVERIFY(compare(UCS4fromBE, expected.utf16(), len1));
+    QVERIFY(UCS4fromLE == UCS4fromBE);
+    QVERIFY(compare(UCS4fromLE.data(), expected.utf16(), UCS4fromLE.size()));
+    QVERIFY(compare(UCS4fromBE.data(), expected.utf16(), UCS4fromLE.size()));
 }
 
 QTEST_GUILESS_MAIN(TestUTFConversion)
diff --git a/qt6/src/poppler-outline.cc b/qt6/src/poppler-outline.cc
index f5ba2a9..2bc0d30 100644
--- a/qt6/src/poppler-outline.cc
+++ b/qt6/src/poppler-outline.cc
@@ -78,7 +78,7 @@ QString OutlineItem::name() const
 
     if (name.isEmpty()) {
         if (const ::OutlineItem *data = m_data->data) {
-            name = unicodeToQString(data->getTitle(), data->getTitleLength());
+            name = unicodeToQString(data->getTitle());
         }
     }
 
diff --git a/qt6/src/poppler-private.cc b/qt6/src/poppler-private.cc
index 91d1725..2cb2396 100644
--- a/qt6/src/poppler-private.cc
+++ b/qt6/src/poppler-private.cc
@@ -94,6 +94,11 @@ QString unicodeToQString(const Unicode *u, int len)
     return QString::fromUtf8(convertedStr.c_str(), convertedStr.getLength());
 }
 
+QString unicodeToQString(const std::vector<Unicode> &u)
+{
+    return unicodeToQString(u.data(), u.size());
+}
+
 QString UnicodeParsedString(const GooString *s1)
 {
     return (s1) ? UnicodeParsedString(s1->toStr()) : QString();
diff --git a/qt6/src/poppler-private.h b/qt6/src/poppler-private.h
index d1f7335..a3117a6 100644
--- a/qt6/src/poppler-private.h
+++ b/qt6/src/poppler-private.h
@@ -72,6 +72,7 @@ namespace Poppler {
 
 /* borrowed from kpdf */
 POPPLER_QT6_EXPORT QString unicodeToQString(const Unicode *u, int len);
+POPPLER_QT6_EXPORT QString unicodeToQString(const std::vector<Unicode> &u);
 
 POPPLER_QT6_EXPORT QString UnicodeParsedString(const GooString *s1);
 
diff --git a/qt6/tests/check_internal_outline.cpp b/qt6/tests/check_internal_outline.cpp
index c12b604..d23e773 100644
--- a/qt6/tests/check_internal_outline.cpp
+++ b/qt6/tests/check_internal_outline.cpp
@@ -56,10 +56,10 @@ void TestInternalOutline::testCreateOutline()
 
 static std::string getTitle(const OutlineItem *item)
 {
-    const Unicode *u = item->getTitle();
+    const std::vector<Unicode> &u = item->getTitle();
     std::string s;
-    for (int i = 0; i < item->getTitleLength(); i++) {
-        s.append(1, (char)u[i]);
+    for (const auto &c : u) {
+        s.append(1, (char)(c));
     }
     return s;
 }
diff --git a/qt6/tests/check_utf_conversion.cpp b/qt6/tests/check_utf_conversion.cpp
index 2cac758..e7f35ea 100644
--- a/qt6/tests/check_utf_conversion.cpp
+++ b/qt6/tests/check_utf_conversion.cpp
@@ -131,16 +131,13 @@ void TestUTFConversion::testUnicodeToAscii7()
     // malloc() always returns 8-byte aligned memory addresses.
     GooString *goo = Poppler::QStringToUnicodeGooString(QString::fromUtf8("®©©©©©©©©©©©©©©©©©©©©")); // clazy:exclude=qstring-allocations
 
-    Unicode *in;
-    const int in_len = TextStringToUCS4(goo->toStr(), &in);
+    const std::vector<Unicode> in = TextStringToUCS4(goo->toStr());
 
     delete goo;
 
     int in_norm_len;
     int *in_norm_idx;
-    Unicode *in_norm = unicodeNormalizeNFKC(in, in_len, &in_norm_len, &in_norm_idx, true);
-
-    free(in);
+    Unicode *in_norm = unicodeNormalizeNFKC(in.data(), in.size(), &in_norm_len, &in_norm_idx, true);
 
     Unicode *out;
     int out_len;
@@ -172,25 +169,24 @@ void TestUTFConversion::testUnicodeLittleEndian()
     // Let's assert both GooString's are different
     QVERIFY(GooUTF16LE != GooUTF16BE);
 
-    Unicode *UCS4fromLE, *UCS4fromBE;
-    const int len1 = TextStringToUCS4(GooUTF16LE, &UCS4fromLE);
-    const int len2 = TextStringToUCS4(GooUTF16BE, &UCS4fromBE);
+    const std::vector<Unicode> UCS4fromLE = TextStringToUCS4(GooUTF16LE);
+    const std::vector<Unicode> UCS4fromBE = TextStringToUCS4(GooUTF16BE);
 
     // len is 4 because TextStringToUCS4() removes the two leading Byte Order Mark (BOM) code points
-    QCOMPARE(len1, len2);
-    QCOMPARE(len1, 4);
+    QCOMPARE(UCS4fromLE.size(), UCS4fromBE.size());
+    QCOMPARE(UCS4fromLE.size(), 4);
 
     // Check that now after conversion, UCS4fromLE and UCS4fromBE are now the same
-    for (int i = 0; i < len1; i++) {
+    for (size_t i = 0; i < UCS4fromLE.size(); i++) {
         QCOMPARE(UCS4fromLE[i], UCS4fromBE[i]);
     }
 
     const QString expected = QStringLiteral("HI!☑");
 
     // Do some final verifications, checking the strings to be "HI!"
-    QVERIFY(*UCS4fromLE == *UCS4fromBE);
-    QVERIFY(compare(UCS4fromLE, expected.utf16(), len1));
-    QVERIFY(compare(UCS4fromBE, expected.utf16(), len1));
+    QVERIFY(UCS4fromLE == UCS4fromBE);
+    QVERIFY(compare(UCS4fromLE.data(), expected.utf16(), UCS4fromLE.size()));
+    QVERIFY(compare(UCS4fromBE.data(), expected.utf16(), UCS4fromBE.size()));
 }
 
 QTEST_GUILESS_MAIN(TestUTFConversion)
diff --git a/utils/HtmlFonts.cc b/utils/HtmlFonts.cc
index ca7d4a4..9f25621 100644
--- a/utils/HtmlFonts.cc
+++ b/utils/HtmlFonts.cc
@@ -230,9 +230,9 @@ GooString *HtmlFont::getFullName()
 }
 
 // this method if plain wrong todo
-GooString *HtmlFont::HtmlFilter(const Unicode *u, int uLen)
+std::unique_ptr<GooString> HtmlFont::HtmlFilter(const Unicode *u, int uLen)
 {
-    GooString *tmp = new GooString();
+    auto tmp = std::make_unique<GooString>();
     const UnicodeMap *uMap;
     char buf[8];
     int n;
diff --git a/utils/HtmlFonts.h b/utils/HtmlFonts.h
index ca4ae54..74cdca0 100644
--- a/utils/HtmlFonts.h
+++ b/utils/HtmlFonts.h
@@ -104,7 +104,7 @@ public:
     }
     const double *getRotMat() const { return rotSkewMat; }
     GooString *getFontName();
-    static GooString *HtmlFilter(const Unicode *u, int uLen); // char* s);
+    static std::unique_ptr<GooString> HtmlFilter(const Unicode *u, int uLen); // char* s);
     bool isEqual(const HtmlFont &x) const;
     bool isEqualIgnoreBold(const HtmlFont &x) const;
     void print() const { printf("font: %s (%s) %d %s%s\n", FontName->c_str(), familyName.c_str(), size, bold ? "bold " : "", italic ? "italic " : ""); };
diff --git a/utils/HtmlOutputDev.cc b/utils/HtmlOutputDev.cc
index 8a16e00..71fe672 100644
--- a/utils/HtmlOutputDev.cc
+++ b/utils/HtmlOutputDev.cc
@@ -127,11 +127,11 @@ static bool debug = false;
 
 #if 0
 static GooString* Dirname(GooString* str){
-  
+
   char *p=str->c_str();
   int len=str->getLength();
   for (int i=len-1;i>=0;i--)
-    if (*(p+i)==SLASH) 
+    if (*(p+i)==SLASH)
       return new GooString(p,i+1);
   return new GooString();
 }
@@ -222,14 +222,13 @@ HtmlString::HtmlString(GfxState *state, double fontSize, HtmlFontAccu *_fonts) :
     len = size = 0;
     yxNext = nullptr;
     xyNext = nullptr;
-    htext = new GooString();
+    htext = std::make_unique<GooString>();
     dir = textDirUnknown;
 }
 
 HtmlString::~HtmlString()
 {
     gfree(text);
-    delete htext;
     gfree(xRight);
 }
 
@@ -340,7 +339,6 @@ void HtmlPage::beginString(GfxState *state, const GooString *s)
 void HtmlPage::conv()
 {
     for (HtmlString *tmp = yxStrings; tmp; tmp = tmp->yxNext) {
-        delete tmp->htext;
         tmp->htext = HtmlFont::HtmlFilter(tmp->text, tmp->len);
 
         int linkIndex = 0;
@@ -636,7 +634,7 @@ void HtmlPage::coalesce()
             bool finish_a = switch_links && hlink1 != nullptr;
             bool finish_italic = hfont1->isItalic() && (!hfont2->isItalic() || finish_a);
             bool finish_bold = hfont1->isBold() && (!hfont2->isBold() || finish_a || finish_italic);
-            CloseTags(str1->htext, finish_a, finish_italic, finish_bold);
+            CloseTags(str1->htext.get(), finish_a, finish_italic, finish_bold);
             if (switch_links && hlink2 != nullptr) {
                 GooString *ls = hlink2->getLinkStart();
                 str1->htext->append(ls);
@@ -649,7 +647,7 @@ void HtmlPage::coalesce()
                 str1->htext->append("<b>", 3);
             }
 
-            str1->htext->append(str2->htext);
+            str1->htext->append(str2->htext.get());
             // str1 now contains href for link of str2 (if it is defined)
             str1->link = str2->link;
             hfont1 = hfont2;
@@ -666,7 +664,7 @@ void HtmlPage::coalesce()
             bool finish_a = str1->getLink() != nullptr;
             bool finish_bold = hfont1->isBold();
             bool finish_italic = hfont1->isItalic();
-            CloseTags(str1->htext, finish_a, finish_italic, finish_bold);
+            CloseTags(str1->htext.get(), finish_a, finish_italic, finish_bold);
 
             str1->xMin = curX;
             str1->yMin = curY;
@@ -693,14 +691,14 @@ void HtmlPage::coalesce()
     bool finish_bold = hfont1->isBold();
     bool finish_italic = hfont1->isItalic();
     bool finish_a = str1->getLink() != nullptr;
-    CloseTags(str1->htext, finish_a, finish_italic, finish_bold);
+    CloseTags(str1->htext.get(), finish_a, finish_italic, finish_bold);
 
 #if 0 //~ for debugging
   for (str1 = yxStrings; str1; str1 = str1->yxNext) {
     printf("x=%3d..%3d  y=%3d..%3d  size=%2d ",
 	   (int)str1->xMin, (int)str1->xMax, (int)str1->yMin, (int)str1->yMax,
 	   (int)(str1->yMax - str1->yMin));
-    printf("'%s'\n", str1->htext->c_str());  
+    printf("'%s'\n", str1->htext->c_str());
   }
   printf("\n------------------------------------------------------------\n\n");
 #endif
@@ -1220,10 +1218,10 @@ void HtmlOutputDev::startPage(int pageNumA, GfxState *state, XRef *xref)
 	exit(1);
       }
       delete fname;
-    // if(state->getRotation()!=0) 
+    // if(state->getRotation()!=0)
     //  fprintf(tin,"ROTATE=%d rotate %d neg %d neg translate\n",state->getRotation(),state->getX1(),-state->getY1());
-    // else 
-      fprintf(tin,"ROTATE=%d neg %d neg translate\n",state->getX1(),state->getY1());  
+    // else
+      fprintf(tin,"ROTATE=%d neg %d neg translate\n",state->getX1(),state->getY1());
     }
   }
 #endif
@@ -1718,10 +1716,11 @@ bool HtmlOutputDev::newHtmlOutlineLevel(FILE *output, const std::vector<OutlineI
     fputs("<ul>\n", output);
 
     for (OutlineItem *item : *outlines) {
-        GooString *titleStr = HtmlFont::HtmlFilter(item->getTitle(), item->getTitleLength());
+        const auto &title = item->getTitle();
+        std::unique_ptr<GooString> titleStr = HtmlFont::HtmlFilter(title.data(), title.size());
 
         GooString *linkName = nullptr;
-        ;
+
         const int itemPage = getOutlinePageNum(item);
         if (itemPage > 0) {
             /*		complex		simple
@@ -1748,12 +1747,13 @@ bool HtmlOutputDev::newHtmlOutlineLevel(FILE *output, const std::vector<OutlineI
         if (linkName) {
             fprintf(output, "<a href=\"%s\">", linkName->c_str());
         }
-        fputs(titleStr->c_str(), output);
+        if (titleStr) {
+            fputs(titleStr->c_str(), output);
+        }
         if (linkName) {
             fputs("</a>", output);
             delete linkName;
         }
-        delete titleStr;
         atLeastOne = true;
 
         item->open();
@@ -1773,14 +1773,14 @@ void HtmlOutputDev::newXmlOutlineLevel(FILE *output, const std::vector<OutlineIt
     fputs("<outline>\n", output);
 
     for (OutlineItem *item : *outlines) {
-        GooString *titleStr = HtmlFont::HtmlFilter(item->getTitle(), item->getTitleLength());
+        const std::vector<Unicode> &title = item->getTitle();
+        auto titleStr = HtmlFont::HtmlFilter(title.data(), title.size());
         const int itemPage = getOutlinePageNum(item);
         if (itemPage > 0) {
             fprintf(output, "<item page=\"%d\">%s</item>\n", itemPage, titleStr->c_str());
         } else {
             fprintf(output, "<item>%s</item>\n", titleStr->c_str());
         }
-        delete titleStr;
 
         item->open();
         if (item->hasKids() && item->getKids()) {
diff --git a/utils/HtmlOutputDev.h b/utils/HtmlOutputDev.h
index c7b08d1..e490eff 100644
--- a/utils/HtmlOutputDev.h
+++ b/utils/HtmlOutputDev.h
@@ -95,7 +95,7 @@ private:
     HtmlString *yxNext; // next string in y-major order
     HtmlString *xyNext; // next string in x-major order
     int fontpos;
-    GooString *htext;
+    std::unique_ptr<GooString> htext;
     int len; // length of text and xRight
     int size; // size of text and xRight arrays
     UnicodeTextDirection dir; // direction (left to right/right to left)
diff --git a/utils/pdfinfo.cc b/utils/pdfinfo.cc
index 7a7fc78..a446c3c 100644
--- a/utils/pdfinfo.cc
+++ b/utils/pdfinfo.cc
@@ -114,14 +114,12 @@ static const ArgDesc argDesc[] = { { "-f", argInt, &firstPage, 0, "first page to
 
 static void printTextString(const GooString *s, const UnicodeMap *uMap)
 {
-    Unicode *u;
     char buf[8];
-    int len = TextStringToUCS4(s->toStr(), &u);
-    for (int i = 0; i < len; i++) {
-        int n = uMap->mapUnicode(u[i], buf, sizeof(buf));
+    std::vector<Unicode> u = TextStringToUCS4(s->toStr());
+    for (const auto &c : u) {
+        int n = uMap->mapUnicode(c, buf, sizeof(buf));
         fwrite(buf, 1, n, stdout);
     }
-    gfree(u);
 }
 
 static void printUCS4String(const Unicode *u, int len, const UnicodeMap *uMap)
diff --git a/utils/pdftohtml.cc b/utils/pdftohtml.cc
index 97b141a..d7c0889 100644
--- a/utils/pdftohtml.cc
+++ b/utils/pdftohtml.cc
@@ -99,7 +99,7 @@ static char ownerPassword[33] = "";
 static char userPassword[33] = "";
 static bool printVersion = false;
 
-static GooString *getInfoString(Dict *infoDict, const char *key);
+static std::unique_ptr<GooString> getInfoString(Dict *infoDict, const char *key);
 static GooString *getInfoDate(Dict *infoDict, const char *key);
 
 static char textEncName[128] = "";
@@ -158,8 +158,11 @@ int main(int argc, char *argv[])
 {
     std::unique_ptr<PDFDoc> doc;
     GooString *fileName = nullptr;
-    GooString *docTitle = nullptr;
-    GooString *author = nullptr, *keywords = nullptr, *subject = nullptr, *date = nullptr;
+    std::unique_ptr<GooString> docTitle;
+    std::unique_ptr<GooString> author;
+    std::unique_ptr<GooString> keywords;
+    std::unique_ptr<GooString> subject;
+    GooString *date = nullptr;
     GooString *htmlFileName = nullptr;
     HtmlOutputDev *htmlOut = nullptr;
     SplashOutputDev *splashOut = nullptr;
@@ -317,7 +320,7 @@ int main(int argc, char *argv[])
         }
     }
     if (!docTitle) {
-        docTitle = new GooString(htmlFileName);
+        docTitle = std::make_unique<GooString>(htmlFileName);
     }
 
     if (!singleHtml) {
@@ -330,16 +333,6 @@ int main(int argc, char *argv[])
     // write text file
     htmlOut = new HtmlOutputDev(doc->getCatalog(), htmlFileName->c_str(), docTitle->c_str(), author ? author->c_str() : nullptr, keywords ? keywords->c_str() : nullptr, subject ? subject->c_str() : nullptr, date ? date->c_str() : nullptr,
                                 rawOrder, firstPage, doOutline);
-    delete docTitle;
-    if (author) {
-        delete author;
-    }
-    if (keywords) {
-        delete keywords;
-    }
-    if (subject) {
-        delete subject;
-    }
     if (date) {
         delete date;
     }
@@ -397,7 +390,7 @@ error:
     return exit_status;
 }
 
-static GooString *getInfoString(Dict *infoDict, const char *key)
+static std::unique_ptr<GooString> getInfoString(Dict *infoDict, const char *key)
 {
     Object obj;
     // Raw value as read from PDF (may be in pdfDocEncoding or UCS2)
@@ -406,7 +399,7 @@ static GooString *getInfoString(Dict *infoDict, const char *key)
     Unicode *unicodeString;
     int unicodeLength;
     // Value HTML escaped and converted to desired encoding
-    GooString *encodedString = nullptr;
+    std::unique_ptr<GooString> encodedString;
     // Is rawString UCS2 (as opposed to pdfDocEncoding)
     bool isUnicode;
 
-- 
2.40.0

