Skip to content

Commit d78c69d

Browse files
SeqLazsuricactus
authored andcommitted
Adding the functionality to embed the symbology raster and SVG to the
project.
1 parent 70562dd commit d78c69d

File tree

2 files changed

+98
-1
lines changed

2 files changed

+98
-1
lines changed

Diff for: libqfieldsync/offline_converter.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,11 @@
5151
from .layer import LayerSource, SyncAction
5252
from .offliners import BaseOffliner
5353
from .project import ProjectConfiguration, ProjectProperties
54-
from .utils.file_utils import copy_attachments, copy_multifile
54+
from .utils.file_utils import (
55+
copy_attachments,
56+
copy_multifile,
57+
embed_layer_symbols_on_project,
58+
)
5559
from .utils.logger import logger
5660
from .utils.qgis import make_temp_qgis_file, open_project
5761
from .utils.xml import get_themapcanvas
@@ -302,6 +306,9 @@ def _convert(self, project: QgsProject) -> None:
302306
elif layer_action == SyncAction.REMOVE:
303307
project.removeMapLayer(layer)
304308

309+
# Change symbol path to embedded marker
310+
embed_layer_symbols_on_project(layer)
311+
305312
self.remove_empty_groups_from_layer_tree_group(project.layerTreeRoot())
306313

307314
export_project_filename = self._export_filename

Diff for: libqfieldsync/utils/file_utils.py

+90
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
***************************************************************************/
2020
"""
2121

22+
import base64
2223
import hashlib
2324
import os
2425
import platform
@@ -29,6 +30,16 @@
2930
from pathlib import Path
3031
from typing import List, Optional, Tuple, Union
3132

33+
from qgis.core import (
34+
QgsCategorizedSymbolRenderer,
35+
QgsRasterMarkerSymbolLayer,
36+
QgsRuleBasedRenderer,
37+
QgsSingleSymbolRenderer,
38+
QgsSvgMarkerSymbolLayer,
39+
QgsSymbol,
40+
QgsVectorLayer,
41+
QgsWkbTypes,
42+
)
3243
from qgis.PyQt.QtCore import QCoreApplication
3344

3445
from .exceptions import NoProjectFoundError, QFieldSyncError
@@ -216,3 +227,82 @@ def is_valid_filepath(path: str) -> bool:
216227
return False
217228

218229
return True
230+
231+
232+
def update_symbols_to_embedded(symbol: QgsSymbol) -> None:
233+
"""
234+
Update SVG or Raster symbols layer to embed it in the QGIS project.
235+
Args:
236+
symbol: The QGIS symbol (from a renderer).
237+
"""
238+
if symbol is None:
239+
return
240+
241+
for symbol_layer in symbol.symbolLayers():
242+
# Filter out only symbology that includes SVG and Raster, skip the rest
243+
if not isinstance(
244+
symbol_layer, (QgsSvgMarkerSymbolLayer, QgsRasterMarkerSymbolLayer)
245+
):
246+
continue
247+
248+
source_path = Path(symbol_layer.path())
249+
250+
# Check if symbol is already embedded
251+
if str(source_path)[:8].startswith("base64:"):
252+
continue
253+
254+
# The symbol is already broken; its file is not reachable
255+
if not source_path.is_file():
256+
continue
257+
258+
with open(source_path, "rb") as file:
259+
file_data = file.read()
260+
encoded_data = base64.b64encode(file_data).decode()
261+
symbol_layer.setPath(f"base64:{encoded_data}")
262+
263+
264+
def embed_layer_symbols_on_project(layer: QgsVectorLayer) -> None:
265+
"""
266+
Update the paths of symbols to embedded symbols in the QGIS project.
267+
268+
Args:
269+
layer: The QgsVectorLayer to update. The layer is a point layer.
270+
"""
271+
272+
if (
273+
not isinstance(layer, QgsVectorLayer)
274+
or layer.geometryType() != QgsWkbTypes.PointGeometry
275+
):
276+
return
277+
278+
renderer = layer.renderer()
279+
280+
if not renderer:
281+
return
282+
283+
if isinstance(renderer, QgsSingleSymbolRenderer):
284+
symbol = renderer.symbol()
285+
if symbol:
286+
update_symbols_to_embedded(symbol=symbol)
287+
288+
elif isinstance(renderer, QgsRuleBasedRenderer):
289+
for rule in renderer.rootRule().children():
290+
symbols = rule.symbols()
291+
292+
if not symbols:
293+
continue
294+
295+
for symbol in symbols:
296+
update_symbols_to_embedded(symbol=symbol)
297+
298+
elif isinstance(renderer, QgsCategorizedSymbolRenderer):
299+
categories = renderer.categories()
300+
if categories:
301+
for index in range(len(categories)):
302+
# Get a fresh category. The renderer doesn't update in-place modifications.
303+
category = renderer.categories()[index]
304+
symbol = category.symbol().clone()
305+
update_symbols_to_embedded(symbol=symbol)
306+
renderer.updateCategorySymbol(index, symbol)
307+
308+
layer.setRenderer(renderer)

0 commit comments

Comments
 (0)