SDK/python : selecting "Print to File..." as printer from API may be fraught with difficulty?

Continuing from this question, could anyone give a quick pointer how to print to a pdf rather than a paper printer.

In essence:

  • What is the correct printer name for “Print to File…”?
  • What property specifies the output filename?

I’m being a bit lazy but, if somebody can answer this easily, it would save me a lot of hassle. In particular, if the printer name is invalid, libreoffice defaults to the default, and prints on paper, which is wasting paper and toner!

TIA!

API/Tutorials/PDF export - The Document Foundation Wiki

or Calc : How can I export a PDF in a Python Macro?

Thanks, but export to pdf doesn’t seem to work correctly for a selection of pages. I need them out of order, eg ‘3,5,1’ and export to pdf produces ‘1,3’ in response.

Thanks for the link though! The tutorials are much appreciated!

would make sense to update the title of your topic then (as well as tags).

pypdftk

Didn’t see a way to add a new tag…
.
NB: out of order selection works fine with a physical printer, and with ‘Print to File…’ from the gui. All I need is to select the pdf printer from the API.

this one might be more acurate with FilterData : How to export first 100 pages of XLS/DOC/PPT to PDF Using LibreOffice - #3 by PYS

just tested with :

    filterProps(0).Name = "PageRange"
    filterProps(0).Value = "3,5,1"

got

Thanks! Tried this but got:

com.sun.star.task.ErrorCodeIOException: SfxBaseModel::impl_store <file:///home/sean/Desktop/00-WRITING/testLibreSDK/test.pdf> failed: 0x81a(Error Area:Io Class:Parameter Code:26) at ./sfx2/source/doc/sfxbasemodel.cxx:3207

Will keep experimenting… :slight_smile:

Could you maybe share your test code?

You should see a pencil besides your choosen title.
It allows also changing tags.
.
I added python and pdf for you.

1 Like

I tried export ‘3,5,1’ from the gui, and it worked this time. Not sure if it was my error but, if this is reliable, it might be sufficient for my needs for a while!

again ? :thinking:

Ahah, I see you’ve used storeToURL() and not storeAsURL()… the difference between save as and convert, I suspect…

Anyway, fossicking around on the web I saw the brilliant suggestion to record a macro doing the required task, then edit the resulting code. Brilliant! (Note: the macro recording feature has to be explicitly enabled in Tools → Options → LibreOffice → Advanced)

I created a macro, exporting pages 1,5,3 to a pdf.

The file generated while recording had the correct pages in the correct order, but running the macro failed, exporting all the pages.

If I look at the recorded macro source, it uses the same setting I have:
("PageRange",0,"1,5,3",com.sun.star.beans.PropertyState.DIRECT_VALUE)

…so I assume this capability is broken on my installation.

I’m going to try the same approach to find out how to print to file, my original strategy.

I have high hopes, altho I’m a bit traumatised by the Basic code, which I was briefly exposed to in the mid 90s…

reworked python with thirdparty-package linked by @fpy

from pathlib import Path
import pypdftk
from com.sun.star.beans import PropertyValue as pv

filter_name = pv(Name="FilterName", Value="writer_pdf_Export")

doc = XSCRIPTCONTEXT.getDocument()

p = Path.home() / "PDF" / "test.pdf"
p2 = p.with_name("excerpt.pdf")
uri = p.as_uri()
doc.storeToURL(uri, ( filter_name,)) # full export to pdf

pages = [[3], [5,7], [9]] # page 3 , 5 to 7 and 9

pypdftk.get_pages(p, pages , p2) # stores to p2 # excerpt from above
2 Likes

Thanks! This looks good. I can probably abandon my attempts to program libreoffice and use this separately. Of course, I’ll still use libreoffice headless to generate the source pdf, but I’m doing that already.
.
pdftk does exactly what I want, and it’s in the apt repo.
.
pdftk pages.1-8.pdf burst

pdftk pg_0001.pdf pg_0005.pdf pg_0003.pdf cat output newfile.pdf
.
Apologies to all if my questions have seemed unfocused and my patience thin. It just seemed programming libreoffice could be an elegant solution for my requirement.
.
As it turns out, answers provided here have guided me to the best solution (pdftk) but I’ve also learned a lot about libreoffice scripts and macros, and will likely experiment with them more when I have time!
.
Thanks again!
@bitrat

If you read my answer in the question you referred to as the start of this, you see a link to the API documentation for the print method. It has a reference to PrintOptions. IMO, that page describes the available options in sufficient details.

If you read it, and your testing doesn’t succeed, you may consider to provide some information, like the code you tried, the specific error you see, your OS, LibreOffice version, and so on - anything that allows answerer to not feel like doing your homework for you :wink:

1 Like

Indeed. RTFM, in other words…

Just hoping someone had done this task already and save my valuable time! :slight_smile:

I modified the following, using code from @fpy


# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
#
# This file is part of the LibreOffice project.
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#

import argparse
import sys
from os.path import basename, abspath

import uno
import unohelper
from com.sun.star.beans import PropertyValue
from com.sun.star.connection import NoConnectException

"""
The purpose of this example is to open a specified text document and save this
file to a specified URL. The type of the saved file is "writer8".
"""

PROG = "$OFFICE_PROGRAM_PATH/python {}".format(basename(sys.argv[0]))
SOFFICE_CONNECTION_URI = "uno:socket,host=localhost,port=54321;urp;StarOffice.ComponentContext"

def connect_soffice():
    """Connect to remote running LibreOffice

    :return: an object representing the remote LibreOffice instance.
    """
    local_context = uno.getComponentContext()
    resolver = local_context.ServiceManager.createInstanceWithContext(
        "com.sun.star.bridge.UnoUrlResolver", local_context
    )
    try:
        remote_context = resolver.resolve(SOFFICE_CONNECTION_URI)
    except NoConnectException:
        raise Exception("Cannot establish a connection to LibreOffice.")

    return remote_context.ServiceManager.createInstanceWithContext(
        "com.sun.star.frame.Desktop", remote_context
    )

def save_doc(src, dest, pages):

    src_url = "file://{}".format(abspath(src)).replace("\\", "/")
    dest_url = "file://{}".format(abspath(dest)).replace("\\", "/")

    soffice = connect_soffice()

    doc = soffice.loadComponentFromURL(
        src_url, "_blank", 0, (PropertyValue(Name="Hidden", Value=True),)
    )
    
    doc = soffice.loadComponentFromURL(src_url, "_blank", 0, tuple([]))
    
    filterProps = PropertyValue(Name="PageRange", Value=pages)

    save_opts = (
        PropertyValue(Name="Overwrite", Value=True),
        PropertyValue(Name="FilterName", Value="writer_pdf_Export"),
        PropertyValue(Name="FilterData", Value=filterProps),
    )
       
    try:       
        doc.storeAsURL(dest_url, save_opts)
        print("Document", src, "saved under", dest)
        
    finally:
        doc.dispose()
        print("Document closed!")

def main():
    parser = argparse.ArgumentParser(description="Document Saver", prog=PROG)
    parser.add_argument("src", help="Path to a Word document to be saved, e.g. path/to/hello.odt")
    parser.add_argument("dest", help="Save the document to here, e.g. path/to/hello.pdf")
    parser.add_argument("pages", help="Page range to be printed, e.g. 1-3")

    args = parser.parse_args()
    save_doc(args.src, args.dest, args.pages)
    
if __name__ == "__main__":
    main()

This fails with ErrorCodeIOException (but see comment below).


Version: 7.4.7.2 / LibreOffice Community
Build ID: 40(Build:2)
CPU threads: 8; OS: Linux 6.1; UI render: default; VCL: gtk3
Locale: en-NZ (en_NZ.UTF-8); UI: en-US
Debian package version: 4:7.4.7-1+deb12u8
Calc: threaded

UPDATE

I got the above code to work (as per here) by changing …
.
doc.storeAsURL(dest_url, save_opts)
to
doc.storeToURL(dest_url, save_opts)
.
Unfortunately, the code ignores the supplied page range and saves all pages.

Perhaps its a bug, or perhaps something is missing?! :crazy_face:

Without testing:
Your filterProps is a single ProperyValue, I think you need an array of ProperyValues, even for a single item, like in your save_opts

1 Like

Good suggestion… but still the same. I will persevere!

Did a simplyfied test with a 13page writer-doc
this BASIC-code works:

Sub Main

dim pages(0) as new com.sun.star.beans.PropertyValue
dim props(1) as new com.sun.star.beans.PropertyValue


pages(0).Name="PageRange"
pages(0).Value="2-4"

props(0).Name= "FilterName"
props(0).Value= "writer_pdf_Export"
props(1).Name= "FilterData"
props(1).Value=pages()

doc = ThisComponent

uri = convertToURL("/home/wertie/PDF/test.pdf")
doc.storeToURL(uri,  props())


End Sub

Unfortunatly the following python runs without Errors, but IGNORES the »PageRange«

from pathlib import Path
from com.sun.star.beans import PropertyValue as pv

pages = pv(Name="PageRange", Value="2-4")
filter_name = pv(Name="FilterName", Value="writer_pdf_Export")
filter_data = pv(Name="FilterData", Value=pages )
#filter_data = pv(Name="FilterData", Value=(pages,) ) # same output

doc = XSCRIPTCONTEXT.getDocument()

uri = (Path.home() / "PDF" / "test.pdf").as_uri()
doc.storeToURL(uri, ( filter_name, filter_data))

1 Like

Yay, your code works, including the out of order page selection! Thank you!! :trophy:

I wonder if there’s an error in the python wrapper? Anyway, I’ll happily suffer Basic coding to have this working. Haven’t tried it as a script with headless yet, but I’m hopeful.

Just one question: how would I run this Basic as an external script, connected to LibreOffice server?

I presume there’s a Basic interpreter attached to the LibreOffice install somewhere… Does it have a repl?

Another option I just saw online was to use python to call the macro remotely…