From 52fceae054134a8ff3b92c3453518a7e2d995cae Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Fri, 19 Apr 2019 17:12:10 +0300 Subject: [PATCH] - started to work on PDF import tool --- FlatCAMApp.py | 6 +- README.md | 6 ++ flatcamTools/ToolPDF.py | 178 +++++++++++++++++++++++++++++++++++++++ flatcamTools/__init__.py | 1 + share/pdf32.png | Bin 0 -> 2032 bytes 5 files changed, 189 insertions(+), 2 deletions(-) create mode 100644 flatcamTools/ToolPDF.py create mode 100644 share/pdf32.png diff --git a/FlatCAMApp.py b/FlatCAMApp.py index b19cb4a1..8620ed20 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -2007,8 +2007,10 @@ class App(QtCore.QObject): self.image_tool.install(icon=QtGui.QIcon('share/image32.png'), pos=self.ui.menufileimport, separator=True) self.pcb_wizard_tool = PcbWizard(self) - self.pcb_wizard_tool.install(icon=QtGui.QIcon('share/drill32.png'), pos=self.ui.menufileimport, - separator=True) + self.pcb_wizard_tool.install(icon=QtGui.QIcon('share/drill32.png'), pos=self.ui.menufileimport) + + self.pdf_tool = ToolPDF(self) + self.pdf_tool.install(icon=QtGui.QIcon('share/pdf32.png'), pos=self.ui.menufileimport) self.log.debug("Tools are installed.") diff --git a/README.md b/README.md index ced07166..54b860da 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,13 @@ CAD program, and create G-Code for Isolation routing. ================================================= +19.04.2019 + +- started to work on PDF import tool + + 18.04.2019 + - Gerber Editor: added custom mouse cursors for each mode in Add Track Tool - Gerber Editor: Poligonize Tool will first fuse polygons that touch each other and at a second try will create a polygon. The polygon will be automatically moved to Aperture '0' (regions). - Gerber Editor: Region Tool will add regions only in '0' aperture diff --git a/flatcamTools/ToolPDF.py b/flatcamTools/ToolPDF.py new file mode 100644 index 00000000..b2676a3b --- /dev/null +++ b/flatcamTools/ToolPDF.py @@ -0,0 +1,178 @@ +############################################################ +# FlatCAM: 2D Post-processing for Manufacturing # +# http://flatcam.org # +# File Author: Marius Adrian Stanciu (c) # +# Date: 3/10/2019 # +# MIT Licence # +############################################################ + +from FlatCAMTool import FlatCAMTool +from FlatCAMObj import * +import math +import numpy as np +import scipy.interpolate + +import zlib +import re + +import gettext +import FlatCAMTranslation as fcTranslate + +fcTranslate.apply_language('strings') +import builtins +if '_' not in builtins.__dict__: + _ = gettext.gettext + + +class ToolPDF(FlatCAMTool): + ''' + Parse a PDF file. + Reference here: https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/pdf_reference_archives/PDFReference.pdf + Return a list of geometries + ''' + toolName = _("PDF Import Tool") + + def __init__(self, app): + FlatCAMTool.__init__(self, app) + self.app = app + self.step_per_circles = self.app.defaults["gerber_circle_steps"] + + self.stream_re = re.compile(b'.*?FlateDecode.*?stream(.*?)endstream', re.S) + + # detect 'w' command + self.strokewidth_re = re.compile(r'^(\d+\.?\d*)\s*w$') + # detect 're' command + self.rect_re = re.compile(r'^(-?\d+\.?\d*)\s(-?\d+\.?\d*)\s(-?\d+\.?\d*)\s(-?\d+\.?\d*)\sre$') + # detect 'm' command + self.start_path_re = re.compile(r'(-?\d+\.?\d*)\s(-?\d+\.?\d*)\sm$') + # detect 'l' command + self.draw_line_re = re.compile(r'(-?\d+\.?\d*)\s(-?\d+\.?\d*)\sl') + # detect 'c' command + self.draw_arc_3pt_re = re.compile(r'(-?\d+\.?\d*)\s(-?\d+\.?\d*)\s(-?\d+\.?\d*)\s(-?\d+\.?\d*)\s(-?\d+\.?\d*)\s(-?\d+\.?\d*)\sc$') + # detect 'v' command + self.draw_arc_2pt_23_re = re.compile(r'(-?\d+\.?\d*)\s(-?\d+\.?\d*)\s(-?\d+\.?\d*)\s(-?\d+\.?\d*)\sv$') + # detect 'y' command + self.draw_arc_2pt_13_re = re.compile(r'(-?\d+\.?\d*)\s(-?\d+\.?\d*)\s(-?\d+\.?\d*)\s(-?\d+\.?\d*)\sy$') + # detect 'h' command + self.end_path_re = re.compile(r'^h$') + + + self.pdf_parsed = '' + + def run(self, toggle=True): + self.app.report_usage("ToolPDF()") + + # if toggle: + # # if the splitter is hidden, display it, else hide it but only if the current widget is the same + # if self.app.ui.splitter.sizes()[0] == 0: + # self.app.ui.splitter.setSizes([1, 1]) + # else: + # try: + # if self.app.ui.tool_scroll_area.widget().objectName() == self.toolName: + # self.app.ui.splitter.setSizes([0, 1]) + # except AttributeError: + # pass + # else: + # if self.app.ui.splitter.sizes()[0] == 0: + # self.app.ui.splitter.setSizes([1, 1]) + # + # FlatCAMTool.run(self) + + self.set_tool_ui() + self.on_open_pdf_click() + + # self.app.ui.notebook.setTabText(2, "PDF Tool") + + def install(self, icon=None, separator=None, **kwargs): + FlatCAMTool.install(self, icon, separator, shortcut='ALT+Q', **kwargs) + + def set_tool_ui(self): + pass + + def on_open_pdf_click(self): + """ + File menu callback for opening an PDF file. + + :return: None + """ + + self.app.report_usage("ToolPDF.on_open_pdf_click()") + self.app.log.debug("ToolPDF.on_open_pdf_click()") + + _filter_ = "Adobe PDF Files (*.pdf);;" \ + "All Files (*.*)" + + try: + filenames, _f = QtWidgets.QFileDialog.getOpenFileNames(caption=_("Open PDF"), + directory=self.app.get_last_folder(), filter=_filter_) + except TypeError: + filenames, _f = QtWidgets.QFileDialog.getOpenFileNames(caption=_("Open PDF"), filter=_filter_) + + filenames = [str(filename) for filename in filenames] + + if len(filenames) == 0: + self.app.inform.emit(_("[WARNING_NOTCL] Open PDF cancelled.")) + else: + for filename in filenames: + if filename != '': + self.app.worker_task.emit({'fcn': self.open_pdf, + 'params': [filename]}) + + def open_pdf(self, filename): + + def obj_init(grb_obj, app_obj): + with open(filename, "rb") as f: + pdf = f.read() + + for s in re.findall(self.stream_re, pdf): + s = s.strip(b'\r\n') + try: + self.pdf_parsed += zlib.decompress(s).decode('UTF-8') + except: + pass + grb_obj.solid_geometry = [self.bezier_to_linestring(0, 0, 0, 0)] + + + with self.app.proc_container.new(_("Opening PDF.")): + # obj_init() + self.parse_pdf() + ret = self.app.new_object("geometry", "bla", obj_init, autoselected=False) + # Register recent file + self.app.file_opened.emit("geometry", "bla") + # # Object name + # name = outname or filename.split('/')[-1].split('\\')[-1] + # + # ret = self.new_object("excellon", name, obj_init, autoselected=False) + # if ret == 'fail': + # self.inform.emit(_('[ERROR_NOTCL] Open Excellon file failed. Probable not an Excellon file.')) + # return + # + # # Register recent file + # self.file_opened.emit("excellon", filename) + # + # # GUI feedback + # self.inform.emit(_("[success] Opened: %s") % filename) + # # self.progress.emit(100) + + def parse_pdf(self): + for pline in self.pdf_parsed: + pass + + def bezier_to_linestring(self, start, stop, c1, c2): + """ + From here: https://gis.stackexchange.com/questions/106937/python-library-or-algorithm-to-generate-arc-geometry-from-three-coordinate-pairs + :return: LineString geometry + """ + coords = np.array([[0, 0], [25, 10], [33, 39], [53, 53]]) + + # equation Bezier, page 184 PDF 1.4 reference + # https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/pdf_reference_archives/PDFReference.pdf + # R(t) = P0*(1 - t) ** 3 + P1*3*t*(1 - 5) ** 2 + P2 * 3*(1 - t) * t ** 2 + P3*t ** 3 + + domain = [] + i = 0 + while i <=1: + domain.append(i) + for i in domain: + + return even_line diff --git a/flatcamTools/__init__.py b/flatcamTools/__init__.py index 6119a4c1..71286c55 100644 --- a/flatcamTools/__init__.py +++ b/flatcamTools/__init__.py @@ -15,5 +15,6 @@ from flatcamTools.ToolNonCopperClear import NonCopperClear from flatcamTools.ToolTransform import ToolTransform from flatcamTools.ToolSolderPaste import SolderPaste from flatcamTools.ToolPcbWizard import PcbWizard +from flatcamTools.ToolPDF import ToolPDF from flatcamTools.ToolShell import FCShell diff --git a/share/pdf32.png b/share/pdf32.png new file mode 100644 index 0000000000000000000000000000000000000000..f90b13f7c3162a2f30e4804ff4b1b787a4e42b70 GIT binary patch literal 2032 zcmV3_ekWc}pf)D_Ng@ zcJ)0on#^UrUP*o|OR|1mAV06q;fr(V&!JnxE%D15-j$As1TQlE&dYQ7<{Spx-UYWY zpTme77a@lLUt`IrSEUo1h!d6)?cdT7IYLSSSkzDF@NwxxX5bM^>Go^sh=h_#fUnNs zE$;WOx53BUkEJ6KLJ9$R@u$)WD+ts6rgS8Hkrtr!i@!19MkJ6FfY1Dg(g~`F6K$~<^Q1?`5=aT~9hRn^@;+5dAR)kqSUN>Q;ddr1Mdk0MGr|B%wGm6r zK9*`FkPzS}ENx(4*k)#?-bxSJ54d9qVg=w`GQ+>U&tq9J`m=Px1Jn2@bgt?oh!r5D z+4s06{+2HO#?M&9m7ppKVg(4v7$&;LXT1;7mE3A1h!x-;_!a-BbNH-uVk6)St+&GG ztU`hq0YaXQ18%3%XW~;V5=+B#7QHcI1PI9#mJ-E_yG&^fg@a256w03*0?g;|QHjI} zl2QOt>~WAo03LJ8&;O9L#!y586+kwvv1FG(1&~dv1X(3e0c6uEMK%dk0NJ!kl0^a) zKsK$?B$q%1kWFg}l1iWg$fh+Fl;D#TCsqMu)0&bA2?m;~0J3RK%>-cyQ~=qu=0J4v zi3%W_)?9E5hR8HD6+kwvx!`Mf&bjdqvm;!BO*!x6~LdrQ0Lso84)9{Q~-ZI$1;AeM%W@aG~Yr62&2}OPkJ1j zbD;u+!{PAki0K5+*{y+h@)5aur~r2Z^L4~>QGMaDD!`qQW!jwzP%j}fuX?BecZqM) zvxf@cgdI72joPh$^Uk`3^?rYYUtNI5r~npRE0|OsFwF0&_ zze#|@gDMiD0wjoEZ~1?11q4#N0JgVjZ1Za~P$1E@3qbyW->zjPUtEY00M*z59e_drIv{zDu4jC$sv61n@WY6hKQ>G0+`zWyvUS- z5||cbsHJJB0z_ha53nt@>UvKwp!{qh6(ADR#LyYfSJiS&9_>-KQm6t%V5|I@ig#Np zq4bXDDnJCbuHO#t&j5P4#?#hR9AJJNFb`fizDI#r&p29&r~na|b~bm#<@NfxZI!MO zPZzsG*eYS$f)uQHhBPr%fP>gx|6W^X|C$7`%xfHcA1NxpK}^r|8SW)j*&^s6zXsr* zkoOKMfMC4#13$L~AGeT`0e($P72p7-Oxu=KCn+xaY4weh^&AZQ)&kEz zL8gM}yX%(<;K{X)92dOk6PCN(XNeegFtIc^+Efh54;A1F?Cu@0+)*JHB=n;1p>PA9 zuRiMgyFL6ngH!+u+l3zjzB6OF;H;EME(XQUBMP|n5MGu-^F`oqhi>+$0_@8fqEdbp#b){T)+V+QoqKgE=HaRkPC89f(scgE^&WP5|EdEW$g>=i91Zri$Q$QCc8^qBn|G$laD`}`IzK50q-;!_ITd$E5>a{>^K>1T(|zOB!@eng#1x~8H@ z0X8^Jf9ne7U4M(Ob7N^*07V689u=T*RDi}&0UGBn1eoETm8;lsEpvZKkBS8#Y}2>U zaU+Rs*E1763J4Gq!2$O%NHZs{Jr4F9zQJO*N--_NV&Vj?2ZZAL{O3*h!OOb3kNyY{()>tr0pRdK)Zr zxPmRQU7Bm9O(5B)wW-KLVmIbbI32@1_u3nGuoj2~0c?~Bl?*X#DC;1-^%hjJahL_y{ O0000