draftail-text-utils 0.1.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- draftail_text_utils/__init__.py +4 -0
- draftail_text_utils/apps.py +7 -0
- draftail_text_utils/conf.py +482 -0
- draftail_text_utils/migrations/__init__.py +0 -0
- draftail_text_utils/rich_text/__init__.py +0 -0
- draftail_text_utils/rich_text/base.py +267 -0
- draftail_text_utils/rich_text/font_family.py +61 -0
- draftail_text_utils/rich_text/font_size.py +94 -0
- draftail_text_utils/rich_text/highlight_color.py +99 -0
- draftail_text_utils/rich_text/text_alignment.py +70 -0
- draftail_text_utils/rich_text/text_color.py +95 -0
- draftail_text_utils/static/draftail_text_utils/css/draftail_text_utils.css +5 -0
- draftail_text_utils/static/draftail_text_utils/css/font_family.css +57 -0
- draftail_text_utils/static/draftail_text_utils/css/font_size.css +102 -0
- draftail_text_utils/static/draftail_text_utils/css/highlight_color.css +117 -0
- draftail_text_utils/static/draftail_text_utils/css/text_alignment.css +72 -0
- draftail_text_utils/static/draftail_text_utils/css/text_color.css +117 -0
- draftail_text_utils/static/draftail_text_utils/js/font_family.js +149 -0
- draftail_text_utils/static/draftail_text_utils/js/font_size.js +408 -0
- draftail_text_utils/static/draftail_text_utils/js/font_size_entity.js +39 -0
- draftail_text_utils/static/draftail_text_utils/js/highlight_color.js +350 -0
- draftail_text_utils/static/draftail_text_utils/js/highlight_color_entity.js +43 -0
- draftail_text_utils/static/draftail_text_utils/js/text_alignment.js +139 -0
- draftail_text_utils/static/draftail_text_utils/js/text_color.js +343 -0
- draftail_text_utils/static/draftail_text_utils/js/text_color_entity.js +42 -0
- draftail_text_utils/templates/draftail_text_utils/icons/align-center.svg +1 -0
- draftail_text_utils/templates/draftail_text_utils/icons/align-justify.svg +1 -0
- draftail_text_utils/templates/draftail_text_utils/icons/align-left.svg +1 -0
- draftail_text_utils/templates/draftail_text_utils/icons/align-right.svg +1 -0
- draftail_text_utils/templates/draftail_text_utils/icons/font.svg +1 -0
- draftail_text_utils/templates/draftail_text_utils/icons/highlighter.svg +1 -0
- draftail_text_utils/templates/draftail_text_utils/icons/palette.svg +1 -0
- draftail_text_utils/templates/draftail_text_utils/icons/text-height.svg +1 -0
- draftail_text_utils/templatetags/__init__.py +0 -0
- draftail_text_utils/templatetags/draftail_text_utils_tags.py +29 -0
- draftail_text_utils/wagtail_hooks.py +266 -0
- draftail_text_utils-0.1.7.dist-info/METADATA +63 -0
- draftail_text_utils-0.1.7.dist-info/RECORD +39 -0
- draftail_text_utils-0.1.7.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Configuration for draftail_text_utils via Django settings.
|
|
3
|
+
|
|
4
|
+
Usage in your Django settings:
|
|
5
|
+
|
|
6
|
+
DRAFTAIL_TEXT_UTILS = {
|
|
7
|
+
# ---- Font size ----
|
|
8
|
+
"FONT_SIZES": {
|
|
9
|
+
"MIN": 8,
|
|
10
|
+
"MAX": 100,
|
|
11
|
+
"STEP": 1,
|
|
12
|
+
"PRESETS": [8, 9, 10, 11, 12, 14, 16, 18, 20, 24, 30, 36, 48, 60, 72, 96],
|
|
13
|
+
},
|
|
14
|
+
# ---- Color palette ----
|
|
15
|
+
# Option A: point to an existing module with a DRAFTAIL_COLORS list
|
|
16
|
+
"COLORS": {
|
|
17
|
+
"MODULE": None, # e.g. "apps.core.models"
|
|
18
|
+
},
|
|
19
|
+
# Option B: provide an explicit palette (ignored if MODULE is set)
|
|
20
|
+
"COLOR_PALETTE": None, # falls back to built-in defaults
|
|
21
|
+
# ---- Font families ----
|
|
22
|
+
# Option A: point to an existing module with a DRAFTAIL_FONT_FAMILIES list
|
|
23
|
+
# or a SiteDesignSettings / TypographyBlock with font definitions.
|
|
24
|
+
"FONT_FAMILIES": {
|
|
25
|
+
"MODULE": None, # e.g. "apps.core.models"
|
|
26
|
+
},
|
|
27
|
+
# Option B: provide an explicit list (ignored if MODULE is set):
|
|
28
|
+
# [{"label": "Roboto", "value": "'Roboto', sans-serif"}, ...]
|
|
29
|
+
# ---- Font URL stylesheets ----
|
|
30
|
+
# Option A: point to an existing module with a DRAFTAIL_FONT_URLS list,
|
|
31
|
+
# or a TypographyBlock / SiteDesignSettings with a font_url field.
|
|
32
|
+
"FONT_URLS": {
|
|
33
|
+
"MODULE": None, # e.g. "apps.core.models"
|
|
34
|
+
},
|
|
35
|
+
# Option B: provide an explicit list of CSS URLs:
|
|
36
|
+
# ["https://fonts.bunny.net/css?family=...", ...]
|
|
37
|
+
# ---- Feature toggles ----
|
|
38
|
+
"FEATURES": {
|
|
39
|
+
"TEXT_COLOR": True,
|
|
40
|
+
"HIGHLIGHT_COLOR": True,
|
|
41
|
+
"FONT_FAMILY": True,
|
|
42
|
+
"FONT_SIZE": True,
|
|
43
|
+
"TEXT_ALIGNMENT": True,
|
|
44
|
+
},
|
|
45
|
+
}
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
import importlib
|
|
49
|
+
import logging
|
|
50
|
+
|
|
51
|
+
from django.conf import settings
|
|
52
|
+
from wagtailtraverse import traverse_value
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
logger = logging.getLogger(__name__)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
DEFAULT_FONT_SIZES = {
|
|
59
|
+
"MIN": 8,
|
|
60
|
+
"MAX": 100,
|
|
61
|
+
"STEP": 1,
|
|
62
|
+
"PRESETS": [8, 9, 10, 11, 12, 14, 16, 18, 20, 24, 30, 36, 48, 60, 72, 96],
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
DEFAULT_COLORS = [
|
|
66
|
+
{"key": "transparent", "label": "Transparent", "value": "#00000000"},
|
|
67
|
+
{"key": "black", "label": "Black", "value": "#000000"},
|
|
68
|
+
{"key": "white", "label": "White", "value": "#FFFFFF"},
|
|
69
|
+
{"key": "gray", "label": "Gray", "value": "#6B7280"},
|
|
70
|
+
{"key": "red", "label": "Red", "value": "#EF4444"},
|
|
71
|
+
{"key": "orange", "label": "Orange", "value": "#F97316"},
|
|
72
|
+
{"key": "yellow", "label": "Yellow", "value": "#EAB308"},
|
|
73
|
+
{"key": "green", "label": "Green", "value": "#22C55E"},
|
|
74
|
+
{"key": "teal", "label": "Teal", "value": "#14B8A6"},
|
|
75
|
+
{"key": "blue", "label": "Blue", "value": "#3B82F6"},
|
|
76
|
+
{"key": "indigo", "label": "Indigo", "value": "#6366F1"},
|
|
77
|
+
{"key": "purple", "label": "Purple", "value": "#A855F7"},
|
|
78
|
+
{"key": "pink", "label": "Pink", "value": "#EC4899"},
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
DEFAULT_FEATURES = {
|
|
82
|
+
"TEXT_COLOR": True,
|
|
83
|
+
"HIGHLIGHT_COLOR": True,
|
|
84
|
+
"FONT_FAMILY": True,
|
|
85
|
+
"FONT_SIZE": True,
|
|
86
|
+
"TEXT_ALIGNMENT": True,
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _user_settings():
|
|
91
|
+
return getattr(settings, "DRAFTAIL_TEXT_UTILS", {})
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _deep_merge(default, override):
|
|
95
|
+
result = default.copy()
|
|
96
|
+
for key, value in override.items():
|
|
97
|
+
if key in result and isinstance(result[key], dict) and isinstance(value, dict):
|
|
98
|
+
result[key] = _deep_merge(result[key], value)
|
|
99
|
+
else:
|
|
100
|
+
result[key] = value
|
|
101
|
+
return result
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
FULL_DEFAULTS = {
|
|
105
|
+
"FONT_SIZES": DEFAULT_FONT_SIZES,
|
|
106
|
+
"COLORS": {},
|
|
107
|
+
"COLOR_PALETTE": None,
|
|
108
|
+
"FONT_FAMILIES": None,
|
|
109
|
+
"FONT_URLS": None,
|
|
110
|
+
"FEATURES": DEFAULT_FEATURES,
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _merged_config():
|
|
115
|
+
user = _user_settings()
|
|
116
|
+
return _deep_merge(FULL_DEFAULTS.copy(), user)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def get_setting(key, default=None):
|
|
120
|
+
config = _merged_config()
|
|
121
|
+
return config.get(key, default)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def get_font_sizes():
|
|
125
|
+
return get_setting("FONT_SIZES", DEFAULT_FONT_SIZES)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def get_font_size_presets():
|
|
129
|
+
sizes = get_font_sizes()
|
|
130
|
+
return sizes.get("PRESETS", DEFAULT_FONT_SIZES["PRESETS"])
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def get_feature_toggles():
|
|
134
|
+
return get_setting("FEATURES", DEFAULT_FEATURES)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def feature_enabled(name):
|
|
138
|
+
return get_feature_toggles().get(name, True)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
# ---------------------------------------------------------------------------
|
|
142
|
+
# wagtailtraverse-based extraction utilities
|
|
143
|
+
# ---------------------------------------------------------------------------
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _extract_struct_from_field(field_value):
|
|
147
|
+
"""
|
|
148
|
+
Extract a plain dict from a Wagtail StreamValue / BoundBlock / StructValue,
|
|
149
|
+
or return the value itself if it is already a dict/list.
|
|
150
|
+
Returns an empty dict if extraction fails.
|
|
151
|
+
"""
|
|
152
|
+
if not field_value:
|
|
153
|
+
return {}
|
|
154
|
+
|
|
155
|
+
if isinstance(field_value, dict):
|
|
156
|
+
return field_value
|
|
157
|
+
|
|
158
|
+
if isinstance(field_value, list):
|
|
159
|
+
if field_value and hasattr(field_value[0], "block"):
|
|
160
|
+
for _path, bound_block in traverse_value(field_value):
|
|
161
|
+
return bound_block.value or {}
|
|
162
|
+
return {}
|
|
163
|
+
|
|
164
|
+
try:
|
|
165
|
+
for _path, bound_block in traverse_value(field_value):
|
|
166
|
+
return bound_block.value or {}
|
|
167
|
+
except Exception:
|
|
168
|
+
logger.exception("Failed to extract struct from field %s", field_value)
|
|
169
|
+
|
|
170
|
+
return {}
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _find_design_settings_model(module):
|
|
174
|
+
"""Find a SiteDesignSettings-style model class in a module."""
|
|
175
|
+
for attr_name in dir(module):
|
|
176
|
+
attr = getattr(module, attr_name)
|
|
177
|
+
if isinstance(attr, type) and hasattr(attr, "_meta"):
|
|
178
|
+
try:
|
|
179
|
+
if attr._meta.label and "SiteDesignSettings" in attr.__name__:
|
|
180
|
+
return attr
|
|
181
|
+
except (AttributeError, KeyError):
|
|
182
|
+
continue
|
|
183
|
+
return None
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def _find_typography_block(module):
|
|
187
|
+
"""Find a TypographyBlock / TypographyStreamBlock class in a module."""
|
|
188
|
+
for attr_name in dir(module):
|
|
189
|
+
attr = getattr(module, attr_name)
|
|
190
|
+
if isinstance(attr, type) and "Typography" in getattr(attr, "__name__", ""):
|
|
191
|
+
return attr
|
|
192
|
+
return None
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def _normalise_font_urls(font_urls):
|
|
196
|
+
"""Normalise font_urls to a list, handling string or single value input."""
|
|
197
|
+
if not font_urls:
|
|
198
|
+
return []
|
|
199
|
+
if isinstance(font_urls, str):
|
|
200
|
+
return [font_urls]
|
|
201
|
+
return list(font_urls)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
# ---------------------------------------------------------------------------
|
|
205
|
+
# Color palette loading
|
|
206
|
+
# ---------------------------------------------------------------------------
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def load_color_palette():
|
|
210
|
+
colors_config = get_setting("COLORS", {})
|
|
211
|
+
module_path = colors_config.get("MODULE")
|
|
212
|
+
|
|
213
|
+
if module_path:
|
|
214
|
+
try:
|
|
215
|
+
module = importlib.import_module(module_path)
|
|
216
|
+
if hasattr(module, "DRAFTAIL_COLORS"):
|
|
217
|
+
return module.DRAFTAIL_COLORS
|
|
218
|
+
if hasattr(module, "COLOR_PALETTE"):
|
|
219
|
+
raw = module.COLOR_PALETTE
|
|
220
|
+
if isinstance(raw, dict):
|
|
221
|
+
return [
|
|
222
|
+
{"key": k, "label": k.replace("_", " ").title(), "value": v}
|
|
223
|
+
for k, v in raw.items()
|
|
224
|
+
]
|
|
225
|
+
return raw
|
|
226
|
+
except (ImportError, AttributeError):
|
|
227
|
+
pass
|
|
228
|
+
|
|
229
|
+
result = load_colors_from_module_settings(module_path)
|
|
230
|
+
if result is not None:
|
|
231
|
+
return result
|
|
232
|
+
|
|
233
|
+
explicit = get_setting("COLOR_PALETTE")
|
|
234
|
+
if explicit is not None:
|
|
235
|
+
return explicit
|
|
236
|
+
|
|
237
|
+
return DEFAULT_COLORS
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def load_colors_from_module_settings(module_path):
|
|
241
|
+
try:
|
|
242
|
+
module = importlib.import_module(module_path)
|
|
243
|
+
except ImportError:
|
|
244
|
+
return None
|
|
245
|
+
|
|
246
|
+
palette = getattr(module, "LIGHT_MODE_DEFAULT_COLORS", None)
|
|
247
|
+
if palette and isinstance(palette, dict):
|
|
248
|
+
return [
|
|
249
|
+
{"key": k, "label": k.replace("_", " ").title(), "value": v}
|
|
250
|
+
for k, v in palette.items()
|
|
251
|
+
]
|
|
252
|
+
|
|
253
|
+
model_class = _find_design_settings_model(module)
|
|
254
|
+
if model_class:
|
|
255
|
+
try:
|
|
256
|
+
instance = model_class.load()
|
|
257
|
+
cp_struct = _extract_struct_from_field(instance.color_palette)
|
|
258
|
+
light = cp_struct.get("light", {})
|
|
259
|
+
if light:
|
|
260
|
+
return [
|
|
261
|
+
{"key": k, "label": k.replace("_", " ").title(), "value": v}
|
|
262
|
+
for k, v in light.items()
|
|
263
|
+
]
|
|
264
|
+
except Exception:
|
|
265
|
+
logger.warning(
|
|
266
|
+
"Failed to load colours from module %s", module_path, exc_info=True
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
return None
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
# ---------------------------------------------------------------------------
|
|
273
|
+
# Font family loading
|
|
274
|
+
# ---------------------------------------------------------------------------
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def load_font_families():
|
|
278
|
+
value = get_setting("FONT_FAMILIES")
|
|
279
|
+
|
|
280
|
+
if isinstance(value, list):
|
|
281
|
+
return value
|
|
282
|
+
|
|
283
|
+
if isinstance(value, dict):
|
|
284
|
+
module_path = value.get("MODULE")
|
|
285
|
+
if module_path:
|
|
286
|
+
result = load_font_families_from_module_settings(module_path)
|
|
287
|
+
if result is not None:
|
|
288
|
+
return result
|
|
289
|
+
|
|
290
|
+
return []
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def load_font_families_from_module_settings(module_path):
|
|
294
|
+
try:
|
|
295
|
+
module = importlib.import_module(module_path)
|
|
296
|
+
except ImportError:
|
|
297
|
+
return None
|
|
298
|
+
|
|
299
|
+
if hasattr(module, "DRAFTAIL_FONT_FAMILIES"):
|
|
300
|
+
return module.DRAFTAIL_FONT_FAMILIES
|
|
301
|
+
|
|
302
|
+
typography_block = _find_typography_block(module)
|
|
303
|
+
if typography_block:
|
|
304
|
+
families = _extract_font_families_from_typography_block(typography_block)
|
|
305
|
+
if families:
|
|
306
|
+
return families
|
|
307
|
+
|
|
308
|
+
model_class = _find_design_settings_model(module)
|
|
309
|
+
if model_class:
|
|
310
|
+
try:
|
|
311
|
+
instance = model_class.load()
|
|
312
|
+
families = _extract_font_families_from_typography_instance(instance)
|
|
313
|
+
if families:
|
|
314
|
+
return families
|
|
315
|
+
except Exception:
|
|
316
|
+
logger.warning(
|
|
317
|
+
"Failed to load font families from module %s",
|
|
318
|
+
module_path,
|
|
319
|
+
exc_info=True,
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
return None
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
def _extract_font_families_from_typography_block(typography_block):
|
|
326
|
+
families = []
|
|
327
|
+
|
|
328
|
+
child_blocks = getattr(typography_block, "child_blocks", None)
|
|
329
|
+
if child_blocks is None:
|
|
330
|
+
child_blocks = {}
|
|
331
|
+
try:
|
|
332
|
+
child_blocks = {f.name: f for f in typography_block.base_blocks.items()}
|
|
333
|
+
except (AttributeError, TypeError):
|
|
334
|
+
pass
|
|
335
|
+
|
|
336
|
+
for field_name in (
|
|
337
|
+
"font_family_heading",
|
|
338
|
+
"font_family_subheading",
|
|
339
|
+
"font_family_body",
|
|
340
|
+
):
|
|
341
|
+
block = child_blocks.get(field_name)
|
|
342
|
+
if block is not None:
|
|
343
|
+
default = getattr(block, "default", None)
|
|
344
|
+
if default:
|
|
345
|
+
label = field_name.replace("font_family_", "").replace("_", " ").title()
|
|
346
|
+
families.append({"label": label, "value": str(default)})
|
|
347
|
+
|
|
348
|
+
custom_block = child_blocks.get("custom_fonts")
|
|
349
|
+
if custom_block is not None:
|
|
350
|
+
default_list = getattr(custom_block, "default", None)
|
|
351
|
+
if default_list:
|
|
352
|
+
for cf in default_list:
|
|
353
|
+
families.append(
|
|
354
|
+
{
|
|
355
|
+
"label": cf.get("label", cf.get("font_family", "")),
|
|
356
|
+
"value": cf.get("font_family", ""),
|
|
357
|
+
}
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
return families or None
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
def _extract_font_families_from_typography_instance(instance):
|
|
364
|
+
tp_struct = _extract_struct_from_field(instance.typography)
|
|
365
|
+
if not tp_struct:
|
|
366
|
+
return None
|
|
367
|
+
|
|
368
|
+
families = []
|
|
369
|
+
for key in (
|
|
370
|
+
"font_family_heading",
|
|
371
|
+
"font_family_subheading",
|
|
372
|
+
"font_family_body",
|
|
373
|
+
):
|
|
374
|
+
value = tp_struct.get(key)
|
|
375
|
+
if value:
|
|
376
|
+
label = key.replace("font_family_", "").replace("_", " ").title()
|
|
377
|
+
families.append({"label": label, "value": value})
|
|
378
|
+
|
|
379
|
+
custom_fonts = tp_struct.get("custom_fonts", [])
|
|
380
|
+
for cf in custom_fonts:
|
|
381
|
+
families.append(
|
|
382
|
+
{
|
|
383
|
+
"label": cf.get("label", cf.get("font_family", "")),
|
|
384
|
+
"value": cf.get("font_family", ""),
|
|
385
|
+
}
|
|
386
|
+
)
|
|
387
|
+
|
|
388
|
+
return families or None
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
# ---------------------------------------------------------------------------
|
|
392
|
+
# Font URL loading
|
|
393
|
+
# ---------------------------------------------------------------------------
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
def load_font_urls():
|
|
397
|
+
value = get_setting("FONT_URLS")
|
|
398
|
+
|
|
399
|
+
if isinstance(value, list):
|
|
400
|
+
return value
|
|
401
|
+
|
|
402
|
+
if isinstance(value, dict):
|
|
403
|
+
module_path = value.get("MODULE")
|
|
404
|
+
if module_path:
|
|
405
|
+
result = load_font_urls_from_module_settings(module_path)
|
|
406
|
+
if result is not None:
|
|
407
|
+
return result
|
|
408
|
+
|
|
409
|
+
return []
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def load_font_urls_from_module_settings(module_path):
|
|
413
|
+
try:
|
|
414
|
+
module = importlib.import_module(module_path)
|
|
415
|
+
except ImportError:
|
|
416
|
+
return None
|
|
417
|
+
|
|
418
|
+
if hasattr(module, "DRAFTAIL_FONT_URLS"):
|
|
419
|
+
return module.DRAFTAIL_FONT_URLS
|
|
420
|
+
|
|
421
|
+
typography_block = _find_typography_block(module)
|
|
422
|
+
if typography_block:
|
|
423
|
+
urls = _extract_font_urls_from_typography_block(typography_block)
|
|
424
|
+
if urls:
|
|
425
|
+
return urls
|
|
426
|
+
|
|
427
|
+
model_class = _find_design_settings_model(module)
|
|
428
|
+
if model_class:
|
|
429
|
+
try:
|
|
430
|
+
instance = model_class.load()
|
|
431
|
+
tp_struct = _extract_struct_from_field(instance.typography)
|
|
432
|
+
if tp_struct:
|
|
433
|
+
urls = _normalise_font_urls(tp_struct.get("font_url", []))
|
|
434
|
+
custom_fonts = tp_struct.get("custom_fonts", [])
|
|
435
|
+
for cf in custom_fonts:
|
|
436
|
+
url = cf.get("font_url")
|
|
437
|
+
if url:
|
|
438
|
+
urls.append(url)
|
|
439
|
+
if urls:
|
|
440
|
+
return urls
|
|
441
|
+
except Exception:
|
|
442
|
+
logger.warning(
|
|
443
|
+
"Failed to load font URLs from module %s",
|
|
444
|
+
module_path,
|
|
445
|
+
exc_info=True,
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
return None
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
def _extract_font_urls_from_typography_block(typography_block):
|
|
452
|
+
child_blocks = getattr(typography_block, "child_blocks", None)
|
|
453
|
+
if child_blocks is None:
|
|
454
|
+
try:
|
|
455
|
+
child_blocks = {f.name: f for f in typography_block.base_blocks.items()}
|
|
456
|
+
except (AttributeError, TypeError):
|
|
457
|
+
child_blocks = {}
|
|
458
|
+
|
|
459
|
+
urls = []
|
|
460
|
+
for field_name in (
|
|
461
|
+
"font_url",
|
|
462
|
+
"google_fonts_url",
|
|
463
|
+
"font_url_heading",
|
|
464
|
+
"font_url_body",
|
|
465
|
+
"font_url_subheading",
|
|
466
|
+
):
|
|
467
|
+
block = child_blocks.get(field_name)
|
|
468
|
+
if block is not None:
|
|
469
|
+
default = getattr(block, "default", None)
|
|
470
|
+
if default:
|
|
471
|
+
urls.append(str(default))
|
|
472
|
+
|
|
473
|
+
custom_block = child_blocks.get("custom_fonts")
|
|
474
|
+
if custom_block is not None:
|
|
475
|
+
default_list = getattr(custom_block, "default", None)
|
|
476
|
+
if default_list:
|
|
477
|
+
for cf in default_list:
|
|
478
|
+
url = cf.get("font_url")
|
|
479
|
+
if url:
|
|
480
|
+
urls.append(url)
|
|
481
|
+
|
|
482
|
+
return list(dict.fromkeys(urls)) if urls else None
|
|
File without changes
|
|
File without changes
|