hytos / DTI_PID / DTI_PID / ImportTextFromPDFDialog.py @ cf7f2a73
이력 | 보기 | 이력해설 | 다운로드 (14.8 KB)
1 | e8f8936a | humkyung | # 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 | 6e6d398b | esham21 | from AppDocData import Config |
71 | e8f8936a | humkyung | |
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 | ac2e45b1 | esham21 | QMessageBox.information(self, self.tr('Information'), self.tr('Successfully saved.')) |
83 | e8f8936a | humkyung | |
84 | def on_import_pdf(self): |
||
85 | """import line and text from autocad"""
|
||
86 | import XmlGenerator as xg |
||
87 | 6e6d398b | esham21 | from Area import Area |
88 | e8f8936a | humkyung | |
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 | 6e6d398b | esham21 | symbol_areas = [] |
103 | e8f8936a | humkyung | 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 | 6e6d398b | esham21 | 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 | e8f8936a | humkyung | """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 | 8e44b27f | esham21 | """add text from pdf file to id xml file"""
|
137 | 5b2f0e17 | esham21 | rects = [] |
138 | |||
139 | e8f8936a | humkyung | 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 | 5b2f0e17 | esham21 | 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 | e8f8936a | humkyung | |
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 | 5b2f0e17 | esham21 | |
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 | e8f8936a | humkyung | |
191 | 5b2f0e17 | esham21 | 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 | e8f8936a | humkyung | """try to convert text element to id2 xml"""
|
200 | import uuid |
||
201 | from EngineeringTextItem import QEngineeringTextItem |
||
202 | 5b2f0e17 | esham21 | from TextItemFactory import TextItemFactory |
203 | from TextInfo import TextInfo |
||
204 | 6e6d398b | esham21 | from CodeTables import CodeTable |
205 | e8f8936a | humkyung | |
206 | try:
|
||
207 | 6e6d398b | esham21 | app_doc_data = AppDocData.instance() |
208 | 5b2f0e17 | esham21 | factory = TextItemFactory.instance() |
209 | nodes = [] |
||
210 | 6e6d398b | esham21 | |
211 | 5b2f0e17 | esham21 | '''
|
212 | e8f8936a | humkyung | 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 | 6e6d398b | esham21 | #_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 | e8f8936a | humkyung |
|
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 | 5b2f0e17 | esham21 | '''
|
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 | 4726c0c0 | esham21 | elif text[-1] != '/' and text[-1] != '-' and text[-1] != '.': |
292 | 5b2f0e17 | esham21 | 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 | 6e6d398b | esham21 | break
|
319 | |||
320 | 5b2f0e17 | esham21 | # 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 | 8e44b27f | esham21 | elif False:#item.area == 'Drawing': |
326 | 5b2f0e17 | esham21 | 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 | 6e6d398b | esham21 | node = item.toXml() |
332 | break
|
||
333 | e8f8936a | humkyung | |
334 | 5b2f0e17 | esham21 | if node == None: |
335 | for area in symbol_areas: |
||
336 | if area.contains([loc[0], loc[1]]): |
||
337 | node = item.toXml() |
||
338 | break
|
||
339 | 8e44b27f | esham21 | else: # allow all text item |
340 | node = item.toXml() |
||
341 | 5b2f0e17 | esham21 | |
342 | if node:
|
||
343 | nodes.append(node) |
||
344 | |||
345 | return nodes
|
||
346 | e8f8936a | humkyung | 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) |