Skip to content

Commit

Permalink
Merge pull request #209 from will-moore/markdown_labels
Browse files Browse the repository at this point in the history
Markdown labels
  • Loading branch information
jburel authored Jun 13, 2017
2 parents d3f2816 + 4296c56 commit 12103c1
Show file tree
Hide file tree
Showing 5 changed files with 177 additions and 48 deletions.
173 changes: 139 additions & 34 deletions omero_figure/scripts/omero/figure_scripts/Figure_To_Pdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,10 @@

try:
from reportlab.pdfgen import canvas
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import inch
from reportlab.platypus import Paragraph
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_RIGHT
reportlab_installed = True
except ImportError:
reportlab_installed = False
Expand Down Expand Up @@ -1338,25 +1339,53 @@ def save_figure(self):

def draw_text(self, text, x, y, fontsize, rgb, align="center"):
""" Adds text to PDF. Overwritten for TIFF below """
ly = y + fontsize
ly = self.page_height - ly + 5
if markdown_imported:
# convert markdown to html
text = markdown.markdown(text)

y = self.page_height - y
c = self.figure_canvas
# Needs to be wide enough to avoid wrapping
para_width = self.page_width

red, green, blue = rgb
red = float(red)/255
green = float(green)/255
blue = float(blue)/255
c.setFont("Helvetica", fontsize)
c.setFillColorRGB(red, green, blue)

alignment = TA_LEFT
if (align == "center"):
c.drawCentredString(x, ly, text)
alignment = TA_CENTER
x = x - (para_width/2)
elif (align == "right"):
c.drawRightString(x, ly, text)
alignment = TA_RIGHT
x = x - para_width
elif (align == "left"):
c.drawString(x, ly, text)
pass
elif align == 'vertical':
# Switch axes
c.rotate(90)
c.drawCentredString(self.page_height - y, -(x + fontsize), text)
px = x
x = y
y = -px
# Align center
alignment = TA_CENTER
x = x - (para_width/2)

style_n = getSampleStyleSheet()['Normal']
style = ParagraphStyle(
'label',
parent=style_n,
alignment=alignment,
textColor=(red, green, blue),
fontSize=fontsize)

para = Paragraph(text, style)
w, h = para.wrap(para_width, y) # find required space
para.drawOn(c, x, y - h + int(fontsize * 0.25))

# Rotate back again
if align == 'vertical':
c.rotate(-90)

def draw_line(self, x, y, x2, y2, width, rgb):
Expand Down Expand Up @@ -1425,7 +1454,6 @@ def __init__(self, conn, script_params, export_images=None):

from omero.gateway import THISPATH
self.GATEWAYPATH = THISPATH
self.font_path = os.path.join(THISPATH, "pilfonts", "FreeSans.ttf")

self.ns = "omero.web.figure.tiff"
self.mimetype = "image/tiff"
Expand All @@ -1434,10 +1462,18 @@ def add_rois(self, panel, page):
""" TIFF export doesn't add ROIs to page (does it to panel)"""
pass

def get_font(self, fontsize):
def get_font(self, fontsize, bold=False, italics=False):
""" Try to load font from known location in OMERO """
font_name = "FreeSans.ttf"
if bold and italics:
font_name = "FreeSansBoldOblique.ttf"
elif bold:
font_name = "FreeSansBold.ttf"
elif italics:
font_name = "FreeSansOblique.ttf"
path_to_font = os.path.join(self.GATEWAYPATH, "pilfonts", font_name)
try:
font = ImageFont.truetype(self.font_path, fontsize)
font = ImageFont.truetype(path_to_font, fontsize)
except:
font = ImageFont.load(
'%s/pilfonts/B%0.2d.pil' % (self.GATEWAYPATH, 24))
Expand Down Expand Up @@ -1521,35 +1557,104 @@ def draw_line(self, x, y, x2, y2, width, rgb):
y += 1
y2 += 1

def draw_temp_label(self, text, fontsize, rgb):
"""Returns a new PIL image with text. Handles html."""
tokens = self.parse_html(text)

widths = []
heights = []
for t in tokens:
font = self.get_font(fontsize, t['bold'], t['italics'])
txt_w, txt_h = font.getsize(t['text'])
widths.append(txt_w)
heights.append(txt_h)

label_w = sum(widths)
label_h = max(heights)

temp_label = Image.new('RGBA', (label_w, label_h), (255, 255, 255, 0))
textdraw = ImageDraw.Draw(temp_label)

w = 0
for t in tokens:
font = self.get_font(fontsize, t['bold'], t['italics'])
txt_w, txt_h = font.getsize(t['text'])
textdraw.text((w, 0), t['text'], font=font, fill=rgb)
w += txt_w
return temp_label

def parse_html(self, html):
"""
Parse html to give list of tokens with bold or italics
Returns list of [{'text': txt, 'bold': true, 'italics': false}]
"""
in_bold = False
in_italics = False

# Remove any <p> tags
html = html.replace('<p>', '')
html = html.replace('</p>', '')

tokens = []
token = ""
i = 0
while i < len(html):
# look for start / end of b or i elements
start_bold = html[i:].startswith("<strong>")
end_bold = html[i:].startswith("</strong>")
start_ital = html[i:].startswith("<em>")
end_ital = html[i:].startswith("</em>")

if start_bold:
i += len("<strong>")
elif end_bold:
i += len("</strong>")
elif start_ital:
i += len("<em>")
elif end_ital:
i += len("</em>")

# if style has changed:
if start_bold or end_bold or start_ital or end_ital:
# save token with previous style
tokens.append({'text': token, 'bold': in_bold,
'italics': in_italics})
token = ""
if start_bold or end_bold:
in_bold = start_bold
elif start_ital or end_ital:
in_italics = start_ital
else:
token = token + html[i]
i += 1
tokens.append({'text': token, 'bold': in_bold, 'italics': in_italics})
return tokens

def draw_text(self, text, x, y, fontsize, rgb, align="center"):
""" Add text to the current figure page """
x = self.scale_coords(x)
y = y - 5 # seems to help, but would be nice to fix this!
y = self.scale_coords(y)
fontsize = self.scale_coords(fontsize)

font = self.get_font(fontsize)
txt_w, txt_h = font.getsize(text)
if markdown_imported:
# convert markdown to html
text = markdown.markdown(text)

temp_label = self.draw_temp_label(text, fontsize, rgb)

if align == "vertical":
# write text on temp image (transparent)
y = self.scale_coords(y)
x = int(round(x))
y = int(round(y))
temp_label = Image.new('RGBA', (txt_w, txt_h), (255, 255, 255, 0))
textdraw = ImageDraw.Draw(temp_label)
textdraw.text((0, 0), text, font=font, fill=rgb)
w = temp_label.rotate(90, expand=True)
# Use label as mask, so transparent part is not pasted
y = y - (w.size[1]/2)
self.tiff_figure.paste(w, (x, y), mask=w)
else:
y = y - 5 # seems to help, but would be nice to fix this!
y = self.scale_coords(y)
textdraw = ImageDraw.Draw(self.tiff_figure)
if align == "center":
x = x - (txt_w / 2)
elif align == "right":
x = x - txt_w
textdraw.text((x, y), text, font=font, fill=rgb)
temp_label = temp_label.rotate(90, expand=True)
y = y - (temp_label.size[1]/2)
elif align == "center":
x = x - (temp_label.size[0] / 2)
elif align == "right":
x = x - temp_label.size[0]
x = int(round(x))
y = int(round(y))
# Use label as mask, so transparent part is not pasted
self.tiff_figure.paste(temp_label, (x, y), mask=temp_label)

def save_page(self):
"""
Expand Down
26 changes: 20 additions & 6 deletions omero_figure/static/figure/css/figure.css
Original file line number Diff line number Diff line change
Expand Up @@ -160,18 +160,26 @@
.markdown-info {
padding: 3px 12px;
color: #aaa;
display: none;
text-align: right;
padding-left: 40px;
background: url(../images/markdown_light32x20.png) 0% center no-repeat;
}
.markdown-info:hover {
background: url(../images/markdown_dark32x20.png) 0% center no-repeat;
}
.editing .markdown-info {
.legend-container .markdown-info {
display: none;
}
.legend-container .editing .markdown-info {
display: block;
}

#labelsTab .markdown-info {
height: 18px;
margin: 9px 9px 0;
float: left"
}

/* hide appropriate collapse/hide button */
.legend-collapsed .collapse-legend{
display: none;
Expand Down Expand Up @@ -313,6 +321,9 @@
.label_layout {
position:absolute;
}
.label_layout p {
margin: 0;
}
.left_vlabels>div {
margin-bottom: 5px;
}
Expand All @@ -326,9 +337,10 @@

.left_vlabels{
position: absolute;
height: 100%;
right: 100%;
width:400px;
height: 300%;
top: -100%;
width: 300%;
text-align: center;
-webkit-transform: rotate(-90deg);
transform: rotate(-90deg);
Expand All @@ -348,13 +360,15 @@
/* Classes here are generated in templates from 'top' etc See labelicon classes below */
.label_top {
bottom: 100%;
width: 100%;
width: 300%;
left: -100%;
text-align: center;
margin-bottom: 3px;
}
.label_bottom {
top: 100%;
width: 100%;
width: 300%;
left: -100%;
text-align:center;
}
.label_left {
Expand Down
12 changes: 8 additions & 4 deletions omero_figure/templates/figure/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -467,12 +467,13 @@ <h4 class="modal-title" id="addImagesLabel">Import from json</h4>
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">Legend Formatting</h4>
<h4 class="modal-title">Markdown Formatting</h4>
</div>
<div class="modal-body">
<p>
You can use "Markdown" syntax to add formatting to plain text. For example,
**this text will be bold** and this *word* will be italic.
**this text will be bold** and this *word* will be italic. NB: Links are not
supported for panel labels.
</p>

<table class="table">
Expand Down Expand Up @@ -888,9 +889,12 @@ <h5>Scalebar</h5>

<hr />

<h5>Add Labels</h5>
<form class="new-label-form form-inline" role="form">
<h5 style="float: left">Add Labels</h5>

<button type="button" class="btn btn-link markdown-info"
title="Use Markdown formatting. Click for more info..."></button>
<div class="clearfix"></div>
<form class="new-label-form form-inline" role="form">
</form>

<h5>Edit Labels</h5>
Expand Down
8 changes: 4 additions & 4 deletions src/js/views/panel_view.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
'height': h +'px'});

// container needs to be square for rotation to vertical
$('.left_vlabels', this.$el).css('width', h + 'px');
$('.left_vlabels', this.$el).css('width', 3 * h + 'px');

// update the img within the panel
var zoom = this.model.get('zoom'),
Expand Down Expand Up @@ -146,8 +146,8 @@
if (typeof ljson.text == 'undefined' && ljson.time) {
ljson.text = self.model.get_time_label_text(ljson.time);
} else {
// Escape all labels so they are safe
ljson.text = _.escape(ljson.text);
// Markdown also escapes all labels so they are safe
ljson.text = markdown.toHTML(ljson.text);
}
positions[l.position].push(ljson);
});
Expand All @@ -168,7 +168,7 @@
self.$el.append(html);

// need to force update of vertical labels layout
$('.left_vlabels', self.$el).css('width', self.$el.height() + 'px');
$('.left_vlabels', self.$el).css('width', 3 * self.$el.height() + 'px');

return this;
},
Expand Down
6 changes: 6 additions & 0 deletions src/js/views/right_panel_view.js
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,12 @@
events: {
"submit .new-label-form": "handle_new_label",
"click .dropdown-menu a": "select_dropdown_option",
"click .markdown-info": "markdownInfo",
},

markdownInfo: function(event) {
event.preventDefault();
$("#markdownInfoModal").modal('show');
},

// Handles all the various drop-down menus in the 'New' AND 'Edit Label' forms
Expand Down

0 comments on commit 12103c1

Please sign in to comment.