def parse_drill_file(filename):
	info("parsing drill file \"" + filename + "\"...")
	# altium's drill file format specification looks like:
	# ;FILE_FORMAT=4:3
	# METRIC,LZ
	lines = []
	for line in open(filename):
		line = line.rstrip("\n\r")
		lines.append(line)
	global tool
	tool = {}
	for line in lines:
		match = re.search("T[0-9]+C([\.0-9]*)F.*", line)
		if match:
			diameter = match.group(1)
			tool[diameter] = []
			#debug(diameter)
			#debug(line)
		match = re.search("X([0-9]*)Y([0-9]*)", line)
		if match:
			x = match.group(1)
			y = match.group(2)
			x = +int(x)/1000.0 # fixme
			y = -int(y)/1000.0 # fixme
			location = (x, y)
			tool[diameter].append(location)
def save_svg_file():
	info("writing svg file \"" + svg_filename + "\"...")
	svg.viewbox(width=panel_width, height=panel_height)
	#svg.viewbox(width=stencil_width, height=stencil_height)
	#svg.viewbox(minx=, miny=, width=panel_width, height=panel_height)
	#svg.size(width=str(panel_width) + "mm", height=str(panel_height) + "mm")
	#svg.save()
	# from https://bitbucket.org/mozman/svgwrite/issues/18/adding-namespaces:
	import xml.etree.ElementTree
	xml_content = svg.get_xml()
	xml_content.attrib['xmlns:inkscape']='http://www.inkscape.org/namespaces/inkscape'
	xml_content.attrib['width']=str(panel_width) + "mm"
	xml_content.attrib['height']=str(panel_height) + "mm"
	#xml_content.attrib['units']="mm"
	#xml_content.attrib['inkscape:window-width']="1200"
	#xml_content.attrib['inkscape:window-height']="800"
	##xml_content.attrib['height']=str(panel_height) + "mm"
	xml_content.attrib['inkscape:document-units']="mm" # this is ignored by inkscape 0.91 r13725
	with open(svg_filename, 'wb') as fd:
		fd.write(xml.etree.ElementTree.tostring(xml_content))
def generate_eps_and_dxf():
# from https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Other_2D_formats
	info("converting svg file to eps...")
	subprocess.call(["inkscape", svg_filename, "--export-eps=" + eps_filename])
	info("converting eps file to dxf...")
	# pstoedit.exe -f "dxf:-polyaslines -mm" the-bicentennial-board.eps the-bicentennial-board.dxf
	# pstoedit.exe -f dxf:-polyaslines\ -mm the-bicentennial-board.eps the-bicentennial-board.dxf
	subprocess.call(["pstoedit", "-q", "-f", "dxf:-polyaslines -mm", eps_filename, dxf_filename])
	info("in solidworks, open dxf file, choose import as 2d sketch, select mm")
def try_making_a_tiled_object():
	info("trying to make a tiled object")
	tilly = svg.defs.add(svg.g(id='tilly'))
	tilly.add(svg.circle( (0,0), 5, fill="#00ff00" ))
	blah = svg.use(tilly, insert=(10,10))
	svg.add(blah)
def generate_pdf():
	info("converting svg file to pdf...")
	#/cygdrive/c/Program\ Files/Inkscape/inkscape.exe drill1.svg --export-pdf="blah.pdf"
	#cd /usr/local/bin
	#ln -s /cygdrive/c/Program\ Files/Inkscape/inkscape.exe
	subprocess.call(["inkscape", svg_filename, "--export-pdf=" + pdf_filename])
def parse_gerber(filename):
	info("parsing gerber file \"" + filename + "\"...")
	#global units
	#global zero_suppression_mode
	#global coordinate_mode
	#global layer_polarity
	#global x_format
	#global y_format
	#global number_of_digits
	#global ratio
	global decimal_places # fixme - a bit funny if this changes between gerbers we're reading
	zero_suppression_mode = "leading"
	coordinate_mode = "absolute"
	layer_polarity = "dark"
	x_format = "55"
	y_format = "55"
	#units = "mm"
	lines = []
	for line in open(filename):
		line = line.rstrip("\n\r")
		lines.append(line)
	gerber_instructions = []
	global apertures
	apertures = {}
	aperture = "00"
	matched_units = 0
	matched_format = 0
	set_ratio = 0
	#gerber_instructions.append("hi there")
	for line in lines:
		matches = 0
		match = re.search("^%MO([IM][NM])\*%$", line)
		if match:
			matches = matches + 1
			if (match.group(1) == "IN"):
				#debug("set units to inches: " + line)
				ratio = 1000.0 / 25.4 / 1.55 # fixme/todo: 1.55 is a magic number here
			else:
				#debug("set units to mm: " + line)
				ratio = 1.0
			matched_units = 1
		match = re.search("^%FS([LTD])([AI])X([0-9][0-9])Y([0-9][0-9])\*%$", line)
		if match:
			matches = matches + 1
			debug("set format: " + line)
			if (match.group(1) == "L"):
				zero_suppression_mode = "leading"
			else:
				zero_suppression_mode = "trailing"
			if (match.group(2) == "A"):
				coordinate_mode = "absolute"
			else:
				coordinate_mode = "incremental"
			x_format = match.group(3)
			y_format = match.group(4)
			decimal_places = int(x_format[1])
			number_of_digits = int(x_format[0]) + int(x_format[1])
			number_of_digits = number_of_digits + 1 # some gerber files output 7 digits for "24" format...
			#decimal_places = decimal_places + 1
			debug("number of digits to use for coordinates = " + str(number_of_digits))
			matched_format = 1
		match = re.search("^%ADD([0-9]+)C,([.0-9]+)\*%$", line) # %ADD010C,0.0254*%
		if match:
			matches = matches + 1
			#aperture_length = len(match.group(1))
			ap = int(match.group(1))
			apertures[ap] = ("C", float(match.group(2)), float(match.group(2)))
			debug("aperture definition: " + line)
		match = re.search("^%ADD([0-9]+)R,([.0-9]+)X([.0-9]+)\*%$", line) # %ADD028R,2.6X1.6*% or %ADD34R,0.0138X0.0472*%
		if match:
			matches = matches + 1
			ap = int(match.group(1))
			apertures[ap] = ("R", float(match.group(2)), float(match.group(3)))
			debug("aperture definition: " + line)
		match = re.search("^%ADD([0-9]+)O,([.0-9]+)X([.0-9]+)\*%$", line) # %ADD11O,0.0138X0.0669*%
		if match:
			matches = matches + 1
			ap = int(match.group(1))
			apertures[ap] = ("O", float(match.group(2)), float(match.group(3)))
			debug("aperture definition: " + line)
		match = re.search("^%LN([a-zA-Z0-9]+)\*%$", line)
		if match:
			matches = matches + 1
			gerber_instructions.append("setlayer" + match.group(1))
		match = re.search("^(G0[1-3])\*$", line)
		if match:
			matches = matches + 1
			gerber_instructions.append(match.group(1))
		match = re.search("^(G0[1-3][XY].*)\*$", line)
		if match:
			matches = matches + 1
			gerber_instructions.append(match.group(1))
		match = re.search("^([XY].*)\*$", line)
		if match:
			matches = matches + 1
			#debug(line)
			gerber_instructions.append(match.group(1))
		match = re.search("^(G54D)([0-9]+)\*$", line)
		if match:
			matches = matches + 1
			debug("aperture selection: " + match.group(2))
			gerber_instructions.append(match.group(1) + match.group(2))
			#debug(match.group(1))
		match = re.search("^%LP([DC])\*%$", line)
		if match:
			# this unfortunately only grabs the last instance...
			matches = matches + 1
			if (match.group(1) == "D"):
				layer_polarity = "dark"
			else:
				layer_polarity = "clear"
		match = re.search("^%IPNEG\*%$", line) # %IPNEG% = reverse whole layer
		if match:
			matches = matches + 1
			# do something here for this...
		match = re.search("^%IPPOS\*%$", line) # %IPPOS% = whole layer normal
		if match:
			matches = matches + 1
		match = re.search("^G04.*$", line)
		if match:
			matches = matches + 1
		match = re.search("^%$", line)
		if match:
			matches = matches + 1
		match = re.search("^\*$", line)
		if match:
			matches = matches + 1
		match = re.search("^G74\*$", line)
		if match:
			warning("cannot handle single quadrant G74 mode")
			matches = matches + 1
			#exit()
		match = re.search("^G75\*$", line) # multi-quadrant mode
		if match:
			matches = matches + 1
		match = re.search("^$", line)
		if match:
			matches = matches + 1
		match = re.search("^M02\*$", line) # M02* = end of job
		if match:
			matches = matches + 1
		match = re.search("^%AM(.*)\*$", line) # AM = aperture macro definition
		if match:
			matches = matches + 1
			warning("ignoring aperture macro \"" + match.group(1) + "\"")
		match = re.search("^([$0-9]*),", line) # aperture macro definition continuation
		if match:
			matches = matches + 1
		match = re.search("^%AD(.*)\*$", line) # AD = aperture macro instantiation
		if match:
			matches = matches + 1
			error("aperture instantiation \"" + match.group(1) + "\" ignored - this part needs to be coded")
		match = re.search("^%IN(.*)\*%$", line) # IN = image name
		if match:
			matches = matches + 1
			debug("ignoring image name \"" + match.group(1) + "\"")
		if (matches == 0):
			warning("did not parse \"" + line + "\"")
		if (set_ratio == 0) and (matched_units == 1) and (matched_format == 1):
			ratio = ratio / (10.0**int(decimal_places))
			debug("ratio = " + str(ratio))
			gerber_instructions.append("setratio" + str(ratio))
			set_ratio = 1
	if (matched_format == 0):
		error("did not find format line", 2)
	for aperture in apertures:
		(CROP, w, h) = apertures[aperture]
		debug("aperture[" + str(aperture) + "]: " + CROP + " " + str(w) + " " + str(h))
	#debug("units: " + units)
	debug("x_format: " + x_format)
	debug("y_format: " + y_format)
	debug("layer_polarity: " + layer_polarity)
	debug("zero_suppression_mode: " + zero_suppression_mode)
	debug("coordinate_mode: " + coordinate_mode)
	return gerber_instructions