def run(self, render_pass, renderer_state): use_pool = True if use_pool: pool = AppKit.NSAutoreleasePool.alloc().init() try: db.newDrawing() if renderer_state.previewing: ps = renderer_state.preview_scale db.size(self.rect.w * ps, self.rect.h * ps) db.scale(ps, ps) DATPen().rect(self.rect).f(self.bg).db_drawPath() else: db.size(self.rect.w, self.rect.h) render_pass.fn(*render_pass.args) result = None if renderer_state.previewing: previews = (render_pass.output_path.parent / "_previews") previews.mkdir(exist_ok=True, parents=True) preview_frame = previews / render_pass.output_path.name db.saveImage(str(preview_frame)) result = preview_frame else: render_pass.output_path.parent.mkdir(exist_ok=True, parents=True) db.saveImage(str(render_pass.output_path)) result = render_pass.output_path db.endDrawing() finally: if use_pool: del pool return result
def test(self): from drawBot.drawBotDrawingTools import _drawBotDrawingTool code = compile(source, "<%s>" % exampleName, "exec") namespace = {} _drawBotDrawingTool._addToNamespace(namespace) def mockSaveImage(path, **options): fileName = "example_mockSaveImage_" + os.path.basename(path) path = os.path.join(tempTestDataDir, fileName) drawBot.saveImage(path, **options) namespace["saveImage"] = mockSaveImage namespace["image"] = mockImage namespace["imageSize"] = mockImageSize namespace["imagePixelColor"] = mockImagePixelColor namespace["Variable"] = mockVariable namespace["printImage"] = mockPrintImage namespace["installFont"] = mockInstallFont namespace["uninstallFont"] = mockUninstallFont namespace["randint"] = mockRandInt randomSeed(0) drawBot.newDrawing() with StdOutCollector(captureStdErr=True): exec(code, namespace) fileName = "example_%s.png" % exampleName imagePath = os.path.join(tempTestDataDir, fileName) expectedImagePath = os.path.join(testDataDir, fileName) if doSaveImage: drawBot.saveImage(imagePath) self.assertImagesSimilar(imagePath, expectedImagePath)
def export(self, path): """Draw a page and export the document into the _export folder. Note that in this version, we still generate the document page just before it is exported. Not Page instances are stored in the Document yet. >>> doc = Document() >>> doc.export('_export/Document-export.jpg') # Exporting the JPG """ # Make sure that the _export folder exists, as it does not standard # dowload from Github, nor it is committed to Github. if path.startswith(EXPORT_DIR) and not os.path.exists(EXPORT_DIR): os.mkdir(EXPORT_DIR) # Now let DrawBot do its work, creating the page canvas, filling it # black, add the title text and then saving it. drawBot.newPage(self.w, self.h) # For now to have something visible, draw a gray rectangle filling the page. drawBot.fill(0.2) # Set fill color at 20% dark gray. drawBot.rect(0, 0, self.w, self.h) # Draw the rectangle for the entire page. # Create a Formatted String for white text with the specified font/fontSize. fs = drawBot.FormattedString('My specimen', font='Georgia', fontSize=80, fill=1) # Draw the FormattedString on this fixed position: x from left and y from top. drawBot.text(fs, (50, self.h - 100)) # Save the drawn DrawBot page into the _export folder, using `path` as file name. # File in the _export folder are ignored by Git, so they don't upload. drawBot.saveImage(path)
def test(self): # get the paths testPath = os.path.join(tempTestDataDir, "%s.%s" % (scriptName, ext)) expectedPath = os.path.join(testDataDir, "expected_%s.%s" % (scriptName, ext)) expectedOutputPath = os.path.join(testDataDir, "expected_%s.txt" % scriptName) expectedOutput = readExpectedOutput(expectedOutputPath) # get drawBot import drawBot # start a new drawing drawBot.newDrawing() # execute the script in place # temporarily ignore deprecation warnings (there are some from PyObjC :( ) with warnings.catch_warnings(): if ignoreDeprecationWarnings: warnings.simplefilter("ignore", DeprecationWarning) output = self.executeScriptPath(path) self.assertEqual(cleanupTraceback(output), cleanupTraceback(expectedOutput)) # save the image drawBot.saveImage(testPath) # tell drawBot drawing is done drawBot.endDrawing() self.assertForFileExtension(ext, expectedPath, testPath)
def test_oddPageWidth_mp4(self): # https://github.com/typemytype/drawbot/issues/250 self.makeTestAnimation(1, pageWidth=201, pageHeight=200) with TempFile(suffix=".mp4") as tmp: with self.assertRaises(DrawBotError) as cm: drawBot.saveImage(tmp.path) self.assertEqual(cm.exception.args[0], "Exporting to mp4 doesn't support odd pixel dimensions for width and height.")
def Composite(pens, rect, save_to, scale=2): db.newDrawing() rect = rect.scale(scale) db.newPage(rect.w, rect.h) def draw(pen, state, data): if state == 0: DrawBotPen(pen, rect).draw(scale=scale) elif state == -1: imgf = pen.data.get("imgf") if imgf: im = db.ImageObject() im.lockFocus() db.size(rect.w+300, rect.h+300) db.translate(150, 150) db.scale(scale) pen.data["im"] = im elif state == 1: imgf = pen.data.get("imgf") im = pen.data.get("im") if imgf and im: im.unlockFocus() imgf(im) x, y = im.offset() db.translate(-150, -150) db.image(im, (x, y)) if isinstance(pens, DATPen): pens = [pens] for dps in pens: dps.walk(draw) db.saveImage(str(save_to)) db.endDrawing()
def __init__(self, RCJKI): self.RCJKI = RCJKI start = time.time() ufo = self.RCJKI.currentFont # ufo = RCJKFont('/Users/jeremiehornus/Documents/GIT/TYPE/gscjkrcjk/Hanzi.rcjk') gname = self.RCJKI.currentGlyph.name gnames = [gname] for gname in gnames: glyph = ufo[gname] self._glyphAtomicElements = glyph._deepComponents self._glyphVariations = glyph._glyphVariations # _glyphAtomicElements = list(ufo._RFont[gname].lib['robocjk.deepComponent.atomicElements']) # _glyphVariations = dict(ufo._RFont[gname].lib['robocjk.deepComponent.glyphVariations']) axes = [axis for axis in self._glyphVariations] # for layer in ufo.layers: # if gname in layer and layer.name not in ['foreground', 'mask']: # axes.append(layer.name) # axes = keepCompatibleLayers(ufo[gname], axes) self.makeInstance(axes, gname, glyph) #saveImage('deepcompo.gif', imageResolution=144) outputPath = os.path.join(self.RCJKI.currentFont.fontPath, "Proofing", '%s.mp4' % gname) files.makepath(outputPath) # outputPath = '/Users/gaetanbaehr/Documents/BlackFoundry/%s.mp4'%gname db.saveImage(outputPath, imageResolution=144) stop = time.time() print(stop - start, 'for all movie')
def draw(): for i in range(0, 100): drawBot.newDrawing() drawBot.newPage(1000, 1000) drawBot.radialGradient( (randint(1,500), randint(1,500)), # startPoint (randint(1,800), randint(1,800)), # endPoint [(randint(0,1), randint(0,1), randint(0,1)), (randint(0,1), randint(0,1), randint(0,1)), (randint(0,1), randint(0,1), randint(0,1))], # colors [randint(0,1), randint(0,1), randint(0,1)], # locations randint(0,1), # startRadius randint(0,800) # endRadius ) # draw a rectangle drawBot.rect(randint(0,100), randint(0,100), randint(0,1000), randint(0,1000)) # draw the path drawBot.drawPath() drawBot.blendMode("multiply") # set a color drawBot.rect(randint(1,10), randint(1,10), randint(1,100), randint(1,100)) # draw oval x, y, width, height drawBot.oval(randint(1,500), randint(1,500), randint(1,500), randint(1,500)) drawBot.oval(randint(1,500), randint(1,500), randint(1,500), randint(1,500)) drawBot.cmykFill(randint(0,1), randint(0,1), randint(0,1), randint(0,1)) # draw a rectangle drawBot.rect(randint(1,10), randint(1,10), randint(1,1000), randint(1,1000)) # set an other color drawBot.cmykFill(randint(0,1), randint(0,1), randint(0,1), randint(0,1)) # overlap a second rectangle drawBot.rect(randint(1,500), randint(1,500), randint(1,600), randint(1,600)) drawBot.saveImage(str(i) + '.png' ) drawBot.endDrawing() print(str(i) + ' done.')
def test_saveImage_pathList(self): self.makeTestDrawing() with self.assertRaises(TypeError) as cm: drawBot.saveImage(["foo.abcde"], foo=123) self.assertEqual( cm.exception.args[0], 'Cannot apply saveImage options to multiple output formats.')
def simulation(steps: int, initialBoard: array) -> None: board = array(initialBoard, copy=True) for step in range(steps): draw_board(board) board = update_board(board) print(f"{step+1}/{steps}") draw.saveImage("LifeGame.mp4")
async def reload(self, trigger): try: load_drawbot = self.args.drawbot if load_drawbot: if not db: raise Exception("Cannot run drawbot program without drawBot installed") else: db.newDrawing() self.program = run_path(str(self.filepath)) if load_drawbot: with tempfile.NamedTemporaryFile(suffix=".svg") as tf: db.saveImage(tf.name) self.preview.clear() self.preview.send(f"<div class='drawbot-render'>{tf.read().decode('utf-8')}</div>", None) db.endDrawing() for k, v in self.program.items(): if isinstance(v, coldtype.text.reader.Font): await v.load() if v.path not in self.watchee_paths(): self.watchees.append([Watchable.Font, v.path]) for ext in v.font.getExternalFiles(): if ext not in self.watchee_paths(): self.watchees.append([Watchable.Font, ext]) elif isinstance(v, DefconFont): p = Path(v.path).resolve() if p not in self.watchee_paths(): self.watchees.append([Watchable.Font, p]) except Exception as e: self.program = None self.show_error()
def test_instructionStack(self): expected = [ "reset None", "newPage 200 200", "save", "clipPath moveTo 5.0 5.0 lineTo 15.0 5.0 lineTo 15.0 15.0 lineTo 5.0 15.0 closePath", "restore", "image Image Object 10 10 0.5 None", "blendMode saturation", "transform 1 0 0 1 10 10", "drawPath moveTo 10.0 10.0 lineTo 110.0 10.0 lineTo 110.0 110.0 lineTo 10.0 110.0 closePath", "textBox foo bar 72.48291015625 84.0 55.0341796875 26.0 center", "frameDuration 10", "saveImage * {'myExtraAgrument': True}" ] with StdOutCollector() as output: import drawBot drawBot.newDrawing() drawBot.size(200, 200) drawBot.save() path = drawBot.BezierPath() path.rect(5, 5, 10, 10) drawBot.clipPath(path) drawBot.restore() im = drawBot.ImageObject() with im: drawBot.size(20, 20) drawBot.rect(5, 5, 10, 10) drawBot.image(im, (10, 10), alpha=.5) drawBot.blendMode("saturation") drawBot.translate(10, 10) drawBot.rect(10, 10, 100, 100) drawBot.text("foo bar", (100, 100), align="center") drawBot.frameDuration(10) drawBot.saveImage("*", myExtraAgrument=True) drawBot.endDrawing() self.assertEqual(output.lines(), expected)
def exportPDFCallback(self, sender): path = putFile("pdf") self.draw(self.pdf.pages, export=True) outputPath = "%s.pdf" % path if path.endswith(".pdf"): outputPath = "%s.pdf" % path[:-4] db.saveImage(outputPath)
def __init__(self): drawBot.newDrawing() # A3, TODO: switch to A2. drawBot.newPage(1190, 842) self.stepIntoArena() self.juggle() path = '/Users/michiel/Desktop/circus.pdf' drawBot.saveImage(path)
def new_drawing(rect: Rect = Rect(1000, 1000), count=1, save_to=None): db.newDrawing() for idx in range(0, count): with new_page(rect) as r: yield idx, r if save_to: db.saveImage(str(save_to)) db.endDrawing()
def test_saveImage_multipage_positionalArgument(self): self.makeTestDrawing() with TempFile(suffix=".png") as tmp: with StdOutCollector(captureStdErr=True) as output: drawBot.saveImage(tmp.path, False) self.assertEqual(output.lines(), [ "*** DrawBot warning: 'multipage' should be a keyword argument: use 'saveImage(path, multipage=True)' ***" ])
def draw(self, saving=False, saveTo=None, fmt="pdf", layers=[], fill=None): savingToFont = isinstance(fmt, defcon.Font) if saving: db.newDrawing() self.saving = True else: self.saving = False db.newPage(*self.animation.dimensions) self.page = Rect.page() self.bps = {} for l in layers: self.bps[l] = RichBezier() with db.savedState(): if fill and not saveTo: with db.savedState(): db.fill(*fill) db.rect(*self.page) self.layers = layers self.animation.fn(self) self.layers = None if self.animation.burn: box = self.page.take(64, Edge.MinY).take(120, Edge.MaxX).offset(-24, 24) db.fontSize(20) db.lineHeight(20) db.font("Menlo-Bold") db.fill(0, 0.8) db.rect(*box.inset(-14, -14).offset(0, 2)) db.fill(1) db.textBox("{:07.2f}\n{:04d}\n{:%H:%M:%S}".format( self.time, self.i, datetime.datetime.now()), box, align="center") for k, bez in self.bps.items(): with db.savedState(): db.fill(*bez.fill) db.drawPath(bez.bp) if saving: if savingToFont: for k, bez in self.bps.items(): g = defcon.Glyph() g.name = "frame_" + str(self.i) g.unicode = self.i + 48 # to get to 0 g.width = self.animation.dimensions[0] bez.bp.drawToPen(g.getPen()) fmt.insertGlyph(g) else: db.saveImage(f"{saveTo}/{self.i}.{fmt}") db.endDrawing() self.saving = False
def test_linkURL_svg(self): expectedPath = os.path.join(testDataDir, "expected_svgLinkURL.svg") drawBot.newDrawing() drawBot.newPage(200, 200) drawBot.rect(10, 10, 20, 20) drawBot.linkURL("http://drawbot.com", (10, 10, 20, 20)) with TempFile(suffix=".svg") as tmp: drawBot.saveImage(tmp.path) self.assertEqual(readData(tmp.path), readData(expectedPath), "Files %r and %s are not the same" % (tmp.path, expectedPath))
def test_formattedStringURL_svg(self): expectedPath = os.path.join(testDataDir, "expected_formattedStringURL.svg") drawBot.newDrawing() drawBot.newPage(200, 200) drawBot.underline("single") drawBot.url("http://drawbot.com") drawBot.text("foo", (10, 10)) with TempFile(suffix=".svg") as tmp: drawBot.saveImage(tmp.path) self.assertEqual(readData(tmp.path), readData(expectedPath), "Files %r and %s are not the same" % (tmp.path, expectedPath))
def test_saveImage_unknownContext(self): self.makeTestDrawing() with self.assertRaises(DrawBotError) as cm: drawBot.saveImage("foo.abcde") self.assertEqual(cm.exception.args[0], "Could not find a supported context for: 'abcde'") with self.assertRaises(DrawBotError) as cm: with StdOutCollector(captureStdErr=True) as output: drawBot.saveImage(["foo.abcde"]) self.assertEqual(output.lines(), ['*** DrawBot warning: saveImage([path, path, ...]) is deprecated, use multiple saveImage statements. ***']) self.assertEqual(cm.exception.args[0], "Could not find a supported context for: 'abcde'")
def test_export_svg_fallbackFont(self): expectedPath = os.path.join(testDataDir, "expected_svgSaveFallback.svg") drawBot.newDrawing() drawBot.newPage(100, 100) drawBot.fallbackFont("Courier") drawBot.font("Times") drawBot.text("a", (10, 10)) with TempFile(suffix=".svg") as tmp: drawBot.saveImage(tmp.path) self.assertEqual(readData(tmp.path), readData(expectedPath), "Files %r and %s are not the same" % (tmp.path, expectedPath))
def Composite1(pens, rect, save_to, paginate=False, scale=2): db.newDrawing() rect = rect.scale(scale) if not paginate: db.newPage(rect.w, rect.h) for pen in DrawBotPen.FindPens(pens): if paginate: db.newPage(rect.w, rect.h) DrawBotPen(pen, rect).draw(scale=scale) db.saveImage(str(save_to)) db.endDrawing()
def export(self, path): """Export the document into the _export folder. We assume that the document and pages are built. We don't do that here, in case multiple formats are saved from the same build. """ if path.startswith(EXPORT_DIR) and not os.path.exists(EXPORT_DIR): os.mkdir(EXPORT_DIR) # Now all the pages drew them themselfs, we can export to the path. # let DrawBot do its work, saving it. drawBot.saveImage(path)
def drawWeight(weight, text): db.newDrawing() self.designFrameViewer.draw() db.newPage(FRAMEX, FRAMEY) db.textBox(user, (0, FRAMEY - 85, FRAMEX, 55), align='center') db.textBox(text, (0, FRAMEY - 105, FRAMEX, 55), align='center') s = .11 tx, ty = (FRAMEX / s - 1000 * 4) * .5, 1000 * 5.8 db.save() db.scale(s, s) db.translate(tx, ty) db.fontSize(60) print(weight) for i, glyph in enumerate(weight): drawDesignFrame() if glyph[0].markColor: db.fill(*glyph[0].markColor) else: db.fill(*INPROGRESS) db.rect(0, 900, 250, 100) db.fill(0, 0, 0, 1) db.stroke(None) db.textBox(glyph[0].name, (0, 900, 1000, 100), align="center") db.fill(0, 0, 0, 1) db.stroke(None) for c in glyph: db.drawGlyph(c) if (i + 1) % 4: db.translate(1000, 0) else: db.translate(-1000 * 3, -1200) db.restore() pdfData = db.pdfImage() now = datetime.datetime.now() if not self.RCJKI.currentFont.mysql: outputPath = os.path.join( self.RCJKI.currentFont.fontPath, "Proofing", user, '%s_%s_%s.pdf' % (date, str(pageIndex).zfill(2), text)) else: outputPath = os.path.join( mysqlpath, '%s_%s_%s.pdf' % (date, str(pageIndex).zfill(2), text)) # os.rename(outputPath, outputPath[:-3]+'ai') files.makepath(outputPath) db.saveImage(outputPath) os.rename(outputPath, outputPath[:-3] + "ai")
def export(self, path, multipage=True): """Export the document into the _export folder. We assume that the document and pages are built. We don't do that here, in case multiple formats are saved from the same build. """ if path.startswith(EXPORT_DIR) and not os.path.exists(EXPORT_DIR): os.mkdir(EXPORT_DIR) # Now all the pages drew them themselfs, we can export to the path. # let DrawBot do its work, saving it. # The optional `multipage` parametes saves “single page file formats” # (such as PNG and JPG) into a numbered sequence of image files. drawBot.saveImage(path, multipage=multipage)
def draw(x_lines, y_lines, width, height, out_fn): mult = 10 drawBot.newDrawing() drawBot.newPage(width * mult, height * mult) drawBot.fill(1, 1, 1) drawBot.rect(0, 0, width * mult, height * mult) draw_colors(x_lines, y_lines, width, height, mult) draw_lines(x_lines, y_lines, mult) drawBot.saveImage(out_fn) drawBot.endDrawing()
def createPlaceholder(wdt, hgt, locationOnDisk): dB.newDrawing() dB.newPage(wdt, hgt) dB.fill(.5) dB.rect(0, 0, dB.width(), dB.height()) dB.stroke(1) dB.strokeWidth(10) dB.line((0, 0), (dB.width(), dB.height())) dB.line((0, dB.height()), (dB.width(), 0)) dB.saveImage(f'{locationOnDisk}') dB.endDrawing()
def releaser(fn, path, frame_class=None): db.newDrawing() r = fn.rect w, h = r.wh() for idx in range(0, fn.duration): print(f"Saving page {idx}...") db.newPage(w, h) if frame_class: fn.func(frame_class(idx, fn)) else: fn.func(r) pdf_path = Path(path) pdf_path.parent.mkdir(exist_ok=True) db.saveImage(str(pdf_path)) print("Saved pdf", str(pdf_path)) db.endDrawing()
def export(self, path, force=False, multipage=True): """Export the document into the _export folder. We assume that the document and pages are built. We don't do that here, in case multiple formats are saved from the same build. If `force` is True or if build has not been done yet, then call self.build anyway. """ if force or not self.hasBuilt: # If forced or not done yet, build the pages. self.build() if path.startswith(EXPORT_DIR) and not os.path.exists(EXPORT_DIR): os.mkdir(EXPORT_DIR) # Now all the pages drew them themselfs, we can export to the path. # let DrawBot do its work, saving it. drawBot.saveImage(path, multipage=multipage)
def test_imageResolution(self): self.makeTestDrawing() with TempFile(suffix=".png") as tmp: drawBot.saveImage(tmp.path) self.assertEqual(drawBot.imageSize(tmp.path), (500, 500)) drawBot.saveImage(tmp.path, imageResolution=144) self.assertEqual(drawBot.imageSize(tmp.path), (1000, 1000)) drawBot.saveImage(tmp.path, imageResolution=36) self.assertEqual(drawBot.imageSize(tmp.path), (250, 250)) drawBot.saveImage(tmp.path, imageResolution=18) self.assertEqual(drawBot.imageSize(tmp.path), (125, 125))
def test_imageAntiAliasing(self): from testScripts import DrawBotTest expectedPath = os.path.join(testDataDir, "expected_imageAntiAliasing.png") drawBot.newDrawing() drawBot.size(100, 100) drawBot.fill(1, 0, 0) drawBot.oval(10, 10, 40, 80) drawBot.fill(0) drawBot.stroke(0) drawBot.line((-0.5, -0.5), (100.5, 100.5)) drawBot.line((0, 20.5), (100, 20.5)) drawBot.fontSize(20) drawBot.text("a", (62, 30)) with TempFile(suffix=".png") as tmp: drawBot.saveImage(tmp.path, antiAliasing=False) self.assertImageFilesEqual(tmp.path, expectedPath)
def test_instructionStack(self): expected = [ "reset None", "newPage 200 200", "save", "clipPath moveTo 5.0 5.0 lineTo 15.0 5.0 lineTo 15.0 15.0 lineTo 5.0 15.0 closePath", "restore", "image Image Object 10 10 0.5 None", "blendMode saturation", "transform 1 0 0 1 10 10", "drawPath moveTo 10.0 10.0 lineTo 110.0 10.0 lineTo 110.0 110.0 lineTo 10.0 110.0 closePath", "textBox foo bar 82.48291015625 84.0 35.0341796875 26.0 center", "frameDuration 10", "saveImage * {'myExtraAgrument': True}" ] with StdOutCollector() as output: import drawBot drawBot.newDrawing() drawBot.size(200, 200) drawBot.save() path = drawBot.BezierPath() path.rect(5, 5, 10, 10) drawBot.clipPath(path) drawBot.restore() im = drawBot.ImageObject() with im: drawBot.size(20, 20) drawBot.rect(5, 5, 10, 10) drawBot.image(im, (10, 10), alpha=.5) drawBot.blendMode("saturation") drawBot.translate(10, 10) drawBot.rect(10, 10, 100, 100) drawBot.text("foo bar", (100, 100), align="center") drawBot.frameDuration(10) drawBot.saveImage("*", myExtraAgrument=True) drawBot.endDrawing() self.assertEqual(output.lines(), expected)
def test(self): import __future__ from drawBot.drawBotDrawingTools import _drawBotDrawingTool compileFlags = __future__.CO_FUTURE_DIVISION code = compile(source, "<%s>" % exampleName, "exec", flags=compileFlags, dont_inherit=True) namespace = {} _drawBotDrawingTool._addToNamespace(namespace) def mockSaveImage(path, **options): fileName = "example_mockSaveImage_" + os.path.basename(path) path = os.path.join(tempTestDataDir, fileName) drawBot.saveImage(path, **options) namespace["saveImage"] = mockSaveImage namespace["image"] = mockImage namespace["imageSize"] = mockImageSize namespace["imagePixelColor"] = mockImagePixelColor namespace["Variable"] = mockVariable namespace["printImage"] = mockPrintImage namespace["installFont"] = mockInstallFont namespace["uninstallFont"] = mockUninstallFont namespace["randint"] = mockRandInt randomSeed(0) drawBot.newDrawing() with StdOutCollector(captureStdErr=True): exec(code, namespace) fileName = "example_%s.png" % exampleName imagePath = os.path.join(tempTestDataDir, fileName) expectedImagePath = os.path.join(testDataDir, fileName) if doSaveImage: drawBot.saveImage(imagePath) if allowFuzzyImageComparison: self.assertImagesSimilar(imagePath, expectedImagePath) else: self.assertFilesEqual(imagePath, expectedImagePath)
def mockSaveImage(path, **options): fileName = "example_mockSaveImage_" + os.path.basename(path) path = os.path.join(tempTestDataDir, fileName) drawBot.saveImage(path, **options)