Skip to content

Commit f8ccb3d

Browse files
committed
add option to add watermark on pdf export
1 parent 0de46e9 commit f8ccb3d

File tree

11 files changed

+134
-29
lines changed

11 files changed

+134
-29
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ exports/
2323
/lotemplate/unittest/files/content/debug.json
2424
/lotemplate/unittest/files/content/*.unittest.odt
2525
/lotemplate/unittest/files/content/*.unittest.pdf
26+
/lotemplate/unittest/files/content/*.unittest_html_*.png
2627
/venv
2728
/lotemplate/unittest/files/content/e89fbedb61af3994184da3e5340bd9e9-calc_variables.ods.json
2829
/output*.*

API/utils.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def start_soffice(workers,jsondir,maxt=60):
3232

3333

3434
def connexion():
35-
global my_lo
35+
global my_lo
3636
cnx= ot.randomConnexion(my_lo)
3737

3838
return cnx
@@ -178,7 +178,6 @@ def fill_file(directory: str, file: str, json, error_caught=False) -> Union[tupl
178178
:param error_caught: specify if an error was already caught
179179
:return: a json and optionally an int which represent the status code to return
180180
"""
181-
182181
if isinstance(json, list):
183182
json=json[0]
184183
print("####\nUsing a list of dict is DEPRECATED, you must directly send the dict.")
@@ -192,33 +191,32 @@ def fill_file(directory: str, file: str, json, error_caught=False) -> Union[tupl
192191
is_name_present = type(json.get("name")) is str
193192
is_variables_present = type(json.get("variables")) is dict
194193
is_page_break_present = type(json.get("page_break")) is bool
195-
194+
is_watermark_present = type(json.get("watermark")) is dict
196195
if (
197196
not is_name_present
198197
or not is_variables_present
199-
or ((length > 2 and not is_page_break_present) or (length > 3 and is_page_break_present))
200198
):
201199
return 415, error_sim(
202200
"JsonSyntaxError",
203201
'api_invalid_instance_syntax',
204202
"Each instance of the array in the json should be an object containing only 'name' - "
205-
"a non-empty string, 'variables' - a non-empty object, and, optionally, 'page_break' - "
206-
"a boolean.")
203+
"a non-empty string, 'variables' - a non-empty object, optionally, 'page_break' - "
204+
"a boolean and 'watermark' a json array.")
207205

208206
try:
209207
json_variables = ot.convert_to_datas_template(json["variables"])
210208
temp.search_error(json_variables)
211209
temp.fill(json["variables"])
212210
if json.get('page_break', False):
213211
temp.page_break()
214-
export_file=temp.export(json["name"],"exports")
212+
watermark=json.get("watermark",{})
213+
export_file=temp.export(json["name"],"exports",False,watermark)
215214
export_name=json["name"]
216215
except Exception as e:
217216
if 'export_name' in locals():
218217
return ( export_file,error_format(e))
219218
else:
220219
return ( "nofile",error_format(e))
221-
222220
return (export_file,send_file(export_file, export_name))
223221

224222
except Exception as e:

README.md

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,12 @@ The tool is written in Python and use a real LibreOffice headless to fill the te
5656
Table of content
5757
----------------
5858

59-
* [Principles](#installation)
59+
* [Principles](#principles)
6060
* [Quick Start](#quick_start)
6161
* [API and CLI Usage](#api-and-cli-usage)
6262
* [DOCX and ODT Template syntax and examples](#docx-and-odt-template-syntax)
6363
* [XLSX and ODS Template syntax and examples](#xlsx-and-ods-template-syntax)
64+
* [Add watermark when output in pdf](#watermark)
6465
* [Supported formats](#supported-formats)
6566
* [Doc for developpers of lotemplate](#doc-for-devs)
6667
* [Unsolvable problems](#unsolvable-problems)
@@ -242,8 +243,12 @@ optional arguments:
242243
-h, --help show this help message and exit
243244
--json_file JSON_FILE , -jf JSON_FILE
244245
Json files that must fill the template, if any
245-
--json JSON , -j JSON
246+
--json JSON , -j JSON
246247
Json strings that must fill the template, if any
248+
--json_watermark_file JSON_FILE, -jwf JSON_FILE
249+
Json files to configure pdf watermark, if any
250+
--json_watermark JSON , -jw JSON
251+
Json strings to configure pdf watermark, if any
247252
--output OUTPUT, -o OUTPUT
248253
Names of the filled files, if the template should
249254
be filled. supported formats: pdf, html, docx, png, odt
@@ -943,6 +948,51 @@ The principle is exactly the same as for LibreOffice Calc exept for the creation
943948

944949
(sorry, my excel is in french...)
945950

951+
<a name="watermark">Add watermark when output in pdf
952+
953+
You can insert a watermark text in the page background when you are exporting in pdf.
954+
955+
### Possible parameters
956+
957+
| Name | Description | DefaultValue |
958+
|--------------------|----------------------------------------------------------------------------------------|--------------|
959+
| Watermark | Specifies the text for a watermark to be drawn on every page of the exported PDF file. | empty |
960+
| WatermarkColorRGB | Specifies the color of the watermark text in RGB | 236,236,236 |
961+
| WatermarkFontHeight| Specifies the font height of the watermark text. | |
962+
| WatermarkRotateAngle | Specifies angle of the watermark text in 1/10 degree. | 450 |
963+
| WatermarkFontName | Specifies font name of the watermark text. | Helvetica |
964+
| TiledWatermark | Specifies the tiled watermark text. | |
965+
966+
967+
### Cli example
968+
969+
create a file named watermark.json
970+
971+
```json
972+
{
973+
"Watermark": "ttottoto",
974+
"WatermarkColorRGB" : "236,0,0"
975+
}
976+
977+
```
978+
then you can used the cli like this
979+
980+
```bash
981+
./lotemplate_cli.py --template_file lotemplate/unittest/files/templates/text_vars.odt --json_file lotemplate/unittest/files/jsons/text_vars_valid.json --output toto.pdf -jwf 'watermark.json'
982+
983+
```
984+
985+
986+
### API example
987+
988+
989+
```bash
990+
991+
# generate a file titi.pdf from a template and a json content
992+
curl -X POST -H 'secretkey: DEFAULT_KEY' -H 'Content-Type: application/json' -d '{"name":"my_file.pdf","variables":{"my_tag":{"type":"text","value":"foo"},"other_tag":{"type":"text","value":"bar"}},"watermark": {"Watermark": "It s a draft","WatermarkColorRGB" : "236,200,0"}}' --output titi.pdf http://localhost:8000/test_dir1/test.odt
993+
994+
```
995+
946996
<a name="supported-formats"></a>Supported formats
947997
-------------------------------------------------
948998

@@ -1043,5 +1093,5 @@ For Pyuno
10431093
- [JODConverter wiki for list formats compatibles with LibreOffice](https://github.com/sbraconnier/jodconverter/wiki/Getting-Started)
10441094
- [The unoconv source code, written in python with PyUNO](https://github.com/unoconv/unoconv/blob/master/unoconv)
10451095
- [Unoconv source code for list formats - and properties - compatible with LibreOffice for export](https://github.com/unoconv/unoconv/blob/94161ec11ef583418a829fca188c3a878567ed84/unoconv#L391)
1046-
1096+
- [convert color RGB to long color use for LibreOffice](https://help.libreoffice.org/latest/fr/text/sbasic/shared/03010305.html)
10471097

lotemplate/Template.py

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,29 @@
1313
from typing import Union
1414
from sorcery import dict_of
1515
import unohelper
16+
import uno
1617
from com.sun.star.beans import PropertyValue
1718
from com.sun.star.io import IOException
1819
from com.sun.star.lang import IllegalArgumentException, DisposedException
1920
from com.sun.star.uno import RuntimeException
20-
2121
from . import errors
2222
#from . import Connexion
23-
from .utils import get_file_url,get_cached_json
23+
from .utils import get_file_url,get_cached_json
2424

2525

2626
import uuid
2727
import shutil
2828
import json
2929

30+
def dict_to_property(values, uno_any=False):
31+
ps = tuple([PropertyValue(Name=n, Value=v) for n, v in values.items()])
32+
if uno_any:
33+
ps = uno.Any('[]com.sun.star.beans.PropertyValue', ps)
34+
return ps
35+
36+
def rgb_to_long(rgb:tuple):
37+
return int(rgb[0])*65536+int(rgb[1])*256+int(rgb[2])
38+
3039
class Template:
3140

3241
TMPDIR='/tmp'
@@ -205,7 +214,9 @@ def close(self) -> None:
205214
except FileNotFoundError:
206215
pass
207216

208-
def export(self, filename: str, dirname=None, no_uid=None ) -> Union[str, None]:
217+
218+
219+
def export(self, filename: str, dirname=None, no_uid=None, my_filter_data=None ) -> Union[str, None]:
209220
"""
210221
Exports the newly generated document, if any.
211222
@@ -220,10 +231,27 @@ def export(self, filename: str, dirname=None, no_uid=None ) -> Union[str, None]
220231
path = os.getcwd() + "/" + dirname + '/' + str(uuid.uuid4()) +filename
221232
url = unohelper.systemPathToFileUrl(path)
222233

234+
235+
filter_data = {
236+
#'EncryptFile': True,
237+
#'DocumentOpenPassword': 'letmein',
238+
'Watermark' : '',
239+
'WatermarkColorRGB' : '236,236,236',
240+
#'WatermarkColor' : 11316396,
241+
'WatermarkRotateAngle' : 450,
242+
'WatermarkFontName' : 'Helvetica',
243+
}
244+
if isinstance(my_filter_data,dict) and bool(my_filter_data) and self.formats[file_type].endswith("pdf_Export"):
245+
filter_data.update(my_filter_data)
246+
filter_data['WatermarkColor']=rgb_to_long(tuple(filter_data['WatermarkColorRGB'].split(",")))
247+
248+
prop_filter_data = dict_to_property(filter_data, True)
223249
# list of available convert filters
224250
# cf https://help.libreoffice.org/latest/he/text/shared/guide/convertfilters.html
225251
try:
226-
self.doc.storeToURL(url, (PropertyValue("FilterName", 0, self.formats[file_type], 0),))
252+
self.doc.storeToURL(url, (PropertyValue("FilterName", 0,self.formats[file_type], 0),
253+
PropertyValue("FilterData", 0,prop_filter_data, 0)),
254+
)
227255
except KeyError:
228256
self.close()
229257
raise errors.ExportError('invalid_format',
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
jeanjhtr rah oeeeeee erg1 aejean 2zhefh qsg$ $ test
2+
my_good_watermark
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"aerhh": {"type": "text", "value": "1"},
3+
"h": {"type": "text", "value": "zhefh"},
4+
"rh": {"type": "text", "value": "jean"},
5+
"gjerg": {"type": "text", "value": "jhtr"},
6+
"aet": {"type": "text", "value": "2"},
7+
"jean": {"type": "text", "value": "rah oeeeeee"}
8+
}
8.79 KB
Binary file not shown.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"Watermark": "my_good_watermark","WatermarkColorRGB" : "236,200,0"}

lotemplate/unittest/test_content.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ def test_counter(self):
6161
def test_text_var_in_header(self):
6262
self.assertTrue(compare_files('text_var_in_header', 'pdf',cnx=cnx))
6363

64+
def test_text_watermark(self):
65+
self.assertTrue(compare_files('text_watermark','pdf',cnx=cnx,watermark=True))
66+
6467
def test_too_many_endif_strange(self):
6568
self.assertTrue(compare_files('too_many_endif_strange',cnx=cnx))
6669

lotemplate/unittest/test_function.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ def get_filename(ext: str):
106106

107107

108108

109-
def compare_files(name: str, format: str = 'txt',cnx = None):
109+
def compare_files(name: str, format: str = 'txt',cnx = None, watermark = False):
110110
if format not in ['txt', 'pdf']:
111111
return False
112112

@@ -126,18 +126,21 @@ def get_filename(ext: str):
126126
return True
127127
else:
128128
raise FileNotFoundError('No file found for ' + name)
129-
130129
temp.scan()
131130
temp.search_error(to_data(get_filename('json')))
132131
temp.fill(file_to_dict(get_filename('json')))
132+
if watermark:
133+
watermark_data=file_to_dict(get_filename('wk.json'))
134+
else:
135+
watermark_data={}
133136

134137
if os.path.isfile(get_filename('unittest.'+format)):
135138
os.remove(get_filename('unittest.'+format))
136-
temp.export(name+'.unittest.'+format,base_path, True)
139+
temp.export(name+'.unittest.'+format,base_path, True,watermark_data)
137140
# temp.close()
138141
if os.path.isfile(get_filename('unittest.odt')):
139142
os.remove(get_filename('unittest.odt'))
140-
temp.export(name+'.unittest.odt',base_path, True)
143+
temp.export(name+'.unittest.odt',base_path, True, watermark_data)
141144
temp.close()
142145

143146
# The PDF format is used to test some documents with headers or footers that are not supported by the text saveAs from

0 commit comments

Comments
 (0)