1 | /* poppler-private.cc: qt interface to poppler |
2 | * Copyright (C) 2005, Net Integration Technologies, Inc. |
3 | * Copyright (C) 2006, 2011, 2015, 2017-2020 by Albert Astals Cid <aacid@kde.org> |
4 | * Copyright (C) 2008, 2010, 2011, 2014 by Pino Toscano <pino@kde.org> |
5 | * Copyright (C) 2013 by Thomas Freitag <Thomas.Freitag@alfa.de> |
6 | * Copyright (C) 2013 Adrian Johnson <ajohnson@redneon.com> |
7 | * Copyright (C) 2016 Jakub Alba <jakubalba@gmail.com> |
8 | * Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>. Work sponsored by the LiMux project of the city of Munich |
9 | * Copyright (C) 2018-2020 Adam Reichold <adam.reichold@t-online.de> |
10 | * Copyright (C) 2019, 2020, 2024 Oliver Sander <oliver.sander@tu-dresden.de> |
11 | * Copyright (C) 2019 João Netto <joaonetto901@gmail.com> |
12 | * Copyright (C) 2021 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com> |
13 | * Copyright (C) 2021 Mahmoud Khalil <mahmoudkhalil11@gmail.com> |
14 | * Copyright (C) 2023 Shivodit Gill <shivodit.gill@gmail.com> |
15 | * Copyright (C) 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela <sune@vuorela.dk> |
16 | * Inspired on code by |
17 | * Copyright (C) 2004 by Albert Astals Cid <tsdgeos@terra.es> |
18 | * Copyright (C) 2004 by Enrico Ros <eros.kde@email.it> |
19 | * |
20 | * This program is free software; you can redistribute it and/or modify |
21 | * it under the terms of the GNU General Public License as published by |
22 | * the Free Software Foundation; either version 2, or (at your option) |
23 | * any later version. |
24 | * |
25 | * This program is distributed in the hope that it will be useful, |
26 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
27 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
28 | * GNU General Public License for more details. |
29 | * |
30 | * You should have received a copy of the GNU General Public License |
31 | * along with this program; if not, write to the Free Software |
32 | * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. |
33 | */ |
34 | |
35 | #include "poppler-private.h" |
36 | #include "poppler-form.h" |
37 | |
38 | #include <QtCore/QByteArray> |
39 | #include <QtCore/QDebug> |
40 | #include <QtCore/QVariant> |
41 | |
42 | #include <Link.h> |
43 | #include <Outline.h> |
44 | #include <PDFDocEncoding.h> |
45 | #include <UnicodeMap.h> |
46 | #include <UTF.h> |
47 | |
48 | #ifdef ANDROID |
49 | # include <QtCore/QString> |
50 | # include <QtCore/QDir> |
51 | # include <QtCore/QFile> |
52 | # include <QtCore/QFileInfo> |
53 | # include <QtCore/QStandardPaths> |
54 | # include <QtCore/QDirIterator> |
55 | #endif |
56 | |
57 | namespace Poppler { |
58 | |
59 | namespace Debug { |
60 | |
61 | static void qDebugDebugFunction(const QString &message, const QVariant & /*closure*/) |
62 | { |
63 | qDebug() << message; |
64 | } |
65 | |
66 | PopplerDebugFunc debugFunction = qDebugDebugFunction; |
67 | QVariant debugClosure; |
68 | |
69 | } |
70 | |
71 | void setDebugErrorFunction(PopplerDebugFunc function, const QVariant &closure) |
72 | { |
73 | Debug::debugFunction = function ? function : Debug::qDebugDebugFunction; |
74 | Debug::debugClosure = closure; |
75 | } |
76 | |
77 | void qt6ErrorFunction(ErrorCategory /*category*/, Goffset pos, const char *msg) |
78 | { |
79 | QString emsg; |
80 | |
81 | if (pos >= 0) { |
82 | emsg = QStringLiteral("Error (%1): " ).arg(a: pos); |
83 | } else { |
84 | emsg = QStringLiteral("Error: " ); |
85 | } |
86 | emsg += QString::fromLatin1(ba: msg); |
87 | (*Debug::debugFunction)(emsg, Debug::debugClosure); |
88 | } |
89 | |
90 | QString unicodeToQString(const Unicode *u, int len) |
91 | { |
92 | const UnicodeMap *utf8Map = globalParams->getUtf8Map(); |
93 | |
94 | // ignore the last characters if they are 0x0 |
95 | while ((len > 0) && (u[len - 1] == 0)) { |
96 | --len; |
97 | } |
98 | |
99 | GooString convertedStr; |
100 | for (int i = 0; i < len; ++i) { |
101 | char buf[8]; |
102 | const int n = utf8Map->mapUnicode(u: u[i], buf, bufSize: sizeof(buf)); |
103 | convertedStr.append(str: buf, lengthA: n); |
104 | } |
105 | |
106 | return QString::fromUtf8(utf8: convertedStr.c_str(), size: convertedStr.getLength()); |
107 | } |
108 | |
109 | QString unicodeToQString(const std::vector<Unicode> &u) |
110 | { |
111 | return unicodeToQString(u: u.data(), len: u.size()); |
112 | } |
113 | |
114 | QString UnicodeParsedString(const GooString *s1) |
115 | { |
116 | return (s1) ? UnicodeParsedString(s1: s1->toStr()) : QString(); |
117 | } |
118 | |
119 | QString UnicodeParsedString(const std::string &s1) |
120 | { |
121 | if (s1.empty()) { |
122 | return QString(); |
123 | } |
124 | |
125 | if (hasUnicodeByteOrderMark(s: s1) || hasUnicodeByteOrderMarkLE(s: s1)) { |
126 | return QString::fromUtf16(reinterpret_cast<const char16_t *>(s1.c_str()), size: s1.size() / 2); |
127 | } else { |
128 | int stringLength; |
129 | const char *cString = pdfDocEncodingToUTF16(orig: s1, length: &stringLength); |
130 | auto result = QString::fromUtf16(reinterpret_cast<const char16_t *>(cString), size: stringLength / 2); |
131 | delete[] cString; |
132 | return result; |
133 | } |
134 | } |
135 | |
136 | GooString *QStringToUnicodeGooString(const QString &s) |
137 | { |
138 | if (s.isEmpty()) { |
139 | return new GooString(); |
140 | } |
141 | int len = s.length() * 2 + 2; |
142 | char *cstring = (char *)gmallocn(count: len, size: sizeof(char)); |
143 | cstring[0] = (char)0xfe; |
144 | cstring[1] = (char)0xff; |
145 | for (int i = 0; i < s.length(); ++i) { |
146 | cstring[2 + i * 2] = s.at(i).row(); |
147 | cstring[3 + i * 2] = s.at(i).cell(); |
148 | } |
149 | GooString *ret = new GooString(cstring, len); |
150 | gfree(p: cstring); |
151 | return ret; |
152 | } |
153 | |
154 | GooString *QStringToGooString(const QString &s) |
155 | { |
156 | int len = s.length(); |
157 | char *cstring = (char *)gmallocn(count: s.length(), size: sizeof(char)); |
158 | for (int i = 0; i < len; ++i) { |
159 | cstring[i] = s.at(i).unicode(); |
160 | } |
161 | GooString *ret = new GooString(cstring, len); |
162 | gfree(p: cstring); |
163 | return ret; |
164 | } |
165 | |
166 | GooString *QDateTimeToUnicodeGooString(const QDateTime &dt) |
167 | { |
168 | if (!dt.isValid()) { |
169 | return nullptr; |
170 | } |
171 | |
172 | return QStringToUnicodeGooString(s: dt.toUTC().toString(QStringLiteral("yyyyMMddhhmmss+00'00'" ))); |
173 | } |
174 | |
175 | Annot::AdditionalActionsType toPopplerAdditionalActionType(Annotation::AdditionalActionType type) |
176 | { |
177 | switch (type) { |
178 | case Annotation::CursorEnteringAction: |
179 | return Annot::actionCursorEntering; |
180 | case Annotation::CursorLeavingAction: |
181 | return Annot::actionCursorLeaving; |
182 | case Annotation::MousePressedAction: |
183 | return Annot::actionMousePressed; |
184 | case Annotation::MouseReleasedAction: |
185 | return Annot::actionMouseReleased; |
186 | case Annotation::FocusInAction: |
187 | return Annot::actionFocusIn; |
188 | case Annotation::FocusOutAction: |
189 | return Annot::actionFocusOut; |
190 | case Annotation::PageOpeningAction: |
191 | return Annot::actionPageOpening; |
192 | case Annotation::PageClosingAction: |
193 | return Annot::actionPageClosing; |
194 | case Annotation::PageVisibleAction: |
195 | return Annot::actionPageVisible; |
196 | case Annotation::PageInvisibleAction: |
197 | return Annot::actionPageInvisible; |
198 | } |
199 | |
200 | return Annot::actionCursorEntering; |
201 | } |
202 | |
203 | DocumentData::~DocumentData() |
204 | { |
205 | qDeleteAll(c: m_embeddedFiles); |
206 | delete (OptContentModel *)m_optContentModel; |
207 | delete doc; |
208 | } |
209 | |
210 | void DocumentData::init() |
211 | { |
212 | m_backend = Document::SplashBackend; |
213 | paperColor = Qt::white; |
214 | m_hints = 0; |
215 | m_optContentModel = nullptr; |
216 | xrefReconstructed = false; |
217 | xrefReconstructedCallback = {}; |
218 | |
219 | #ifdef ANDROID |
220 | // Copy fonts from android apk to the app's storage dir, and |
221 | // set the font directory path |
222 | QString assetsFontDir = QStringLiteral("assets:/share/fonts" ); |
223 | QString fontsdir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/fonts" ); |
224 | QDir fontPath = QDir(fontsdir); |
225 | |
226 | if (fontPath.mkpath(fontPath.absolutePath())) { |
227 | GlobalParams::setFontDir(fontPath.absolutePath().toStdString()); |
228 | QDirIterator iterator(assetsFontDir, QDir::NoFilter, QDirIterator::Subdirectories); |
229 | |
230 | while (iterator.hasNext()) { |
231 | iterator.next(); |
232 | QFileInfo fontFileInfo = iterator.fileInfo(); |
233 | QString fontFilePath = assetsFontDir + QStringLiteral("/" ) + fontFileInfo.fileName(); |
234 | QString destPath = fontPath.absolutePath() + QStringLiteral("/" ) + fontFileInfo.fileName(); |
235 | QFile::copy(fontFilePath, destPath); |
236 | } |
237 | } else { |
238 | GlobalParams::setFontDir("" ); |
239 | } |
240 | #endif |
241 | } |
242 | |
243 | void DocumentData::noitfyXRefReconstructed() |
244 | { |
245 | if (!xrefReconstructed) { |
246 | xrefReconstructed = true; |
247 | } |
248 | |
249 | if (xrefReconstructedCallback) { |
250 | xrefReconstructedCallback(); |
251 | } |
252 | } |
253 | |
254 | FormWidget *FormFieldData::getFormWidget(const FormField *f) |
255 | { |
256 | return f->m_formData->fm; |
257 | } |
258 | |
259 | FormFieldIconData *FormFieldIconData::getData(const FormFieldIcon &f) |
260 | { |
261 | return f.d_ptr; |
262 | } |
263 | |
264 | } |
265 | |