프로젝트

일반

사용자정보

통계
| 개정판:

hytos / DTI_PID / DTI_PID / ImportTextFromPDFDialog.py @ dd913487

이력 | 보기 | 이력해설 | 다운로드 (14.8 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
        id2_xml_files = [f for f in os.listdir(temp_path) if os.path.isfile(os.path.join(temp_path, f)) and
93
                         (os.path.splitext(f)[1].upper() == '.XML')]
94

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

    
97
        for _file in self._dwgs:
98
            file_name = os.path.splitext(os.path.basename(_file))[0]
99
            matches = [id2_xml_file for id2_xml_file in id2_xml_files if id2_xml_file.replace(file_name, '').upper() == '.XML']
100
            if matches:
101
                try:
102
                    symbol_areas = []
103
                    pdf_xml_path = os.path.join(os.path.dirname(_file), os.path.splitext(os.path.basename(_file))[0] + '.xml')
104
                    pdf_xml = parse(pdf_xml_path)
105

    
106
                    id2_xml_path = os.path.join(temp_path, matches[0])
107
                    id2_xml = parse(id2_xml_path)
108

    
109
                    pdf_xml_root = pdf_xml.getroot()
110
                    id2_xml_root = id2_xml.getroot()
111

    
112
                    for symbol in id2_xml_root.find('SYMBOLS').iter('SYMBOL'):
113
                        pt = [float(x) for x in symbol.find('LOCATION').text.split(',')]
114
                        size = [float(x) for x in symbol.find('SIZE').text.split(',')]
115
                        area = Area('Size')
116
                        area.x, area.y = pt[0], pt[1]
117
                        area.width, area.height = size[0], size[1]
118
                        symbol_areas.append(area)
119

    
120
                    """remove texts from id2 xml file"""
121
                    textInfo = id2_xml_root.find(xg.TEXT_INFO_LIST_NODE_NAME)
122
                    textInfo.clear()
123

    
124
                    noteInfo = id2_xml_root.find(xg.NOTE_TEXT_INFO_LIST_NOTE_NAME)
125
                    noteInfo.clear()
126

    
127
                    lineNoInfo = id2_xml_root.find(xg.LINE_NOS_NODE_NAME)
128
                    lineNoInfo.clear()
129
                    """up to here"""
130

    
131
                    """remove unknowns from id2 xml file"""
132
                    unknowns = id2_xml_root.find(xg.UNKNOWNS_NODE_NAME)
133
                    unknowns.clear()
134
                    """up to here"""
135

    
136
                    """add text from pdf file to id xml file"""
137
                    rects = []
138

    
139
                    for blk_tbl_record in pdf_xml_root.iter('Text'):
140
                        position = [float(token) for token in blk_tbl_record.attrib['Position'].split(',')]                    
141
    
142
                        rect = self.make_text_box(blk_tbl_record, position)
143
                        rects.append(rect)
144

    
145
                    nodes = self.texts_to_xml(rects, symbol_areas)
146
                    if nodes:
147
                        textInfo.extend(nodes)
148

    
149
                    id2_xml.write(id2_xml_path, encoding="utf-8", xml_declaration=True)
150
                    """up to here"""
151

    
152
                    QMessageBox.information(self, self.tr('Information'), self.tr('Importing Success!'),
153
                                            QMessageBox.Close)
154
                except Exception as ex:
155
                    from App import App 
156
                    from AppDocData import MessageType
157

    
158
                    message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
159
                              f"{sys.exc_info()[-1].tb_lineno}"
160
                    App.mainWnd().addMessage.emit(MessageType.Error, message)
161
    
162
    def make_text_box(self, text_node, position):
163
        try:
164
            loc = [self.text_scale[2] + position[0], self.text_scale[3] + position[1]]
165

    
166
            text = text_node.text
167
            angle = 0#round(float(text_node.attrib['Angle']), 2)
168

    
169
            #_height = self.text_scale[1]
170
            _height = round(float(text_node.attrib['Height']), 2)
171
            loc[1] += round(_height / 2, 2)
172
            _height = round(_height * self.text_scale[4], 2)
173
            loc[1] -= round(_height / 2, 2)
174

    
175
            #_width = round(_height * len(text) * self.text_scale[4])
176
            _width = round(float(text_node.attrib['Width']), 2)
177

    
178
            allowed_error = 0.01
179
            if abs(angle - 1.57) < allowed_error:
180
                _height, _width = _width, _height
181
                loc[0], loc[1] = loc[0] - _width, loc[1] - _height + _width
182
            elif abs(angle - 4.71) < allowed_error:
183
                _height, _width = _width, _height
184
                loc[0], loc[1] = loc[0] - _width, loc[1] + _height - _width
185

    
186
            rect = QRect(loc[0], loc[1], _width, _height)
187
            rect._text = text_node.text
188

    
189
            return rect
190

    
191
        except Exception as ex:
192
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
193
                    f"{sys.exc_info()[-1].tb_lineno}"
194

    
195
            print(message)
196

    
197

    
198
    def texts_to_xml(self, rects, symbol_areas):
199
        """try to convert text element to id2 xml"""
200
        import uuid
201
        from EngineeringTextItem import QEngineeringTextItem
202
        from TextItemFactory import TextItemFactory
203
        from TextInfo import TextInfo
204
        from CodeTables import CodeTable
205

    
206
        try:
207
            app_doc_data = AppDocData.instance()
208
            factory = TextItemFactory.instance()
209
            nodes = []
210

    
211
            '''
212
            loc = [self.text_scale[2] + position[0], self.text_scale[3] + position[1]]
213

214
            text = text_node.text
215
            angle = 0#round(float(text_node.attrib['Angle']), 2)
216

217
            #_height = self.text_scale[1]
218
            _height = round(float(text_node.attrib['Height']), 2)
219
            loc[1] += round(_height / 2, 2)
220
            _height = round(_height * self.text_scale[4], 2)
221
            loc[1] -= round(_height / 2, 2)
222

223
            #_width = round(_height * len(text) * self.text_scale[4])
224
            _width = round(float(text_node.attrib['Width']), 2)
225

226
            allowed_error = 0.01
227
            if abs(angle - 1.57) < allowed_error:
228
                _height, _width = _width, _height
229
                loc[0], loc[1] = loc[0] - _width, loc[1] - _height + _width
230
            elif abs(angle - 4.71) < allowed_error:
231
                _height, _width = _width, _height
232
                loc[0], loc[1] = loc[0] - _width, loc[1] + _height - _width
233
            '''
234

    
235
            # merge adjacent text box
236
            configs = app_doc_data.getConfigs('Text Recognition', 'Merge Size')
237
            mergeSize = int(configs[0].value) if 1 == len(configs) else 10
238
            gap_size = 3
239

    
240
            horizontals = rects
241

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

    
268
            for merge in h_merges:
269
                for rect in merge:
270
                    if rect in rects:
271
                        rects.remove(rect)
272
                    else:
273
                        pass
274

    
275
            for merge in h_merges:
276
                max_x, max_y, min_x, min_y = 0, 0, sys.maxsize, sys.maxsize
277
                for rect in merge:
278
                    if rect.left() < min_x:
279
                        min_x = rect.left()
280
                    if rect.right() > max_x:
281
                        max_x = rect.right()
282
                    if rect.top() < min_y:
283
                        min_y = rect.top()
284
                    if rect.bottom() > max_y:
285
                        max_y = rect.bottom()
286

    
287
                text = None
288
                for rect in sorted(merge, key=lambda param:param.left()):
289
                    if text is None:
290
                        text = rect._text
291
                    elif text[-1] != '/' and text[-1] != '-' and text[-1] != '.':
292
                        text = text + ' ' + rect._text
293
                    else:
294
                        text = text + rect._text
295

    
296
                rect = QRect(min_x, min_y, max_x - min_x, max_y - min_y)
297
                rect._text = text
298
                rects.append(rect)
299
            # up to here
300

    
301
            for rect in rects:
302
                node = None
303
                text = rect._text
304
                loc = [rect.left(), rect.top()]
305
                _width, _height = rect.width(), rect.height()
306

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

    
309
                item = QEngineeringTextItem()
310
                item.setPlainText(text)
311
                item.loc = loc
312
                item.size = (_width, _height)
313
                item.angle = 0
314

    
315
                for area in app_doc_data.getAreaList():
316
                    if area.contains([loc[0], loc[1]]):
317
                        item.area = area.name
318
                        break
319

    
320
                # determine instrument text
321
                if item.area != 'None' and item.area != 'Drawing':
322
                    node = item.toXml()
323
                elif item.area == 'Drawing' and type(_item) is not QEngineeringTextItem:
324
                    node = item.toXml()
325
                elif False:#item.area == 'Drawing':
326
                    app_doc_data.getReplaceTables()
327
                    app_doc_data.getCustomTables()
328
                    app_doc_data.loadSymbolAttributeCodeTables()
329
                    for table in CodeTable.TABLES.values():
330
                        if table.find_match_exactly(item.text()):
331
                            node = item.toXml()
332
                            break
333

    
334
                    if node == None:
335
                        for area in symbol_areas:
336
                            if area.contains([loc[0], loc[1]]):
337
                                node = item.toXml()
338
                                break
339
                else: # allow all text item
340
                    node = item.toXml()
341

    
342
                if node:
343
                    nodes.append(node)
344
            
345
            return nodes
346
        except Exception as ex:
347
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
348
                    f"{sys.exc_info()[-1].tb_lineno}"
349

    
350
            print(message)
351

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