PDF adding pages to existing file



  • Hello,

    I'm currently using Drawbot to generate a character set of the current font. The problem is that when saves the PDF it doesn't generate a new file but it keeps adding pages to an existing PDF in the directory.

    I'm not sure how much would help to have the whole script but for now I'm sharing the block of code that I think is causing this problem. I guess it is the way I'm saving the PDF.

    # -----------------------------------------------------
    # Accessing the directory of the file
    fDirectory = os.path.dirname(f.filepath) # Directory
    fName = os.path.basename(f.filepath) # Only the name
    fNameParts = os.path.splitext(fName) # Split parts
    fPath = f.filepath
    
    # -----------------------------------------------------
    # Generating a file with the character set 
    NewfName = fNameParts[0] + ' - Character set.pdf' # Change extension
    NewfPath = os.path.join(fDirectory, NewfName) 
    
    # Saving the .pdf
    saveImage(NewfPath)
    
    

    Thanks beforehand for any help!



  • hello @RicardGarcia,

    from the problem description, it sounds like youโ€™re saving the pdf inside a loop, for example after making each page. usually what you want to do instead is create pages with a loop, and then save the pdf at the end outside the loop.

    (the snippet you posted is not related to the problem, I think)

    hope this helps!



  • ps. see also this character set proof example using the DrawBot extension in RoboFont.



  • Thanks for the answer, @gferreira. I'm actually saving the pdf at the very end of the script outside the loop that creates the pages.

    I'm using Drawbot inside Glyphs and, just to test it, I've run the script both using Drawbot as a module (which generates the problem) and running it with the UI inside Glyphs and it works fine.

    I still don't fully get what's going on there, do you have any idea? I could share the code if you want.

    Thanks again for your help and the link.


  • admin

    outside the app DrawBot, when using DrawBot as a module, you have to call drawBot.newDrawing() to reset the drawing stack.

    see https://www.drawbot.com/content/canvas/pages.html#drawBot.newDrawing

    its also adviced to call drawBot.endDrawing() when you are done.

    see https://www.drawbot.com/content/canvas/pages.html#drawBot.endDrawing



  • Thanks @frederik! This was what I was missing and now it works just as I expected.



  • Hello again,

    I'm again using DrawBot as a module to use it inside Glyphs and, while ths script works inside Drawbot's plugin in Glyphs, it doesn't generate the same output when I'm using it as a script.

    I'm using newDrawing() and endDrawing() but still it doesn't work.

    Any idea what it could be?

    Thanks!


  • admin

    euh, nope, you will need to provide more info ๐Ÿ™‚

    do you have a sample script?
    and how is the output different?

    thanks



  • Hi @frederik, I'm posting the script itself (even though it's a bit long) but before, maybe this will help knowing what's happening. So I'm doing an animation out of different frames and each frame has a glyph drawn in it. When I'm using Drawbot inside Glyphs its drawing both frame (red background) and the glyph in white.

    When it comes to run the script outside Drawbot (inside Glyphs), it keeps drawing the red background and each frame but it is no longer drawing the glyph. I guess it could have to do with completeBezierPathfrom Glyphs but I've used before and it works in other scripts.

    Here the script:

    # ---------------------
    # Modules
    # ---------------------
    from GlyphsApp import *
    from drawBot import *
    from AppKit import NSColor
    import vanilla, math
    import textwrap
    import os
    
    
    # ---------------------
    # Empty stack for Drawbot
    newDrawing()
    
    
    # ---------------------
    # Clear log in Macro Panel
    Glyphs.clearLog()
    
    
    
    class singleGlyphsAnimation( object ):
    	"""GUI generating an animation with all selected characters in the Font level"""
    
    	def __init__( self ):
    		
    		# Window sizes
    		windowW = 350
    		windowH = 320
    		
    		# UI elements
    		linePos = 20
    		margin = 20
    		lineHeight = 23
    		columnLine = 140
    		#print(columnLine)
    		
    
    
    		# Building window
    		self.w = vanilla.FloatingWindow(
    			 ( windowW, windowH ),
    			 "Animation generator"
    			 )
    
    		# ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท
    
    		# Title
    		self.w.descriptionText = vanilla.TextBox( (margin, linePos-5, -margin, lineHeight), "Select the characters you want in the animation", sizeStyle = "small")
    		# Opening the window
    		self.w.open()
    		
    		# ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท
    		
    		
    		
    		linePos += lineHeight*1.4
    		#self.w.line0 = HorizontalLine((margin, int(linePos+4), -margin, 1))
    		#linePos += lineHeight
    
    		
    		
    		# ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท
    		
    		# Page format (in pixels)
    		self.w.pageFormatTitle = vanilla.TextBox((margin, linePos, -margin, lineHeight), "Set frame format (px):", sizeStyle = "small")
    		
    		# ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท
    
    		# ColorWell
    		self.w.ColorWellTitle = vanilla.TextBox((columnLine + 40, linePos, 200, lineHeight), "Set colors:", sizeStyle = "small")
    
    		# ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท
    
    
    		linePos += lineHeight
    		# Width
    		self.w.widthTitle = vanilla.TextBox((margin*3, linePos+2, 100, lineHeight), "Width", sizeStyle = "small") 
    		self.w.widthUser = vanilla.EditText((110, linePos, 50, 20), "1920", sizeStyle = "small")
    		#print(int(str(self.w.widthUser.get())))
    		
    		
    		# Foreground color
    		self.w.fgColorTitle = vanilla.TextBox((columnLine + margin*3, linePos+2, 100, lineHeight), "Foreground", sizeStyle = "small") 
    		self.w.fgColorWell = vanilla.ColorWell((columnLine + 20 + 120, linePos, 50, 20), color = NSColor.colorWithCalibratedRed_green_blue_alpha_(255/255, 255/255, 255/255, 1))
    		#print(self.w.fgColorWell.get())
    		linePos += lineHeight
    		
    		# Height
    		self.w.heightTitle = vanilla.TextBox((margin*3, linePos+2, 100, lineHeight), "Height", sizeStyle = "small") 
    		self.w.heightUser = vanilla.EditText((110, linePos, 50, 20), "1200", sizeStyle = "small")
    		
    		# Background color
    		self.w.bgColorTitle = vanilla.TextBox((columnLine + margin*3, linePos+2, 100, lineHeight), "Background", sizeStyle = "small") 
    		self.w.bgColorWell = vanilla.ColorWell((columnLine + 20 + 120, linePos, 50, 20), color = NSColor.colorWithCalibratedRed_green_blue_alpha_(0/255, 0/255, 0/255, 1))
    		#print(self.w.bgColorWell.get())
    
    		
    		# ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท
    
    
    		linePos += lineHeight +3
    		self.w.line = vanilla.HorizontalLine((margin, int(linePos+12), columnLine, 1))
    		self.w.line2 = vanilla.HorizontalLine((columnLine + 40, int(linePos+12), -margin, 1))
    		linePos += lineHeight
    
    
    		# ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท
    
    		# Frame duration title
    		self.w.frameDurationTitle = vanilla.TextBox((margin, linePos+2, 200, lineHeight), "Set frame duration:", sizeStyle = "small") 
    		
    		# Enable show nodes
    		self.w.showNodes = vanilla.CheckBox((columnLine + 40, linePos-2, -margin, lineHeight), "Show nodes", value = False, sizeStyle = "small")
    		#print(self.w.showNodes.get())
    		
    		self.w.showNodes.getNSButton().setToolTip_("If enabled, it will show the nodes and handles instead of a filled version of the characters.")
    		
    		
    		
    		# Frame duration edit
    		linePos += lineHeight
    		self.w.frameTextEdit = vanilla.TextBox((margin*3, linePos+2, 100, lineHeight), "Fps", sizeStyle = "small") 
    		self.w.frameDurationUser = vanilla.EditText((110, linePos, 50, 20), "10", sizeStyle = "small")
    
    
    		# ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท
    
    
    		linePos += lineHeight
    		self.w.line3 = vanilla.HorizontalLine((margin, int(linePos+15), -margin, 1))
    		linePos += lineHeight
    
    
    		# ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท
    		
    		# Saving file format
    		self.w.savingFormatTitle = vanilla.TextBox((margin, int(linePos+10), columnLine, lineHeight), "Select the file format:", sizeStyle = "small")
    		
    		self.w.popUpFormat = vanilla.PopUpButton((columnLine+10, linePos+7, -margin, 20),
                                  [".gif", ".mov", ".jpg", ".pdf"])
    
    		
    		# ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท
    		
    		
    		linePos += lineHeight * 3
    		
    		
    		# ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท
    		# Generate button
    		
    		self.w.generateAnimation = vanilla.Button((margin, linePos, -margin, lineHeight), "Generate animation", sizeStyle = "small", callback = self.generateAnimation)
    		
    
    	# Test button	
    	def testButton( self, sender ):
    		
    		print("Test")		
    		
    
    	# Main button used to generate the animation
    	def generateAnimation( self, sender ):
    		print("Animation!")
    		
    		# ---------------------
    		# Variables
    		# ---------------------
    		# Font object
    		f = Glyphs.font
    		#print(f.selectedLayers)
    
    
    		# Selection
    		selection = f.selection
    		#print(selection)
    
    		# Frame duration 
    		fDuration = int(self.w.frameDurationUser.get())
    		# Scale
    		scaleUser = 60
    		s = scaleUser/100
    
    		# Current master ID
    		masterID = f.selectedFontMaster.id
    
    		# Randomize master's index
    		#masterIndexRandom = random.randint(0,len(f.masters))
    		#print(masterIndexRandom)
    
    
    		# Get the selected layer
    		layer = Glyphs.font.selectedLayers[f.masterIndex]
    		    
    		# String
    		userString = f.selection
    
    		# Amount of frames
    		#frames = len(f.selection)
    
    		# Frame duration
    		#fDuration = self.w.frameTextEdit.get()
    
    
    		# Page format (pixels)
    		w, h = int(str(self.w.widthUser.get())), int(str(self.w.heightUser.get()))
    
    		# Set visible nodes
    		nodesActive = True 
    
    		# Background color (CMYK)
    		bgColor = [0, .90, .86, 0] # TR Red
    
    		# Lettershapes colours (CMYK)
    		fgColor = [0, 0, 0, .2]
    
    
    		# ---------------------
    		# Functions
    		# ---------------------
    
    		# Centered glyph
    		def centeredGlyph(layer, scaleFactor, pageWidth, pageHeight):
    		    
    		    with savedState():
    		        scale(scaleFactor)
    		        layerW = layer.bounds.size.width
    		        layerH = layer.bounds.size.height
    		        
    		    
    		        newOriginX = ( pageWidth - layerW ) / 2
    		        newOriginY = ( pageHeight - layerH ) / 2
    		    
    		    #print(newOriginX, newOriginY)
    		    
    		    return(newOriginX, newOriginY)
    
    
    		# Drawing the nodes of each character
    		def drawNodes(layer, diameter, color):
    
    			radius = diameter / 2
    
    			for j, path in enumerate(layer.paths):
    			    	#print(i, path)
    				
    			    	for i, node in enumerate(path.nodes):
    			    	    xNode, yNode = node.position
    			    	    if node.type == "offcurve":
    			    	        cmykFill(*bgColor)
    			    	        strokeWidth(1)
    			    	        stroke(1)
    			    	        oval(
    			    	            xNode - radius,
    			    	            yNode - radius,
    			    	            diameter,
    			    	            diameter
    			    	            )
    			    	        cmykFill(*fgColor)		    
    			    	    elif node.type == "curve":
    			    	        fill(1)
    			    	        oval(
    			    	            xNode - radius,
    			    	            yNode - radius,
    			    	            diameter,
    			    	            diameter
    			    	            )
    			    	        cmykFill(*fgColor)		    
    				        
    			    	        # Previous 3rd point
    			    	        p3X, p3Y = path.nodes[i-3].position
    				        
    			    	        # Previous 2nd point
    			    	        p2X, p2Y = path.nodes[i-2].position
    				        
    			    	        # Previous point
    			    	        p1X, p1Y = path.nodes[i-1].position
    				        
    			    	        stroke(1)
    			    	        line(
    			    	            (p3X, p3Y),
    			    	            (p2X, p2Y)
    			    	            )
    				            
    			    	        line(
    			    	            (p1X, p1Y),
    			    	            (xNode, yNode)
    			    	            )
    				        
    			    	        stroke(None)
    				        
    			    	    else:
    			    	        fill(1)
    			    	        oval(
    			    	            xNode - radius,
    			    	            yNode - radius,
    			    	            diameter,
    			    	            diameter
    			    	            )
    			    	        cmykFill(*fgColor)		    
    
    
    
    		for g in selection:
    		    
    			newPage(w, h)
    			frameDuration(1/fDuration)
    
    			# Background
    
    			cmykFill(*bgColor)
    			rect(0,0,w,h)
    			
    
    			layer = g.layers[f.masterIndex]
    			#print(layer.parent)
    			# Generating a new page/frame
    
    			#fill(1)
    			print(layer.parent.name)
    
    			
    			#print( layer.bounds.origin.x, layer.bounds.origin.y)
    
    
    			X, Y = centeredGlyph(layer, s, w, h)
    
    			with savedState():
    			    #oval(width()/2, height()/2, 20, 20)
    				scale(s, center =(width()/2, height()/2))
    
    				translate(X-layer.bounds.origin.x , Y- layer.bounds.origin.y + layer.bounds.size.height * .06)
    
    				
    
    				if self.w.showNodes.get() == 1:
    					drawNodes(layer, 10, (0))
    					strokeWidth(1)
    					cmykStroke(0,0,0,.2)
    					cmykFill(None)
    
    					drawPath(layer.completeBezierPath)
    					print("nodes")
    					#for c in layer.components:
    					    #drawPath(c.completeBezierPath)
    
    
    				elif self.w.showNodes.get() == 0:
    					cmykFill(0,0,0,.2)
    					strokeWidth(1)
    					cmykStroke(None)
    					drawPath(layer.completeBezierPath)
    					print("not-nodes")
    
    				else:
    					print("Nodes problem")    
    					pass
    
    
    		# ---------------------
    		# Telling Drawbot the drawing is done
    		endDrawing()
    
    
    		# Closing the window
    		self.w.close()
    
    		# ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท 
    		# Saving process 
    		# ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท
    
    		# -----------------------------------
    		# Accessing the directory of the file
    		fDirectory = os.path.dirname(f.filepath) # Only the directory
    		fName = os.path.basename(f.filepath) # Only the name
    		fNameParts = os.path.splitext(fName) # Split parts
    		fPath = f.filepath
    
    		# --------------------------------------
    		# Generating a text file with the report 
    		# Assigning a format
    		self.w.popUpFormat.get()
    		listOfFormats = [".gif", ".mov", ".jpg", ".pdf"]
    
    
    		NewfName = fNameParts[0] + ' - Glyphs animation%s' % (listOfFormats[self.w.popUpFormat.get()]) # Change extension
    		NewfPath = os.path.join(fDirectory, NewfName) # Change extension
    
    		# Saving the .pdf
    		saveImage(NewfPath)
    
    
    		# ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท 
    		# Glyphs' notification
    		# ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท
    		Glyphs.showNotification('Animation generator', 'Generated animation from selection in the current font')
    
    
    		# ---------------------
    		# Test
    		# ---------------------
    		print("Done")  
    
    
    
    
    # Calling the object
    singleGlyphsAnimation()
    

  • admin

    could you provide a simpler example... by eliminating all imports and functionality which is not default (like from GylphsApp)

    And every drawing should be wrapped in newDrawing() and endDrawing. You are doing this now at the top of the script (once).



  • Hi, Frederik. The point now is that it is using some functionalities I don't know how to translate into a more generic context using currentFont(), for instance. Now I'm using a function inside Glyph's API called completeBezierPathwhich draws what's inside a glyph cell. I've been making the script much simpler but still I have this small details. Is there something similar to this GLyphs function that I could use?


  • admin

    I only wanted to teach you how to narrow down the problem, by elimenating parts :), make the problem as small as possible and fix the smallest problems first.

    All the Glyhps callback are not the issue:

    When you draw physically: you would start a drawing by first taking some paper, picking up a pencil, put the pencil down and draw, finish the drawing by framing it and hanging it on the wall!

    Translating your code: you are drawing on the same paper over and over again, and only saving the image after you hang it to the wall... ๐Ÿ™‚

    I will let you try some more

    good luck!!


Log in to reply