Skip to content

Commit 983da72

Browse files
authored
Merge pull request pklaus#11 from DL6ER/font-color
Allow setting font color individually per line
2 parents 102ebfe + f120c01 commit 983da72

7 files changed

Lines changed: 191 additions & 144 deletions

File tree

app/labeldesigner/label.py

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@ def __init__(
8787
border_roundness=0,
8888
border_distance=(0, 0),
8989
border_color=(0, 0, 0),
90-
timestamp=0):
90+
timestamp=0,
91+
red_support=False):
9192
self._width = width
9293
self._height = height
9394
self.label_content = label_content
@@ -108,6 +109,7 @@ def __init__(
108109
self._border_color = border_color
109110
self._counter = 1
110111
self._timestamp = timestamp
112+
self._red_support = red_support
111113

112114
@property
113115
def label_content(self):
@@ -394,13 +396,11 @@ def _draw_text(self, img = None, bboxes = [], text_offset = (0, 0)):
394396

395397
# Iterate over lines of text
396398
for i, line in enumerate(self.text):
397-
color = self._fore_color
398-
399399
# Calculate spacing
400-
spacing = int(int(line['font_size'])*((int(line['line_spacing']) - 100) / 100)) if 'line_spacing' in line else 0
400+
spacing = int(int(line['size'])*((int(line['line_spacing']) - 100) / 100)) if 'line_spacing' in line else 0
401401

402402
# Get font
403-
font = self._get_font(line['font_path'], line['font_size'])
403+
font = self._get_font(line['path'], line['size'])
404404

405405
# Determine anchors
406406
anchor = None
@@ -419,7 +419,12 @@ def _draw_text(self, img = None, bboxes = [], text_offset = (0, 0)):
419419
else:
420420
raise ValueError(f"Unsupported alignment: {align}")
421421

422-
if do_draw and 'font_inverted' in line and line['font_inverted']:
422+
red_font = 'color' in line and line['color'] == 'red'
423+
# if red_font and not self._red_support:
424+
# raise ValueError("Red font is not supported on this label")
425+
color = (255, 0, 0) if red_font else (0, 0, 0)
426+
427+
if do_draw and 'inverted' in line and line['inverted']:
423428
# Draw a filled rectangle
424429
center_x = 0
425430
if anchor == "lt":
@@ -434,10 +439,11 @@ def _draw_text(self, img = None, bboxes = [], text_offset = (0, 0)):
434439
elif anchor == "rt":
435440
max_bbox_x = text_offset[0] + max(bbox[0][2] for bbox in bboxes)
436441
min_bbox_x = max_bbox_x - (bboxes[i][0][2] - bboxes[i][0][0])
437-
shift = 0.1 * int(line['font_size'])
442+
shift = 0.1 * int(line['size'])
438443
y_min = bboxes[i][0][1] + text_offset[1] - shift
439444
y_max = bboxes[i][0][3] + text_offset[1] - shift
440-
draw.rectangle((min_bbox_x, y_min, max_bbox_x, y_max), fill=self._fore_color)
445+
draw.rectangle((min_bbox_x, y_min, max_bbox_x, y_max), fill=color)
446+
# Overwrite font color with white on colored background
441447
color = (255, 255, 255)
442448

443449
# Either calculate bbox or actually draw
@@ -481,5 +487,5 @@ def _compute_bbox(self, bboxes):
481487
max_width = max(bbox[0][2] for bbox in bboxes)
482488
return (bboxes[0][0][0], bboxes[0][0][1], max_width, bboxes[-1][0][3])
483489

484-
def _get_font(self, font_path, font_size):
485-
return ImageFont.truetype(font_path, int(font_size))
490+
def _get_font(self, font_path, size):
491+
return ImageFont.truetype(font_path, int(size))

app/labeldesigner/routes.py

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -195,19 +195,18 @@ def get_label_dimensions(label_size):
195195

196196
def get_font_path(line: dict):
197197
try:
198-
font_family_name = line.get('font_family')
199-
font_style_name = line.get('font_style')
200-
if font_family_name not in FONTS.fonts:
201-
raise LookupError("Unknown font family: %s" % font_family_name)
202-
if font_style_name not in FONTS.fonts[font_family_name]:
203-
font_style_name = current_app.config['LABEL_DEFAULT_FONT_STYLE']
204-
if font_style_name not in FONTS.fonts[font_family_name]:
198+
family_name = line.get('family')
199+
style_name = line.get('style')
200+
if family_name not in FONTS.fonts:
201+
raise LookupError("Unknown font family: %s" % family_name)
202+
if style_name not in FONTS.fonts[family_name]:
203+
style_name = current_app.config['LABEL_DEFAULT_FONT_STYLE']
204+
if style_name not in FONTS.fonts[family_name]:
205205
raise LookupError("Unknown font style: %s for font %s" %
206-
(font_style_name, font_family_name))
207-
font_path = FONTS.fonts[font_family_name][font_style_name]
206+
(style_name, family_name))
207+
return FONTS.fonts[family_name][style_name]
208208
except KeyError:
209-
raise LookupError("Couln't find the font & style")
210-
return font_path
209+
raise LookupError("Couldn't find the requested font + style")
211210

212211
def get_uploaded_image(image):
213212
name, ext = os.path.splitext(image.filename)
@@ -269,18 +268,22 @@ def get_uploaded_image(image):
269268

270269
# For each line in text, we determine and add the font path
271270
for line in context['text']:
272-
if 'font_family' not in line:
273-
line['font_family'] = current_app.config['LABEL_DEFAULT_FONT_FAMILY']
274-
if 'font_style' not in line:
275-
line['font_style'] = current_app.config['LABEL_DEFAULT_FONT_STYLE']
276-
if 'font_size' not in line:
271+
if 'family' not in line:
272+
line['family'] = current_app.config['LABEL_DEFAULT_FONT_FAMILY']
273+
if 'style' not in line:
274+
line['style'] = current_app.config['LABEL_DEFAULT_FONT_STYLE']
275+
if 'size' not in line or not line['size'].isdigit():
277276
raise ValueError("Font size is required")
278-
line['font_path'] = get_font_path(line)
277+
if int(line['size']) < 1:
278+
raise ValueError("Font size must be at least 1")
279+
line['path'] = get_font_path(line)
279280

280281
# Reject extraordinary long texts
281282
if len(line['text']) > 10_000:
282283
raise ValueError("Text is too long")
283284

285+
# if context['print_color'] == 'red' and not context['red_support']:
286+
# raise ValueError("Red font is not supported on this label")
284287
fore_color = (255, 0, 0) if context['print_color'] == 'red' else (0, 0, 0)
285288
border_color = (255, 0, 0) if context['border_color'] == 'red' else (0, 0, 0)
286289

app/labeldesigner/templates/labeldesigner.html

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -29,36 +29,27 @@
2929
<div id="collapse1" class="collapse" aria-labelledby="heading1" data-parent="#accordion">
3030
<div class="card-body">
3131
<label for="labelSize" style="margin-bottom: 0">Label Size:</label>
32-
<select class="form-control" id="labelSize" onChange="preview()">
33-
{% for label_size in label_sizes %}<option value="{{label_size[0]}}" data-x="{{label_size[3][0]}}" data-y="{{label_size[3][1]}}" data-round="{{label_size[2]}}" {% if default_label_size == label_size[0] %}selected{% endif %}>{{label_size[1]}}</option>{% endfor %}
32+
<select class="form-control" id="labelSize" onChange="preview()" data-default="{{default_label_size}}">
33+
{% for label_size in label_sizes %}<option value="{{label_size[0]}}" data-x="{{label_size[3][0]}}"
34+
data-y="{{label_size[3][1]}}" data-round="{{label_size[2]}}" {% if default_label_size==label_size[0]
35+
%}selected{% endif %}>{{label_size[1]}}</option>{% endfor %}
3436
</select>
3537
<div class="form-text text-muted">Detected label: <span id="label-width">???</span> x <span id="label-height">???</span></div>
3638
<div id="labelMismatch" class="form-text text-danger" style="display: none;">Label size mismatch detected!</div>
3739

3840
<label for="orientation" class="control-label input-group" style="margin-top: 10px; margin-bottom: 0">Label Orientation:</label>
3941
<div class="btn-group btn-group-toggle btn-block" data-toggle="buttons">
4042
<label class="btn btn-secondary {% if default_orientation == 'standard' %}active{% endif %}" id="orientation_standard">
41-
<input type="radio" name="orientation" onchange="preview()" value="standard" aria-label="Standard" {% if default_orientation == 'standard' %}checked{% endif %}>
43+
<input type="radio" name="orientation" onchange="preview()" value="standard" aria-label="Standard" {% if
44+
default_orientation=='standard' %}checked data-default="1" {% endif %}>
4245
<span class="fas fa-ruler-horizontal" aria-hidden="true"> Standard
4346
</label>
4447
<label class="btn btn-secondary {% if default_orientation == 'rotated' %}active{% endif %}" id="orientation_rotated">
45-
<input type="radio" name="orientation" onchange="preview()" value="rotated" aria-label="Rotated" {% if default_orientation == 'rotated' %}checked{% endif %}>
48+
<input type="radio" name="orientation" onchange="preview()" value="rotated" aria-label="Rotated" {% if
49+
default_orientation=='rotated' %}checked data-default="1" {% endif %}>
4650
<span class="fas fa-ruler-vertical" aria-hidden="true"> Rotated
4751
</label>
4852
</div>
49-
50-
<div class="red-support">
51-
<label for="printColor" class="control-label input-group" style="margin-top: 10px; margin-bottom: 0">Print Color:</label>
52-
<div class="btn-group btn-group-toggle btn-block" data-toggle="buttons">
53-
<label class="btn btn-dark" id="print_color_black">
54-
<input type="radio" name="printColor" onchange="preview()" value="black" aria-label="Black">Black
55-
</label>
56-
<label class="btn btn-danger" id="print_color_red">
57-
<input type="radio" name="printColor" onchange="preview()" value="red" aria-label="Red">Red
58-
</label>
59-
</div>
60-
</div>
61-
6253
</div>
6354
<!-- class="card-body" -->
6455
</div>
@@ -127,6 +118,18 @@
127118
Invert text
128119
</label>
129120
</div>
121+
<div class="red-support">
122+
<label for="printColor" class="control-label input-group" style="margin-top: 10px; margin-bottom: 0">Print
123+
Color:</label>
124+
<div class="btn-group btn-group-toggle btn-block" data-toggle="buttons">
125+
<label class="btn btn-dark" id="print_color_black">
126+
<input type="radio" name="fontColor" onchange="preview()" value="black" aria-label="Black" data-default="1">Black
127+
</label>
128+
<label class="btn btn-danger" id="print_color_red">
129+
<input type="radio" name="fontColor" onchange="preview()" value="red" aria-label="Red">Red
130+
</label>
131+
</div>
132+
</div>
130133
</div>
131134
</div>
132135

@@ -298,7 +301,7 @@
298301
<label for="printType" class="control-label input-group" style="margin-bottom: 0">Print Type:</label>
299302
<div class="btn-group btn-group-toggle btn-block" data-toggle="buttons">
300303
<label class="btn btn-secondary active" id="printTypeText">
301-
<input type="radio" name="printType" onchange="preview()" value="text" aria-label="Text" checked>
304+
<input type="radio" name="printType" onchange="preview()" value="text" aria-label="Text" data-default="1" checked>
302305
<span class="fas fa-font" aria-hidden="true"></span><br>Text
303306
</label>
304307
<label class="btn btn-secondary" id="printTypeQrCode">

app/static/js/main.js

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,13 @@ function setFontSettingsPerLine() {
1818

1919
// Default font settings from the current UI controls
2020
var currentFont = {
21-
font_family: $('#fontFamily option:selected').text(),
22-
font_style: $('#fontStyle option:selected').text(),
23-
font_size: $('#fontSize').val(),
24-
font_inverted: $('#fontInverted').is(':checked'),
21+
family: $('#fontFamily option:selected').text(),
22+
style: $('#fontStyle option:selected').text(),
23+
size: $('#fontSize').val(),
24+
inverted: $('#fontInverted').is(':checked'),
2525
align: $('input[name=fontAlign]:checked').val(),
26-
line_spacing: $('input[name=lineSpacing]:checked').val()
26+
line_spacing: $('input[name=lineSpacing]:checked').val(),
27+
color: $('input[name=fontColor]:checked').val()
2728
};
2829

2930
// Create lines in the <option> with id #lineSelect
@@ -91,26 +92,29 @@ $(document).ready(function () {
9192
if (isNaN(idx) || !fontSettingsPerLine || !fontSettingsPerLine[idx]) return;
9293
var fs = fontSettingsPerLine[idx];
9394
// Only set font family and get styles if font family is changed
94-
if (fs.font_family !== $('#fontFamily option:selected').text()) {
95+
if (fs.family !== $('#fontFamily option:selected').text()) {
9596
// Set font family
96-
$('#fontFamily').val(fs.font_family);
97+
$('#fontFamily').val(fs.family);
9798
// Set font style
98-
updateStyles(fs.font_style);
99+
updateStyles(fs.style);
99100
}
100101
else {
101102
// Only set font style
102-
$('#fontStyle').val(fs.font_style);
103+
$('#fontStyle').val(fs.style);
103104
}
104105
// Set font size
105-
$('#fontSize').val(fs.font_size);
106+
$('#fontSize').val(fs.size);
106107
// Set alignment
107108
$('input[name=fontAlign]').prop('checked', false).parent().removeClass('active');
108109
$('input[name=fontAlign][value="' + fs.align + '"]').prop('checked', true).parent().addClass('active');
109110
// Set line spacing
110111
$('input[name=lineSpacing]').prop('checked', false).parent().removeClass('active');
111112
$('input[name=lineSpacing][value="' + fs.line_spacing + '"]').prop('checked', true).parent().addClass('active');
112113
// Set font inversion
113-
$('#fontInverted').prop('checked', fs.font_inverted);
114+
$('#fontInverted').prop('checked', fs.inverted);
115+
// Set font color
116+
$('input[name=fontColor]').prop('checked', false).parent().removeClass('active');
117+
$('input[name=fontColor][value="' + fs.color + '"]').prop('checked', true).parent().addClass('active');
114118
});
115119

116120
// When the user changes the caret/selection in the textarea, update #lineSelect and font controls
@@ -197,6 +201,7 @@ function updateStyles(style = null) {
197201
}
198202
});
199203
styleSelect.trigger("change");
204+
preview();
200205
}
201206
});
202207
}
@@ -401,7 +406,7 @@ function updatePrinterStatus() {
401406
if (printerPath) {
402407
printerPath.textContent = printer_status.path || 'Unknown';
403408
}
404-
if (printer_status['red_support'] === true && $('#labelSize option:selected').val().includes('red')) {
409+
if ($('#labelSize option:selected').val().includes('red')) {
405410
$(".red-support").show();
406411
} else {
407412
$('#print_color_black').prop('active', true);
@@ -499,7 +504,7 @@ function restoreAllSettingsFromLocalStorage() {
499504
if (this.type === 'checkbox' || this.type === 'radio') {
500505
$(this).prop('checked', !!data[key]);
501506
if (this.type === 'radio') {
502-
if ($(this).val() == data[key]) $(this).prop('checked', true);
507+
$(this).prop('checked', $(this).val() == data[key]);
503508
}
504509
} else {
505510
$(this).val(data[key]);
@@ -560,11 +565,15 @@ function resetSettings() {
560565
function set_all_inputs_default(force = false) {
561566
// Iterate over those <input> that have a data-default propery and set the value if empty
562567
$('input[data-default], select[data-default], textarea[data-default]').each(function() {
563-
if (!$(this).val() || force) {
568+
if (this.type === 'checkbox' || this.type === 'radio') {
569+
$(this).prop('checked', $(this).data('default') == 1 || $(this).data('default') === true);
570+
}
571+
else if (this.type === 'select-one' || this.type === 'number') {
564572
$(this).val($(this).data('default'));
573+
565574
}
566-
if (this.type === 'checkbox') {
567-
$(this).prop('checked', $(this).data('default') == 1 || $(this).data('default') === true);
575+
else if (!$(this).val() || force) {
576+
$(this).val($(this).data('default'));
568577
}
569578
});
570579
}
@@ -578,6 +587,7 @@ window.onload = function () {
578587

579588
// Restore settings on load
580589
restoreAllSettingsFromLocalStorage();
590+
581591
// Save on change
582592
$(document).on('change input', 'input, select, textarea', function() {
583593
saveAllSettingsToLocalStorage();

tests/large_label.png

-3.9 KB
Loading

tests/red_text.png

3.99 KB
Loading

0 commit comments

Comments
 (0)