You should see a pencil besides your choosen title.
It allows also changing tags.
.
I added python and pdf for you.
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 ?
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
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
Indeed. RTFM, in other words…
Just hoping someone had done this task already and save my valuable time!
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?!
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
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«
edit: working python, based on: https://ask.libreoffice.org/t/using-the-sort-method-with-python/53535/2
from pathlib import Path
from com.sun.star.beans import PropertyValue as pv
pages = pv(Name="PageRange", Value="2-4")
pages = uno.Any( f"[]{pages.typeName}", (pages,)) #that solves the issue!
filter_name = pv(Name="FilterName", Value="writer_pdf_Export")
filter_data = pv(Name="FilterData", Value=pages )
doc = XSCRIPTCONTEXT.getDocument()
uri = (Path.home() / "PDF" / "test.pdf").as_uri()
doc.storeToURL(uri, ( filter_name, filter_data))
Yay, your code works, including the out of order page selection! Thank you!!
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…
the »arrays« are »tuple«s in this context:
print( type( ( filter_name, filter_data)))
⇒ <class tuple>
Oh sorry, I deleted my comment…
Oh, yes, I see you made up the tuple in the call to storeToURL()
Cf. this implementation for Basic: getUnoTypeForSbxValue
(note the comment telling “If all elements of the arrays are from the same type, take this one - otherwise the whole will be considered as Any-Sequence” - it checks the types of the Basic array first, and then decided which type the sequence
elements must be)
to the Python-specific implementation: pyObject2Any
which takes either a tuple, or a list, and converts them to a sequence
of any
unconditionally.
The code in the core, that eventually gets the resulting sequence
(itself packed into an any
), will attempt to extract it into a sequence
of PropertyValue
- and will fail. Which is handled silently in this case.
So yes, as I mentioned elsewhere, it’s a bug in the implementation … and someone could want to file a bug report.
mikekaganski I’m pretty fluent in C++, but I’ve kind of tried to avoid reading source for a while now.
.
I just want to say a sincere thank you for the help you and others have offered. I really do appreciate it! I may well revisit this topic at some time, but I’ve found a way to meet my requirements using pdftk and bash.
.
In case you’re interested, I write with markdown, which allows me to use git for version control. I convert my manuscripts to .odt using pandoc, then print to pdf using libreoffice and a style template. Usually, I just leave it at that, and have a live preview with my pdf viewer. My goal here is to rearrange the pages into signatures, so I can bind them into book form. I’m involved in the book’s design, so this is a way to make a dummy.
.
Sorry I annoyed you. Wasn’t intentional!
This thread shows a way to create a strictly-typed UNO value:
So instead of
filter_data = pv(Name="FilterData", Value=(pages,) )
you may want to experiment with something like
filter_data = pv(Name="FilterData", Value=uno.Any('[]com.sun.star.beans.PropertyValue', (pages,) ))
It works for me, wе just need to add (if not)
import uno
and fix the typo uno:Any
to uno.Any
.
Thanks to @jimk for the idea!