1# encoding: utf-8 2 3""" 4Step implementations for text-related features 5""" 6 7from __future__ import absolute_import, print_function, unicode_literals 8 9import hashlib 10 11from behave import given, then, when 12 13from docx import Document 14from docx.enum.text import WD_BREAK, WD_UNDERLINE 15from docx.oxml import parse_xml 16from docx.oxml.ns import nsdecls, qn 17from docx.text.font import Font 18from docx.text.run import Run 19 20from helpers import test_docx, test_file, test_text 21 22 23# given =================================================== 24 25@given('a run') 26def given_a_run(context): 27 document = Document() 28 run = document.add_paragraph().add_run() 29 context.document = document 30 context.run = run 31 32 33@given('a run having {bool_prop_name} set on') 34def given_a_run_having_bool_prop_set_on(context, bool_prop_name): 35 run = Document().add_paragraph().add_run() 36 setattr(run, bool_prop_name, True) 37 context.run = run 38 39 40@given('a run having known text and formatting') 41def given_a_run_having_known_text_and_formatting(context): 42 run = Document().add_paragraph().add_run('foobar') 43 run.bold = True 44 run.italic = True 45 context.run = run 46 47 48@given('a run having mixed text content') 49def given_a_run_having_mixed_text_content(context): 50 """ 51 Mixed here meaning it contains ``<w:tab/>``, ``<w:cr/>``, etc. elements. 52 """ 53 r_xml = """\ 54 <w:r %s> 55 <w:t>abc</w:t> 56 <w:tab/> 57 <w:t>def</w:t> 58 <w:cr/> 59 <w:t>ghi</w:t> 60 <w:drawing/> 61 <w:br/> 62 <w:t>jkl</w:t> 63 </w:r>""" % nsdecls('w') 64 r = parse_xml(r_xml) 65 context.run = Run(r, None) 66 67 68@given('a run having {underline_type} underline') 69def given_a_run_having_underline_type(context, underline_type): 70 run_idx = { 71 'inherited': 0, 'no': 1, 'single': 2, 'double': 3 72 }[underline_type] 73 document = Document(test_docx('run-enumerated-props')) 74 context.run = document.paragraphs[0].runs[run_idx] 75 76 77@given('a run having {style} style') 78def given_a_run_having_style(context, style): 79 run_idx = { 80 'no explicit': 0, 81 'Emphasis': 1, 82 'Strong': 2, 83 }[style] 84 context.document = document = Document(test_docx('run-char-style')) 85 context.run = document.paragraphs[0].runs[run_idx] 86 87 88@given('a run inside a table cell retrieved from {cell_source}') 89def given_a_run_inside_a_table_cell_from_source(context, cell_source): 90 document = Document() 91 table = document.add_table(rows=2, cols=2) 92 if cell_source == 'Table.cell': 93 cell = table.cell(0, 0) 94 elif cell_source == 'Table.row.cells': 95 cell = table.rows[0].cells[1] 96 elif cell_source == 'Table.column.cells': 97 cell = table.columns[1].cells[0] 98 run = cell.paragraphs[0].add_run() 99 context.document = document 100 context.run = run 101 102 103# when ==================================================== 104 105@when('I add a column break') 106def when_add_column_break(context): 107 run = context.run 108 run.add_break(WD_BREAK.COLUMN) 109 110 111@when('I add a line break') 112def when_add_line_break(context): 113 run = context.run 114 run.add_break() 115 116 117@when('I add a page break') 118def when_add_page_break(context): 119 run = context.run 120 run.add_break(WD_BREAK.PAGE) 121 122 123@when('I add a picture to the run') 124def when_I_add_a_picture_to_the_run(context): 125 run = context.run 126 run.add_picture(test_file('monty-truth.png')) 127 128 129@when('I add a run specifying its text') 130def when_I_add_a_run_specifying_its_text(context): 131 context.run = context.paragraph.add_run(test_text) 132 133 134@when('I add a run specifying the character style Emphasis') 135def when_I_add_a_run_specifying_the_character_style_Emphasis(context): 136 context.run = context.paragraph.add_run(test_text, 'Emphasis') 137 138 139@when('I add a tab') 140def when_I_add_a_tab(context): 141 context.run.add_tab() 142 143 144@when('I add text to the run') 145def when_I_add_text_to_the_run(context): 146 context.run.add_text(test_text) 147 148 149@when('I assign mixed text to the text property') 150def when_I_assign_mixed_text_to_the_text_property(context): 151 context.run.text = 'abc\tdef\nghi\rjkl' 152 153 154@when('I assign {value_str} to its {bool_prop_name} property') 155def when_assign_true_to_bool_run_prop(context, value_str, bool_prop_name): 156 value = {'True': True, 'False': False, 'None': None}[value_str] 157 run = context.run 158 setattr(run, bool_prop_name, value) 159 160 161@when('I assign {value} to run.style') 162def when_I_assign_value_to_run_style(context, value): 163 if value == 'None': 164 new_value = None 165 elif value.startswith('styles['): 166 new_value = context.document.styles[value.split('\'')[1]] 167 else: 168 new_value = context.document.styles[value] 169 170 context.run.style = new_value 171 172 173@when('I clear the run') 174def when_I_clear_the_run(context): 175 context.run.clear() 176 177 178@when('I set the run underline to {underline_value}') 179def when_I_set_the_run_underline_to_value(context, underline_value): 180 new_value = { 181 'True': True, 'False': False, 'None': None, 182 'WD_UNDERLINE.SINGLE': WD_UNDERLINE.SINGLE, 183 'WD_UNDERLINE.DOUBLE': WD_UNDERLINE.DOUBLE, 184 }[underline_value] 185 context.run.underline = new_value 186 187 188# then ===================================================== 189 190@then('it is a column break') 191def then_type_is_column_break(context): 192 attrib = context.last_child.attrib 193 assert attrib == {qn('w:type'): 'column'} 194 195 196@then('it is a line break') 197def then_type_is_line_break(context): 198 attrib = context.last_child.attrib 199 assert attrib == {} 200 201 202@then('it is a page break') 203def then_type_is_page_break(context): 204 attrib = context.last_child.attrib 205 assert attrib == {qn('w:type'): 'page'} 206 207 208@then('run.font is the Font object for the run') 209def then_run_font_is_the_Font_object_for_the_run(context): 210 run, font = context.run, context.run.font 211 assert isinstance(font, Font) 212 assert font.element is run.element 213 214 215@then('run.style is styles[\'{style_name}\']') 216def then_run_style_is_style(context, style_name): 217 expected_value = context.document.styles[style_name] 218 run = context.run 219 assert run.style == expected_value, 'got %s' % run.style 220 221 222@then('the last item in the run is a break') 223def then_last_item_in_run_is_a_break(context): 224 run = context.run 225 context.last_child = run._r[-1] 226 expected_tag = ( 227 '{http://schemas.openxmlformats.org/wordprocessingml/2006/main}br' 228 ) 229 assert context.last_child.tag == expected_tag 230 231 232@then('the picture appears at the end of the run') 233def then_the_picture_appears_at_the_end_of_the_run(context): 234 run = context.run 235 r = run._r 236 blip_rId = r.xpath( 237 './w:drawing/wp:inline/a:graphic/a:graphicData/pic:pic/pic:blipFill/' 238 'a:blip/@r:embed' 239 )[0] 240 image_part = run.part.related_parts[blip_rId] 241 image_sha1 = hashlib.sha1(image_part.blob).hexdigest() 242 expected_sha1 = '79769f1e202add2e963158b532e36c2c0f76a70c' 243 assert image_sha1 == expected_sha1, ( 244 "image SHA1 doesn't match, expected %s, got %s" % 245 (expected_sha1, image_sha1) 246 ) 247 248 249@then('the run appears in {boolean_prop_name} unconditionally') 250def then_run_appears_in_boolean_prop_name(context, boolean_prop_name): 251 run = context.run 252 assert getattr(run, boolean_prop_name) is True 253 254 255@then('the run appears with its inherited {boolean_prop_name} setting') 256def then_run_inherits_bool_prop_value(context, boolean_prop_name): 257 run = context.run 258 assert getattr(run, boolean_prop_name) is None 259 260 261@then('the run appears without {boolean_prop_name} unconditionally') 262def then_run_appears_without_bool_prop(context, boolean_prop_name): 263 run = context.run 264 assert getattr(run, boolean_prop_name) is False 265 266 267@then('the run contains no text') 268def then_the_run_contains_no_text(context): 269 assert context.run.text == '' 270 271 272@then('the run contains the text I specified') 273def then_the_run_contains_the_text_I_specified(context): 274 assert context.run.text == test_text 275 276 277@then('the run formatting is preserved') 278def then_the_run_formatting_is_preserved(context): 279 assert context.run.bold is True 280 assert context.run.italic is True 281 282 283@then('the run underline property value is {underline_value}') 284def then_the_run_underline_property_value_is(context, underline_value): 285 expected_value = { 286 'None': None, 'False': False, 'True': True, 287 'WD_UNDERLINE.DOUBLE': WD_UNDERLINE.DOUBLE 288 }[underline_value] 289 assert context.run.underline == expected_value 290 291 292@then('the tab appears at the end of the run') 293def then_the_tab_appears_at_the_end_of_the_run(context): 294 r = context.run._r 295 tab = r.find(qn('w:tab')) 296 assert tab is not None 297 298 299@then('the text of the run represents the textual run content') 300def then_the_text_of_the_run_represents_the_textual_run_content(context): 301 assert context.run.text == 'abc\tdef\nghi\njkl', ( 302 'got \'%s\'' % context.run.text 303 ) 304