Inkscape.org
Creating New Extensions Export/Print current view as PDF
  1. #1
    Dabrush Dabrush @Dabrush

    Hello, I have started today at making a small extension for myself to help with a repetitive task I have to do from time to time:

    Moving a selected object by a certain amount of pixels and printing to pdf. This is used to get full pdf prints of objects that are larger than one page.

    Generally the moving and cycling through seems to work fine, but I just can't get the export to PDF to work and I am seriously struggling with documentation I either can't find or solutions that don't seem to work anymore.

    For the export to PDF I am trying to use this line (where file_path grabs the currently opened temporary file)

    subprocess.call(['inkscape', '-f', str(file_path), '--export-pdf', save_path])

    In case that the issue might be somewhere else I will add the rest of my code.
    This is the first thing I've coded in years and the first thing in python ever, so please forgive the numerous inefficiencies and other issues. 

    #!/usr/bin/env python
    
    import subprocess
    import inkex
    from inkex.transforms import Transform
    
    class MoveAndPrint(inkex.EffectExtension):
    	def add_arguments(self, pars):
    		pars.add_argument(
    			"--pages_x",
    			type=int,
    			help="Number of horizontal pages",
    		)
    		pars.add_argument(
    			"--pages_y",
    			type=int,
    			help="Number of vertical pages",
    		)
    		pars.add_argument(
    			"--offset_x",
    			type=int,
    			help="Number of segments to divide the path into",
    		)
    		pars.add_argument(
    			"--offset_y",
    			type=int,
    			help="Number of segments to divide the path into",
    		)
    		pars.add_argument(
    			"--output_path",
    			help="Number of segments to divide the path into",
    		)
    		pars.add_argument("--tab")
    
    	def effect(self):
    	
    		x_shift = self.options.offset_x
    		y_shift = self.options.offset_y
    		pdf_path = self.options.output_path
    		pages_x = self.options.pages_x
    		pages_y = self.options.pages_y
    		selected_items = self.svg.selected
    		base_x = ""
    		base_y = ""
    		
    		for elem in selected_items:
    			bbox = elem.bounding_box()
    			base_x = elem.left
    			base_y = elem.top
    		
    		for page_x in range(pages_x):
    			for page_y in range(pages_y):
    				for item in selected_items:
    					inkex.debug("Page " + str(page_x) + " " + str(page_y))
    					item.set('x', str(page_x * x_shift))
    					item.set('y', str(page_y * y_shift))
    					
    					file_path = self.options.input_file
    					inkex.debug("File Path: " + str(file_path))
    					save_path = pdf_path + str(page_x) + str(page_y) + ".pdf"
    					inkex.debug("PDF Path: " + save_path)
    					if save_path:
    						subprocess.call(['inkscape', '-f', str(file_path), '--export-pdf', save_path])
    
    
    if __name__ == "__main__":
    	MoveAndPrint().run()

    and the corresponding inx file:

    <?xml version="1.0" encoding="UTF-8"?>
    <inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
        <name>Move And Print</name>
        <id>org.test.effect.moveandprint</id>
        <param name="tab" type="notebook">
            <page name="Options" gui-text="Options">
                <param name="pages_x" type="int" gui-text="Pages Horizontal:">pages_x</param>
                <param name="pages_y" type="int" gui-text="Pages Vertical:">pages_y</param>
                <param name="offset_x" type="int" max="100000" gui-text="Offset Horizontal:">offset_x</param>
                <param name="offset_y" type="int" max="100000" gui-text="Offset Vertical:">offset_y</param>
    			<param name="output_path" type="string" gui-text="Output Path:">output_path</param>
            </page>
        </param>
        <effect>
    		<object-type>all</object-type>
            <effects-menu>
                <submenu name="Moveit"/>
            </effects-menu>
        </effect>
        <script>
            <command location="inx" interpreter="python">moveandprint.py</command>
        </script>
    </inkscape-extension>
    

    I hope someone can help me with this or point me in the right direction.

  2. #2
    inklinea inklinea @inklinea⛰️

    maybe something like this : 

    # Make a temp folder to save changes to self.svg
    import tempfile, os
    temp_folder = tempfile.mkdtemp()
    # Attach reference to temp folder to self.
    MoveAndPrint.temp_folder = temp_folder
    
    for page_x in range(pages_x):
       for page_y in range(pages_y):
          for item in selected_items:
             inkex.debug("Page " + str(page_x) + " " + str(page_y))
             item.set('x', str(page_x * x_shift))
             item.set('y', str(page_y * y_shift))
             
             file_path = self.options.input_file
             inkex.debug("File Path: " + str(file_path))
             save_path = pdf_path + str(page_x) + str(page_y) + ".pdf"
             inkex.debug("PDF Path: " + save_path)
             if save_path:
                # Use uuid to generate a long unique string for temp filename
                from uuid import uuid4
                current_svg_temp_filepath = os.path.join(temp_folder, f'{str(uuid4())}.svg')
                # Let us save the current state of the svg to the temp folder
                with open(current_svg_temp_filepath, 'wb') as output_file:
                   self.save(output_file)
                my_actions = '--actions='
                export_pdf_actions = my_actions + f'export-filename:{save_path};export-do;'
                inkex.command.inkscape(current_svg_temp_filepath, export_pdf_actions)
    
    # Cleanup temp folder
    import shutil
    if hasattr(self, 'temp_folder'):
       shutil.rmtree(self.temp_folder)
  3. #3
    inklinea inklinea @inklinea⛰️

    Import statements should be at the top - but I put them before the relevant code so you can see them. 

    self.options.input_file does not change while the extension is running so you do have to save the self.svg if you want to use the current state of the svg with the command line.

     

  4. #4
    Dabrush Dabrush @Dabrush

    Thank you for the advice. I can see the temp folder and saving working, and it seems to properly loop, but sadly I still don't get any PDF output with this.

    Do you have any idea left how I could debug this? If scaling is preserved, in the worst case I can also just convert the SVGs to PDF in batch afterwards, but I doubt this can be done without issues.

  5. #5
    inklinea inklinea @inklinea⛰️

    I should have mentioned a couple of things.

    The export_pdf_actions = my_actions + f'export-filename:{save_path};export-do;'

    that line can be edited to be any valid list of actions. However the only reason it will output to .pdf is because {save_path} string has a .pdf extension at the end.

    Also if you do not give a complete path for the output_file, the files will will dumped by default into the root folder where your .py extension file is.

    Have a look in there - you might see them :)

    Really you should use a file chooser:

    # Path param - for selecting making files / folders
    <param type="my_path" name="save_path" gui-text="File Save Path" mode="folder">None Selected</param>
    pars.add_argument("--my_path", type=str, dest="my_path")

    Probably better to use the mode="folder" as above, and use the string entry for the user to choose the base file name. Add an increment to the user string, and .svg

    then use os.path.join(my_path, base_filename_string) for the save filepath.

    Usually you would use some kind of try / except loop in case the user has navigated to a folder which cannot be saved in etc and use inkex.errormsg('Cannot save file') in the except block

     

  6. #6
    Dabrush Dabrush @Dabrush

    Sadly the files are also not present in the default location, but I suspect that the SVG would be good enough and to then convert them in batch with a different program.

    However my new issue I've found is that groups don't have left or top properties that I can use to find the location, and I suspect I also can't use "set" to move the group like that. Any idea here? I can convert the group to a path, but a path also doesn't have the properties I have wanted to use. It seems like my first try only really worked with the rectangle...

  7. #7
    inklinea inklinea @inklinea⛰️

    Yes, It's something that I was able to get away with in Linux, not in Windows. 

    Do you just want to move an element around a page by pixels and save each time ? - or do you want to involve Inkscape Pages too ? 

    If you just specify exactly what you want to perform, I will see if I can make an example

     

  8. #8
    Dabrush Dabrush @Dabrush

    Sorry for the late answer. What I was planning to do was basically a semi-automated version of this video: https://youtu.be/i2OFr1pVxuA

    In the prompt I query for horizontal and vertical pages, as well as pixel height and width of pages (which I screwed up since I didn't realize that inkscape also works with fractions of pixels).

    I have somewhat put this on the back burner now considering I found too many other issues in my code outside of saving. (But the pdf save really isn't necessary, since I can just convert the svgs in batch with PDF24 for example).