Add to search ebooks with keywords
[platform/core/multimedia/libmedia-service.git] / src / common / media-svc-util-pdf.cpp
1 /*
2  * libmedia-service
3  *
4  * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  */
19 #include <podofo/podofo.h>
20 #include <stack>
21 #include <media-svc-util-pdf.h>
22 #include <media-svc-debug.h>
23 #include <glib.h>
24
25 using namespace std;
26 using namespace PoDoFo;
27
28 static bool __media_svc_pdf_find_keyword(const char *full, const char *keyword)
29 {
30         media_svc_retv_if(!full, false);
31         media_svc_retv_if(!keyword, false);
32
33         if (g_regex_match_simple(keyword, full, G_REGEX_CASELESS, (GRegexMatchFlags)0)) {
34                 media_svc_debug("Found");
35                 return true;
36         }
37
38         return false;
39  }
40
41 static char * __media_svc_pdf_parse_text(PdfMemDocument *pdf, PdfPage *page, const char *keyword)
42 {
43         EPdfContentsType type;
44         PdfVariant var;
45         PdfFont *cur_font = NULL;
46         bool text_block = false;
47         const char *tok;
48         stack<PdfVariant> stack;
49         PdfString unicode;
50         PdfArray array;
51
52         GString *full_text = NULL;
53         gchar *tmp_text = NULL;
54
55         media_svc_retv_if(!pdf, NULL);
56         media_svc_retv_if(!page, NULL);
57         media_svc_retv_if(!keyword, NULL);
58
59         PdfContentsTokenizer tokenizer(page);
60
61         full_text = g_string_new(NULL);
62
63         while (tokenizer.ReadNext(type, tok, var)) {
64                 if (type == ePdfContentsType_Keyword) {
65                         if (!text_block && strcmp(tok, "BT") == 0) {
66                                 text_block = true;
67                                 continue;
68                         } else if (text_block && strcmp(tok, "ET") == 0) {
69                                 text_block = false;
70                         }
71
72                         if (!text_block)
73                                 continue;
74
75                         if (strcmp(tok, "Tf") == 0) {
76                                 if (stack.size() < 2) {
77                                         cur_font = NULL;
78                                         continue;
79                                 }
80
81                                 stack.pop();
82                                 cur_font = pdf->GetFont(page->GetFromResources(PdfName("Font"), stack.top().GetName()));
83                         } else if (strcmp(tok, "Tj") == 0 || strcmp(tok, "'") == 0 || strcmp(tok, "\"") == 0) {
84                                 if (stack.empty())
85                                         continue;
86
87                                 if (!cur_font || !cur_font->GetEncoding())
88                                         continue;
89
90                                 unicode = cur_font->GetEncoding()->ConvertToUnicode(stack.top().GetString(), cur_font);
91                                 full_text = g_string_append(full_text, unicode.GetStringUtf8().c_str());
92
93                                 stack.pop();
94                         } else if (strcmp(tok, "TJ") == 0) {
95                                 if (stack.empty())
96                                         continue;
97
98                                 array = stack.top().GetArray();
99                                 stack.pop();
100
101                                 for (int i = 0; i < static_cast<int>(array.GetSize()); i++) {
102                                         if (array[i].IsString() || array[i].IsHexString()) {
103                                                 if (!cur_font || !cur_font->GetEncoding())
104                                                         continue;
105
106                                                 unicode = cur_font->GetEncoding()->ConvertToUnicode(array[i].GetString(), cur_font);
107                                                 full_text = g_string_append(full_text, unicode.GetStringUtf8().c_str());
108                                         }
109                                 }
110                         }
111                 } else {
112                         if (text_block)
113                                 stack.push(var);
114                 }
115         }
116
117         while (!stack.empty())
118                 stack.pop();
119
120         tmp_text = g_string_free(full_text, FALSE);
121
122         /* GString start with an empty string. */
123         if (strlen(tmp_text) == 0) {
124                 g_free(tmp_text);
125                 return NULL;
126         } else {
127                 return tmp_text;
128         }
129 }
130
131 bool _media_svc_pdf_is_keyword_included(const char *path, const char *keyword)
132 {
133         bool res = false;
134         gchar *full_text = NULL;
135
136         media_svc_retvm_if(!path, false, "Invalid path");
137         media_svc_retvm_if(!keyword, false, "Invalid keyword");
138
139         try {
140                 PdfMemDocument pdf(path);
141
142                 // PDF format starts from 1..
143                 // GetPageCount() is a value, not a calculation.. So, it does not affect the performance of this forloop.
144                 for (int n = 0; n < pdf.GetPageCount(); ++n) {
145                         PdfPage *page = pdf.GetPage(n);
146
147                         full_text = __media_svc_pdf_parse_text(&pdf, page, keyword);
148
149                         if (full_text) {
150                                 res = __media_svc_pdf_find_keyword(full_text, keyword);
151                                 g_free(full_text);
152
153                                 if (res)
154                                         return res;
155                         }
156                 }
157         } catch (const PdfError& e) {
158                 media_svc_error("Initialization failed : %s", e.what());
159         }
160
161         return false;
162 }