프로젝트

일반

사용자정보

통계
| 개정판:

hytos / DTI_PID / DTI_PID / ImportTextFromPDFDialog.py @ 52437c65

이력 | 보기 | 이력해설 | 다운로드 (15.2 KB)

1
# coding: utf-8
2
"""This is text importing module from AutoCAD"""
3
import os
4
import sys
5
from PyQt5.QtCore import *
6
from PyQt5.QtGui import *
7
from PyQt5.QtWidgets import *
8
from AppDocData import AppDocData
9
import subprocess
10
from xml.etree.ElementTree import Element, SubElement, dump, ElementTree, parse
11

    
12
import ImportTextFromPDF_UI
13

    
14

    
15
class QImportTextFromPDFDialog(QDialog):
16
    def __init__(self, parent):
17
        QDialog.__init__(self, parent)
18

    
19
        self.ui = ImportTextFromPDF_UI.Ui_ImportTextFromPDFDialog()
20
        self.ui.setupUi(self)
21

    
22
        self._dwgs = []
23

    
24
        app_doc_data = AppDocData.instance()
25

    
26
        configs = app_doc_data.getConfigs('PDF Size', 'Width')
27
        self.ui.spinBoxWidth.setValue(int(configs[0].value)) if 1 == len(configs) else \
28
                self.ui.spinBoxWidth.setValue(0)
29
        configs = app_doc_data.getConfigs('PDF Size', 'Height')
30
        self.ui.spinBoxHeight.setValue(int(configs[0].value)) if 1 == len(configs) else \
31
                self.ui.spinBoxHeight.setValue(0)
32
        configs = app_doc_data.getConfigs('PDF Offset', 'X')
33
        self.ui.spinBoxTextX.setValue(int(configs[0].value)) if 1 == len(configs) else \
34
                self.ui.spinBoxTextX.setValue(0)
35
        configs = app_doc_data.getConfigs('PDF Offset', 'Y')
36
        self.ui.spinBoxTextY.setValue(int(configs[0].value)) if 1 == len(configs) else \
37
                self.ui.spinBoxTextY.setValue(0)        
38
        configs = app_doc_data.getConfigs('PDF Offset', 'Scale')
39
        self.ui.doubleSpinBoxScale.setValue(float(configs[0].value)) if 1 == len(configs) else \
40
                self.ui.doubleSpinBoxScale.setValue(0.5)
41
        self.text_scale = None
42

    
43
        self.isAccepted = False
44
 
45
        self.ui.toolButtonPDF.clicked.connect(self.on_add_PDF_click)
46
        self.ui.pushButtonSave.clicked.connect(self.on_save_setting)
47
        self.ui.pushButtonImport.clicked.connect(self.on_import_pdf)
48
        self.ui.pushButtonClose.clicked.connect(self.close)
49

    
50

    
51
    def on_add_PDF_click(self):
52
        """select drawing files"""
53
        project = AppDocData.instance().getCurrentProject()
54

    
55
        options = QFileDialog.Options()
56
        options |= QFileDialog.DontUseNativeDialog
57
        files, _ = QFileDialog.getOpenFileNames(self, self.tr('Import', "Select Drawing File"),
58
                                                project.getDrawingFilePath(), "png files (*.png)", options=options)
59
        if files:
60
            self.ui.lineEditDrawing.setText(files[0]) if len(files) == 1 else \
61
                self.ui.lineEditDrawing.setText(str(len(files)) + ' files')
62

    
63
            self._dwgs.clear()
64
            for file in files:
65
                if os.path.exists(file):
66
                    self._dwgs.append(file)
67
                    
68
    def on_save_setting(self):
69
        """save setting"""
70
        from AppDocData import Config        
71

    
72
        configs = []
73
        configs.append(Config('PDF Size', 'Width', self.ui.spinBoxWidth.value()))
74
        configs.append(Config('PDF Size', 'Height', self.ui.spinBoxHeight.value()))
75
        configs.append(Config('PDF Offset', 'X', self.ui.spinBoxTextX.value()))
76
        configs.append(Config('PDF Offset', 'Y', self.ui.spinBoxTextY.value()))
77
        configs.append(Config('PDF Offset', 'Scale', self.ui.doubleSpinBoxScale.value()))
78

    
79
        app_doc_data = AppDocData.instance()
80
        app_doc_data.saveConfigs(configs)
81

    
82
        QMessageBox.information(self, self.tr('Information'), self.tr('Successfully saved.'))
83

    
84
    def on_import_pdf(self):
85
        """import line and text from autocad"""
86
        import XmlGenerator as xg
87
        from Area import Area
88

    
89
        project = AppDocData.instance().getCurrentProject()
90

    
91
        temp_path = project.getTempPath()
92
        drawing_path = project.getDrawingFilePath()
93
        id2_xml_files = [f for f in os.listdir(temp_path) if os.path.isfile(os.path.join(temp_path, f)) and
94
                         (os.path.splitext(f)[1].upper() == '.XML')]
95

    
96
        self.text_scale = [self.ui.spinBoxWidth.value(), self.ui.spinBoxHeight.value(), self.ui.spinBoxTextX.value(), self.ui.spinBoxTextY.value(), self.ui.doubleSpinBoxScale.value()]
97

    
98
        for _file in self._dwgs:
99
            file_name = os.path.splitext(os.path.basename(_file))[0]
100
            matches = [id2_xml_file for id2_xml_file in id2_xml_files if id2_xml_file.replace(file_name, '').upper() == '.XML']
101
            if matches:
102
                try:
103
                    id2_image_file = os.path.join(drawing_path, file_name + '.png')
104
                    img = AppDocData.my_imread(file_path=id2_image_file)
105
                    _width = img.shape[1]
106
                    _height = img.shape[0]
107

    
108
                    symbol_areas = []
109
                    pdf_xml_path = os.path.join(os.path.dirname(_file), os.path.splitext(os.path.basename(_file))[0] + '.xml')
110
                    pdf_xml = parse(pdf_xml_path)
111

    
112
                    id2_xml_path = os.path.join(temp_path, matches[0])
113
                    id2_xml = parse(id2_xml_path)
114

    
115
                    pdf_xml_root = pdf_xml.getroot()
116
                    id2_xml_root = id2_xml.getroot()
117

    
118
                    for symbol in id2_xml_root.find('SYMBOLS').iter('SYMBOL'):
119
                        pt = [float(x) for x in symbol.find('LOCATION').text.split(',')]
120
                        size = [float(x) for x in symbol.find('SIZE').text.split(',')]
121
                        area = Area('Size')
122
                        area.x, area.y = pt[0], pt[1]
123
                        area.width, area.height = size[0], size[1]
124
                        symbol_areas.append(area)
125

    
126
                    """remove texts from id2 xml file"""
127
                    textInfo = id2_xml_root.find(xg.TEXT_INFO_LIST_NODE_NAME)
128
                    textInfo.clear()
129

    
130
                    noteInfo = id2_xml_root.find(xg.NOTE_TEXT_INFO_LIST_NOTE_NAME)
131
                    noteInfo.clear()
132

    
133
                    lineNoInfo = id2_xml_root.find(xg.LINE_NOS_NODE_NAME)
134
                    lineNoInfo.clear()
135
                    """up to here"""
136

    
137
                    """remove unknowns from id2 xml file"""
138
                    unknowns = id2_xml_root.find(xg.UNKNOWNS_NODE_NAME)
139
                    unknowns.clear()
140
                    """up to here"""
141

    
142
                    """add text from pdf file to id xml file"""
143
                    rects = []
144

    
145
                    for blk_tbl_record in pdf_xml_root.iter('Text'):
146
                        position = [float(token) for token in blk_tbl_record.attrib['Position'].split(',')]                    
147
    
148
                        rect = self.make_text_box(blk_tbl_record, position, _height)
149
                        rects.append(rect)
150

    
151
                    nodes = self.texts_to_xml(rects, symbol_areas)
152
                    if nodes:
153
                        textInfo.extend(nodes)
154

    
155
                    id2_xml.write(id2_xml_path, encoding="utf-8", xml_declaration=True)
156
                    """up to here"""
157

    
158
                    QMessageBox.information(self, self.tr('Information'), self.tr('Importing Success!'),
159
                                            QMessageBox.Close)
160
                except Exception as ex:
161
                    from App import App 
162
                    from AppDocData import MessageType
163

    
164
                    message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
165
                              f"{sys.exc_info()[-1].tb_lineno}"
166
                    App.mainWnd().addMessage.emit(MessageType.Error, message)
167
    
168
    def make_text_box(self, text_node, position, height):
169
        try:
170
            loc = [self.text_scale[2] + position[0], self.text_scale[3] + (height - position[1])]
171

    
172
            text = text_node.text
173
            angle = 0#round(float(text_node.attrib['Angle']), 2)
174

    
175
            #_height = self.text_scale[1]
176
            _height = round(float(text_node.attrib['Height']), 2)
177
            loc[1] += round(_height / 2, 2)
178
            _height = round(_height * self.text_scale[4], 2)
179
            loc[1] -= round(_height / 2, 2)
180

    
181
            #_width = round(_height * len(text) * self.text_scale[4])
182
            _width = round(float(text_node.attrib['Width']), 2)
183

    
184
            allowed_error = 0.01
185
            if abs(angle - 1.57) < allowed_error:
186
                _height, _width = _width, _height
187
                loc[0], loc[1] = loc[0] - _width, loc[1] - _height + _width
188
            elif abs(angle - 4.71) < allowed_error:
189
                _height, _width = _width, _height
190
                loc[0], loc[1] = loc[0] - _width, loc[1] + _height - _width
191

    
192
            rect = QRect(loc[0], loc[1], _width, _height)
193
            rect._text = text_node.text
194

    
195
            return rect
196

    
197
        except Exception as ex:
198
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
199
                    f"{sys.exc_info()[-1].tb_lineno}"
200

    
201
            print(message)
202

    
203

    
204
    def texts_to_xml(self, rects, symbol_areas):
205
        """try to convert text element to id2 xml"""
206
        import uuid
207
        from EngineeringTextItem import QEngineeringTextItem
208
        from TextItemFactory import TextItemFactory
209
        from TextInfo import TextInfo
210
        from CodeTables import CodeTable
211

    
212
        try:
213
            app_doc_data = AppDocData.instance()
214
            factory = TextItemFactory.instance()
215
            nodes = []
216

    
217
            '''
218
            loc = [self.text_scale[2] + position[0], self.text_scale[3] + position[1]]
219

220
            text = text_node.text
221
            angle = 0#round(float(text_node.attrib['Angle']), 2)
222

223
            #_height = self.text_scale[1]
224
            _height = round(float(text_node.attrib['Height']), 2)
225
            loc[1] += round(_height / 2, 2)
226
            _height = round(_height * self.text_scale[4], 2)
227
            loc[1] -= round(_height / 2, 2)
228

229
            #_width = round(_height * len(text) * self.text_scale[4])
230
            _width = round(float(text_node.attrib['Width']), 2)
231

232
            allowed_error = 0.01
233
            if abs(angle - 1.57) < allowed_error:
234
                _height, _width = _width, _height
235
                loc[0], loc[1] = loc[0] - _width, loc[1] - _height + _width
236
            elif abs(angle - 4.71) < allowed_error:
237
                _height, _width = _width, _height
238
                loc[0], loc[1] = loc[0] - _width, loc[1] + _height - _width
239
            '''
240

    
241
            # merge adjacent text box
242
            configs = app_doc_data.getConfigs('Text Recognition', 'Merge Size')
243
            mergeSize = int(configs[0].value) if 1 == len(configs) else 10
244
            gap_size = 3
245

    
246
            horizontals = rects
247

    
248
            h_merges = []
249
            for horizontal1 in horizontals:
250
                for horizontal2 in horizontals:
251
                    if horizontal1 is horizontal2:
252
                        continue
253
                    if abs(horizontal1.center().y() - horizontal2.center().y()) < gap_size:
254
                        l1, l2 = horizontal1.left() - mergeSize, horizontal2.left() - mergeSize
255
                        r1, r2 = horizontal1.right() + mergeSize, horizontal2.right() + mergeSize
256
                        l_x_y, s_x_y = [l1, r1], [l2, r2]
257
                        if not (max(l_x_y) < min(s_x_y) or max(s_x_y) < min(l_x_y)):
258
                            inserted = False
259
                            for merge in h_merges:
260
                                if horizontal1 in merge and horizontal2 in merge:
261
                                    inserted = True
262
                                    break
263
                                elif horizontal1 in merge and horizontal2 not in merge:
264
                                    merge.append(horizontal2)
265
                                    inserted = True
266
                                    break
267
                                elif horizontal2 in merge and horizontal1 not in merge:
268
                                    merge.append(horizontal1)
269
                                    inserted = True
270
                                    break
271
                            if not inserted:
272
                                h_merges.append([horizontal1, horizontal2])
273

    
274
            for merge in h_merges:
275
                for rect in merge:
276
                    if rect in rects:
277
                        rects.remove(rect)
278
                    else:
279
                        pass
280

    
281
            for merge in h_merges:
282
                max_x, max_y, min_x, min_y = 0, 0, sys.maxsize, sys.maxsize
283
                for rect in merge:
284
                    if rect.left() < min_x:
285
                        min_x = rect.left()
286
                    if rect.right() > max_x:
287
                        max_x = rect.right()
288
                    if rect.top() < min_y:
289
                        min_y = rect.top()
290
                    if rect.bottom() > max_y:
291
                        max_y = rect.bottom()
292

    
293
                text = None
294
                for rect in sorted(merge, key=lambda param:param.left()):
295
                    if text is None:
296
                        text = rect._text
297
                    elif text[-1] != '/' and text[-1] != '-' and text[-1] != '.':
298
                        text = text + ' ' + rect._text
299
                    else:
300
                        text = text + rect._text
301

    
302
                rect = QRect(min_x, min_y, max_x - min_x, max_y - min_y)
303
                rect._text = text
304
                rects.append(rect)
305
            # up to here
306

    
307
            for rect in rects:
308
                node = None
309
                text = rect._text
310
                loc = [rect.left(), rect.top()]
311
                _width, _height = rect.width(), rect.height()
312

    
313
                _item = factory.createTextItem(TextInfo(text, 10, 10, 10, 10, 0)) # just for type check
314

    
315
                item = QEngineeringTextItem()
316
                item.setPlainText(text)
317
                item.loc = loc
318
                item.size = (_width, _height)
319
                item.angle = 0
320

    
321
                for area in app_doc_data.getAreaList():
322
                    if area.contains([loc[0], loc[1]]):
323
                        item.area = area.name
324
                        break
325

    
326
                # determine instrument text
327
                if item.area != 'None' and item.area != 'Drawing':
328
                    node = item.toXml()
329
                elif item.area == 'Drawing' and type(_item) is not QEngineeringTextItem:
330
                    node = item.toXml()
331
                elif False:#item.area == 'Drawing':
332
                    app_doc_data.getReplaceTables()
333
                    app_doc_data.getCustomTables()
334
                    app_doc_data.loadSymbolAttributeCodeTables()
335
                    for table in CodeTable.TABLES.values():
336
                        if table.find_match_exactly(item.text()):
337
                            node = item.toXml()
338
                            break
339

    
340
                    if node == None:
341
                        for area in symbol_areas:
342
                            if area.contains([loc[0], loc[1]]):
343
                                node = item.toXml()
344
                                break
345
                else: # allow all text item
346
                    node = item.toXml()
347

    
348
                if node:
349
                    nodes.append(node)
350
            
351
            return nodes
352
        except Exception as ex:
353
            from App import App
354

    
355
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
356
                      f"{sys.exc_info()[-1].tb_lineno}"
357
            App.mainWnd().addMessage.emit(MessageType.Error, message)
358

    
359
def close(self):
360
    QDialog.reject(self)
클립보드 이미지 추가 (최대 크기: 500 MB)