From e2525b6150350cc52843fddf9ec29691c8565dcc Mon Sep 17 00:00:00 2001 From: Jens Schuppe Date: Mon, 29 Apr 2024 13:03:04 +0200 Subject: [PATCH] Replace tokens by splitting text runs/paragraphs instead of replacing --- .editorconfig | 349 +++--------------- .../DocumentRendererType/LocalUnoconv.php | 2 +- .../LocalUnoconv/PhpWordTemplateProcessor.php | 319 ++++++++++------ phpcs.xml.dist | 6 + 4 files changed, 274 insertions(+), 402 deletions(-) diff --git a/.editorconfig b/.editorconfig index e71715b..f5f7003 100644 --- a/.editorconfig +++ b/.editorconfig @@ -3,251 +3,29 @@ charset = utf-8 end_of_line = lf indent_size = 2 indent_style = space -insert_final_newline = false -max_line_length = 80 +insert_final_newline = true +max_line_length = 120 tab_width = 2 -ij_continuation_indent_size = 8 -ij_formatter_off_tag = @formatter:off -ij_formatter_on_tag = @formatter:on -ij_formatter_tags_enabled = false -ij_smart_tabs = false -ij_wrap_on_typing = false - -[*.css] ij_continuation_indent_size = 2 -ij_css_align_closing_brace_with_properties = false -ij_css_blank_lines_around_nested_selector = 1 -ij_css_blank_lines_between_blocks = 0 -ij_css_brace_placement = end_of_line -ij_css_enforce_quotes_on_format = false -ij_css_hex_color_long_format = false -ij_css_hex_color_lower_case = true -ij_css_hex_color_short_format = true -ij_css_hex_color_upper_case = false -ij_css_keep_blank_lines_in_code = 2 -ij_css_keep_indents_on_empty_lines = false -ij_css_keep_single_line_blocks = true -ij_css_properties_order = font, font-family, font-size, font-weight, font-style, font-variant, font-size-adjust, font-stretch, line-height, position, z-index, top, right, bottom, left, display, visibility, float, clear, overflow, overflow-x, overflow-y, clip, zoom, align-content, align-items, align-self, flex, flex-flow, flex-basis, flex-direction, flex-grow, flex-shrink, flex-wrap, justify-content, order, box-sizing, width, min-width, max-width, height, min-height, max-height, margin, margin-top, margin-right, margin-bottom, margin-left, padding, padding-top, padding-right, padding-bottom, padding-left, table-layout, empty-cells, caption-side, border-spacing, border-collapse, list-style, list-style-position, list-style-type, list-style-image, content, quotes, counter-reset, counter-increment, resize, cursor, user-select, nav-index, nav-up, nav-right, nav-down, nav-left, transition, transition-delay, transition-timing-function, transition-duration, transition-property, transform, transform-origin, animation, animation-name, animation-duration, animation-play-state, animation-timing-function, animation-delay, animation-iteration-count, animation-direction, text-align, text-align-last, vertical-align, white-space, text-decoration, text-emphasis, text-emphasis-color, text-emphasis-style, text-emphasis-position, text-indent, text-justify, letter-spacing, word-spacing, text-outline, text-transform, text-wrap, text-overflow, text-overflow-ellipsis, text-overflow-mode, word-wrap, word-break, tab-size, hyphens, pointer-events, opacity, color, border, border-width, border-style, border-color, border-top, border-top-width, border-top-style, border-top-color, border-right, border-right-width, border-right-style, border-right-color, border-bottom, border-bottom-width, border-bottom-style, border-bottom-color, border-left, border-left-width, border-left-style, border-left-color, border-radius, border-top-left-radius, border-top-right-radius, border-bottom-right-radius, border-bottom-left-radius, border-image, border-image-source, border-image-slice, border-image-width, border-image-outset, border-image-repeat, outline, outline-width, outline-style, outline-color, outline-offset, background, background-color, background-image, background-repeat, background-attachment, background-position, background-position-x, background-position-y, background-clip, background-origin, background-size, box-decoration-break, box-shadow, text-shadow -ij_css_space_after_colon = true -ij_css_space_before_opening_brace = true -ij_css_use_double_quotes = true -ij_css_value_alignment = do_not_align - -[{*.ant, *.fxml, *.jhm, *.jnlp, *.jrxml, *.rng, *.tld, *.wsdl, *.xml, *.xsd, *.xsl, *.xslt, *.xul, phpunit.xml.dist}] -ij_continuation_indent_size = 4 -ij_xml_align_attributes = true -ij_xml_align_text = false -ij_xml_attribute_wrap = normal -ij_xml_block_comment_at_first_column = true -ij_xml_keep_blank_lines = 2 -ij_xml_keep_indents_on_empty_lines = false -ij_xml_keep_line_breaks = true -ij_xml_keep_line_breaks_in_text = true -ij_xml_keep_whitespaces = false -ij_xml_keep_whitespaces_around_cdata = preserve -ij_xml_keep_whitespaces_inside_cdata = false -ij_xml_line_comment_at_first_column = true -ij_xml_space_after_tag_name = false -ij_xml_space_around_equals_in_attribute = false -ij_xml_space_inside_empty_tag = false -ij_xml_text_wrap = normal +ij_visual_guides = 80,120 -[{*.civbanking, *.civibanking, *.har, *.jsb2, *.jsb3, *.json, .babelrc, .eslintrc, .stylelintrc, bowerrc, composer.lock, jest.config}] -ij_continuation_indent_size = 4 -ij_json_keep_blank_lines_in_code = 0 -ij_json_keep_indents_on_empty_lines = false -ij_json_keep_line_breaks = true -ij_json_space_after_colon = true -ij_json_space_after_comma = true -ij_json_space_before_colon = true -ij_json_space_before_comma = false -ij_json_spaces_within_braces = false -ij_json_spaces_within_brackets = false -ij_json_wrap_long_lines = false - -[{*.cjs, *.js}] -ij_continuation_indent_size = 2 -ij_javascript_align_imports = false -ij_javascript_align_multiline_array_initializer_expression = false -ij_javascript_align_multiline_binary_operation = false -ij_javascript_align_multiline_chained_methods = false -ij_javascript_align_multiline_extends_list = false -ij_javascript_align_multiline_for = true -ij_javascript_align_multiline_parameters = true -ij_javascript_align_multiline_parameters_in_calls = false -ij_javascript_align_multiline_ternary_operation = false -ij_javascript_align_object_properties = 0 -ij_javascript_align_union_types = false -ij_javascript_align_var_statements = 0 -ij_javascript_array_initializer_new_line_after_left_brace = false -ij_javascript_array_initializer_right_brace_on_new_line = false -ij_javascript_array_initializer_wrap = off -ij_javascript_assignment_wrap = off -ij_javascript_binary_operation_sign_on_next_line = false -ij_javascript_binary_operation_wrap = off -ij_javascript_blacklist_imports = rxjs/Rx, node_modules/**, **/node_modules/**, @angular/material, @angular/material/typings/** -ij_javascript_blank_lines_after_imports = 1 -ij_javascript_blank_lines_around_class = 1 -ij_javascript_blank_lines_around_field = 0 -ij_javascript_blank_lines_around_function = 1 -ij_javascript_blank_lines_around_method = 1 -ij_javascript_block_brace_style = end_of_line -ij_javascript_call_parameters_new_line_after_left_paren = false -ij_javascript_call_parameters_right_paren_on_new_line = false -ij_javascript_call_parameters_wrap = off -ij_javascript_catch_on_new_line = true -ij_javascript_chained_call_dot_on_new_line = true -ij_javascript_class_brace_style = end_of_line -ij_javascript_comma_on_new_line = false -ij_javascript_do_while_brace_force = always -ij_javascript_else_on_new_line = true -ij_javascript_enforce_trailing_comma = keep -ij_javascript_extends_keyword_wrap = off -ij_javascript_extends_list_wrap = off -ij_javascript_field_prefix = _ -ij_javascript_file_name_style = relaxed -ij_javascript_finally_on_new_line = true -ij_javascript_for_brace_force = always -ij_javascript_for_statement_new_line_after_left_paren = false -ij_javascript_for_statement_right_paren_on_new_line = false -ij_javascript_for_statement_wrap = off -ij_javascript_force_quote_style = false -ij_javascript_force_semicolon_style = false -ij_javascript_function_expression_brace_style = end_of_line -ij_javascript_if_brace_force = always -ij_javascript_import_merge_members = global -ij_javascript_import_prefer_absolute_path = global -ij_javascript_import_sort_members = true -ij_javascript_import_sort_module_name = false -ij_javascript_import_use_node_resolution = true -ij_javascript_imports_wrap = on_every_item -ij_javascript_indent_case_from_switch = true -ij_javascript_indent_chained_calls = true -ij_javascript_indent_package_children = 0 -ij_javascript_jsx_attribute_value = braces -ij_javascript_keep_blank_lines_in_code = 2 -ij_javascript_keep_first_column_comment = true -ij_javascript_keep_indents_on_empty_lines = false -ij_javascript_keep_line_breaks = true -ij_javascript_keep_simple_blocks_in_one_line = false -ij_javascript_keep_simple_methods_in_one_line = false -ij_javascript_line_comment_add_space = true -ij_javascript_line_comment_at_first_column = false -ij_javascript_method_brace_style = end_of_line -ij_javascript_method_call_chain_wrap = off -ij_javascript_method_parameters_new_line_after_left_paren = false -ij_javascript_method_parameters_right_paren_on_new_line = false -ij_javascript_method_parameters_wrap = off -ij_javascript_object_literal_wrap = on_every_item -ij_javascript_parentheses_expression_new_line_after_left_paren = false -ij_javascript_parentheses_expression_right_paren_on_new_line = false -ij_javascript_place_assignment_sign_on_next_line = false -ij_javascript_prefer_as_type_cast = false -ij_javascript_prefer_explicit_types_function_expression_returns = false -ij_javascript_prefer_explicit_types_function_returns = false -ij_javascript_prefer_explicit_types_vars_fields = false -ij_javascript_prefer_parameters_wrap = false -ij_javascript_reformat_c_style_comments = false -ij_javascript_space_after_colon = true -ij_javascript_space_after_comma = true -ij_javascript_space_after_dots_in_rest_parameter = false -ij_javascript_space_after_generator_mult = true -ij_javascript_space_after_property_colon = true -ij_javascript_space_after_quest = true -ij_javascript_space_after_type_colon = true -ij_javascript_space_after_unary_not = false -ij_javascript_space_before_async_arrow_lparen = true -ij_javascript_space_before_catch_keyword = true -ij_javascript_space_before_catch_left_brace = true -ij_javascript_space_before_catch_parentheses = true -ij_javascript_space_before_class_lbrace = true -ij_javascript_space_before_class_left_brace = true -ij_javascript_space_before_colon = true -ij_javascript_space_before_comma = false -ij_javascript_space_before_do_left_brace = true -ij_javascript_space_before_else_keyword = true -ij_javascript_space_before_else_left_brace = true -ij_javascript_space_before_finally_keyword = true -ij_javascript_space_before_finally_left_brace = true -ij_javascript_space_before_for_left_brace = true -ij_javascript_space_before_for_parentheses = true -ij_javascript_space_before_for_semicolon = false -ij_javascript_space_before_function_left_parenth = true -ij_javascript_space_before_generator_mult = false -ij_javascript_space_before_if_left_brace = true -ij_javascript_space_before_if_parentheses = true -ij_javascript_space_before_method_call_parentheses = false -ij_javascript_space_before_method_left_brace = true -ij_javascript_space_before_method_parentheses = false -ij_javascript_space_before_property_colon = false -ij_javascript_space_before_quest = true -ij_javascript_space_before_switch_left_brace = true -ij_javascript_space_before_switch_parentheses = true -ij_javascript_space_before_try_left_brace = true -ij_javascript_space_before_type_colon = false -ij_javascript_space_before_unary_not = false -ij_javascript_space_before_while_keyword = true -ij_javascript_space_before_while_left_brace = true -ij_javascript_space_before_while_parentheses = true -ij_javascript_spaces_around_additive_operators = true -ij_javascript_spaces_around_arrow_function_operator = true -ij_javascript_spaces_around_assignment_operators = true -ij_javascript_spaces_around_bitwise_operators = true -ij_javascript_spaces_around_equality_operators = true -ij_javascript_spaces_around_logical_operators = true -ij_javascript_spaces_around_multiplicative_operators = true -ij_javascript_spaces_around_relational_operators = true -ij_javascript_spaces_around_shift_operators = true -ij_javascript_spaces_around_unary_operator = false -ij_javascript_spaces_within_array_initializer_brackets = false -ij_javascript_spaces_within_brackets = false -ij_javascript_spaces_within_catch_parentheses = false -ij_javascript_spaces_within_for_parentheses = false -ij_javascript_spaces_within_if_parentheses = false -ij_javascript_spaces_within_imports = false -ij_javascript_spaces_within_interpolation_expressions = false -ij_javascript_spaces_within_method_call_parentheses = false -ij_javascript_spaces_within_method_parentheses = false -ij_javascript_spaces_within_object_literal_braces = false -ij_javascript_spaces_within_object_type_braces = true -ij_javascript_spaces_within_parentheses = false -ij_javascript_spaces_within_switch_parentheses = false -ij_javascript_spaces_within_type_assertion = false -ij_javascript_spaces_within_union_types = true -ij_javascript_spaces_within_while_parentheses = false -ij_javascript_special_else_if_treatment = true -ij_javascript_ternary_operation_signs_on_next_line = false -ij_javascript_ternary_operation_wrap = off -ij_javascript_union_types_wrap = on_every_item -ij_javascript_use_chained_calls_group_indents = false -ij_javascript_use_double_quotes = true -ij_javascript_use_explicit_js_extension = global -ij_javascript_use_path_mapping = always -ij_javascript_use_public_modifier = false -ij_javascript_use_semicolon_after_statement = true -ij_javascript_var_declaration_wrap = normal -ij_javascript_while_brace_force = always -ij_javascript_while_on_new_line = false -ij_javascript_wrap_comments = true - -[{*.ctp, *.engine, *.hphp, *.inc, *.install, *.module, *.php, *.php4, *.php5, *.phtml, *.profile, *.test, *.theme}] -indent_size = 4 -max_line_length = 120 -tab_width = 4 -ij_continuation_indent_size = 4 +[{*.php}] ij_php_align_assignments = false ij_php_align_class_constants = false +ij_php_align_enum_cases = false ij_php_align_group_field_declarations = false ij_php_align_inline_comments = false ij_php_align_key_value_pairs = false +ij_php_align_match_arm_bodies = false ij_php_align_multiline_array_initializer_expression = false ij_php_align_multiline_binary_operation = false ij_php_align_multiline_chained_methods = false -ij_php_align_multiline_extends_list = true +ij_php_align_multiline_extends_list = false ij_php_align_multiline_for = true ij_php_align_multiline_parameters = false -ij_php_align_multiline_parameters_in_calls = true +ij_php_align_multiline_parameters_in_calls = false ij_php_align_multiline_ternary_operation = false +ij_php_align_named_arguments = false ij_php_align_phpdoc_comments = false ij_php_align_phpdoc_param_names = false ij_php_anonymous_brace_style = end_of_line @@ -255,50 +33,59 @@ ij_php_api_weight = 28 ij_php_array_initializer_new_line_after_left_brace = true ij_php_array_initializer_right_brace_on_new_line = true ij_php_array_initializer_wrap = on_every_item -ij_php_assignment_wrap = off +ij_php_assignment_wrap = normal +ij_php_attributes_wrap = normal ij_php_author_weight = 28 ij_php_binary_operation_sign_on_next_line = false -ij_php_binary_operation_wrap = off -ij_php_blank_lines_after_class_header = 0 +ij_php_binary_operation_wrap = normal +ij_php_blank_lines_after_class_header = 1 ij_php_blank_lines_after_function = 1 ij_php_blank_lines_after_imports = 1 -ij_php_blank_lines_after_opening_tag = 1 +ij_php_blank_lines_after_opening_tag = 0 ij_php_blank_lines_after_package = 1 ij_php_blank_lines_around_class = 1 ij_php_blank_lines_around_constants = 1 +ij_php_blank_lines_around_enum_cases = 0 ij_php_blank_lines_around_field = 1 ij_php_blank_lines_around_method = 1 -ij_php_blank_lines_before_class_end = 0 +ij_php_blank_lines_before_class_end = 1 ij_php_blank_lines_before_imports = 1 ij_php_blank_lines_before_method_body = 0 ij_php_blank_lines_before_package = 1 -ij_php_blank_lines_before_return_statement = 0 -ij_php_blank_lines_between_imports = 1 +ij_php_blank_lines_before_return_statement = 1 +ij_php_blank_lines_between_imports = 0 ij_php_block_brace_style = end_of_line ij_php_call_parameters_new_line_after_left_paren = true ij_php_call_parameters_right_paren_on_new_line = true ij_php_call_parameters_wrap = on_every_item -ij_php_catch_on_new_line = false +ij_php_catch_on_new_line = true ij_php_category_weight = 28 -ij_php_class_brace_style = next_line +ij_php_class_brace_style = end_of_line +ij_php_comma_after_last_argument = false ij_php_comma_after_last_array_element = true +ij_php_comma_after_last_closure_use_var = false +ij_php_comma_after_last_match_arm = false +ij_php_comma_after_last_parameter = false ij_php_concat_spaces = true ij_php_copyright_weight = 28 ij_php_deprecated_weight = 4 ij_php_do_while_brace_force = always -ij_php_else_if_style = combine -ij_php_else_on_new_line = false +ij_php_else_if_style = as_is +ij_php_else_on_new_line = true ij_php_example_weight = 28 ij_php_extends_keyword_wrap = off -ij_php_extends_list_wrap = on_every_item +ij_php_extends_list_wrap = off ij_php_fields_default_visibility = private ij_php_filesource_weight = 28 ij_php_finally_on_new_line = true ij_php_for_brace_force = always -ij_php_for_statement_new_line_after_left_paren = true -ij_php_for_statement_right_paren_on_new_line = true +ij_php_for_statement_new_line_after_left_paren = false +ij_php_for_statement_right_paren_on_new_line = false ij_php_for_statement_wrap = off +ij_php_force_empty_methods_in_one_line = false ij_php_force_short_declaration_array_style = true +ij_php_getters_setters_naming_style = camel_case +ij_php_getters_setters_order_style = getters_first ij_php_global_weight = 28 ij_php_group_use_wrap = on_every_item ij_php_if_brace_force = always @@ -310,37 +97,39 @@ ij_php_indent_break_from_case = true ij_php_indent_case_from_switch = true ij_php_indent_code_in_php_tags = false ij_php_internal_weight = 28 -ij_php_keep_blank_lines_after_lbrace = 0 -ij_php_keep_blank_lines_before_right_brace = 0 -ij_php_keep_blank_lines_in_code = 2 -ij_php_keep_blank_lines_in_declarations = 2 +ij_php_keep_blank_lines_after_lbrace = 1 +ij_php_keep_blank_lines_before_right_brace = 1 +ij_php_keep_blank_lines_in_code = 1 +ij_php_keep_blank_lines_in_declarations = 1 ij_php_keep_control_statement_in_one_line = false ij_php_keep_first_column_comment = false ij_php_keep_indents_on_empty_lines = false -ij_php_keep_line_breaks = true +ij_php_keep_line_breaks = false ij_php_keep_rparen_and_lbrace_on_one_line = true +ij_php_keep_simple_classes_in_one_line = false ij_php_keep_simple_methods_in_one_line = false ij_php_lambda_brace_style = end_of_line ij_php_license_weight = 28 ij_php_line_comment_add_space = false ij_php_line_comment_at_first_column = true ij_php_link_weight = 28 -ij_php_lower_case_boolean_const = true +ij_php_lower_case_boolean_const = false ij_php_lower_case_keywords = true -ij_php_lower_case_null_const = true -ij_php_method_brace_style = next_line +ij_php_lower_case_null_const = false +ij_php_method_brace_style = end_of_line ij_php_method_call_chain_wrap = on_every_item ij_php_method_parameters_new_line_after_left_paren = true ij_php_method_parameters_right_paren_on_new_line = true ij_php_method_parameters_wrap = on_every_item ij_php_method_weight = 28 ij_php_modifier_list_wrap = false -ij_php_multiline_chained_calls_semicolon_on_new_line = false +ij_php_multiline_chained_calls_semicolon_on_new_line = true ij_php_namespace_brace_style = 1 ij_php_new_line_after_php_opening_tag = true ij_php_null_type_position = in_the_end ij_php_package_weight = 28 ij_php_param_weight = 1 +ij_php_parameters_attributes_wrap = normal ij_php_parentheses_expression_new_line_after_left_paren = false ij_php_parentheses_expression_right_paren_on_new_line = false ij_php_phpdoc_blank_line_before_tags = true @@ -352,7 +141,7 @@ ij_php_phpdoc_param_spaces_between_type_and_name = 1 ij_php_phpdoc_use_fqcn = true ij_php_phpdoc_wrap_long_lines = true ij_php_place_assignment_sign_on_next_line = false -ij_php_place_parens_for_constructor = 0 +ij_php_place_parens_for_constructor = 1 ij_php_property_read_weight = 28 ij_php_property_weight = 28 ij_php_property_write_weight = 28 @@ -362,11 +151,13 @@ ij_php_see_weight = 5 ij_php_since_weight = 28 ij_php_sort_phpdoc_elements = true ij_php_space_after_colon = true +ij_php_space_after_colon_in_enum_backed_type = true +ij_php_space_after_colon_in_named_argument = true ij_php_space_after_colon_in_return_type = true ij_php_space_after_comma = true ij_php_space_after_for_semicolon = true ij_php_space_after_quest = true -ij_php_space_after_type_cast = false +ij_php_space_after_type_cast = true ij_php_space_after_unary_not = false ij_php_space_before_array_initializer_left_brace = false ij_php_space_before_catch_keyword = true @@ -375,6 +166,8 @@ ij_php_space_before_catch_parentheses = true ij_php_space_before_class_left_brace = true ij_php_space_before_closure_left_parenthesis = true ij_php_space_before_colon = true +ij_php_space_before_colon_in_enum_backed_type = false +ij_php_space_before_colon_in_named_argument = false ij_php_space_before_colon_in_return_type = false ij_php_space_before_comma = false ij_php_space_before_do_left_brace = true @@ -402,13 +195,14 @@ ij_php_space_before_while_parentheses = true ij_php_space_between_ternary_quest_and_colon = false ij_php_spaces_around_additive_operators = true ij_php_spaces_around_arrow = false -ij_php_spaces_around_assignment_in_declare = false +ij_php_spaces_around_assignment_in_declare = true ij_php_spaces_around_assignment_operators = true ij_php_spaces_around_bitwise_operators = true ij_php_spaces_around_equality_operators = true ij_php_spaces_around_logical_operators = true ij_php_spaces_around_multiplicative_operators = true ij_php_spaces_around_null_coalesce_operator = true +ij_php_spaces_around_pipe_in_union_type = false ij_php_spaces_around_relational_operators = true ij_php_spaces_around_shift_operators = true ij_php_spaces_around_unary_operator = false @@ -426,44 +220,21 @@ ij_php_spaces_within_switch_parentheses = false ij_php_spaces_within_while_parentheses = false ij_php_special_else_if_treatment = false ij_php_subpackage_weight = 28 -ij_php_ternary_operation_signs_on_next_line = false -ij_php_ternary_operation_wrap = off +ij_php_ternary_operation_signs_on_next_line = true +ij_php_ternary_operation_wrap = on_every_item ij_php_throws_weight = 3 ij_php_todo_weight = 6 +ij_php_treat_multiline_arrays_and_lambdas_multiline = false ij_php_unknown_tag_weight = 28 -ij_php_upper_case_boolean_const = false -ij_php_upper_case_null_const = false +ij_php_upper_case_boolean_const = true +ij_php_upper_case_null_const = true ij_php_uses_weight = 28 ij_php_var_weight = 0 -ij_php_variable_naming_style = mixed +ij_php_variable_naming_style = camel_case ij_php_version_weight = 28 ij_php_while_brace_force = always ij_php_while_on_new_line = false -[{*.htm, *.html, *.ng, *.sht, *.shtm, *.shtml}] -ij_continuation_indent_size = 4 -ij_html_add_new_line_before_tags = body, div, p, form, h1, h2, h3 -ij_html_align_attributes = true -ij_html_align_text = false -ij_html_attribute_wrap = normal -ij_html_block_comment_at_first_column = true -ij_html_do_not_align_children_of_min_lines = 0 -ij_html_do_not_break_if_inline_tags = title, h1, h2, h3, h4, h5, h6, p -ij_html_do_not_indent_children_of_tags = html, body, thead, tbody, tfoot -ij_html_enforce_quotes = false -ij_html_inline_tags = a, abbr, acronym, b, basefont, bdo, big, br, cite, cite, code, dfn, em, font, i, img, input, kbd, label, q, s, samp, select, small, span, strike, strong, sub, sup, textarea, tt, u, var -ij_html_keep_blank_lines = 2 -ij_html_keep_indents_on_empty_lines = false -ij_html_keep_line_breaks = true -ij_html_keep_line_breaks_in_text = true -ij_html_keep_whitespaces = false -ij_html_keep_whitespaces_inside = span, pre, textarea -ij_html_line_comment_at_first_column = true -ij_html_new_line_after_last_attribute = never -ij_html_new_line_before_first_attribute = never -ij_html_quote_style = double -ij_html_remove_new_line_before_tags = br -ij_html_space_after_tag_name = false -ij_html_space_around_equality_in_attribute = false -ij_html_space_inside_empty_tag = false -ij_html_text_wrap = normal +[{*.neon,*.neon.dist,*neon.template}] +indent_style = tab +tab_width = 4 diff --git a/CRM/Civioffice/DocumentRendererType/LocalUnoconv.php b/CRM/Civioffice/DocumentRendererType/LocalUnoconv.php index 6fbf47e..d831d1c 100644 --- a/CRM/Civioffice/DocumentRendererType/LocalUnoconv.php +++ b/CRM/Civioffice/DocumentRendererType/LocalUnoconv.php @@ -645,4 +645,4 @@ public static function defaultConfiguration(): array 'phpword_tokens' => false, ]; } -} \ No newline at end of file +} diff --git a/CRM/Civioffice/DocumentRendererType/LocalUnoconv/PhpWordTemplateProcessor.php b/CRM/Civioffice/DocumentRendererType/LocalUnoconv/PhpWordTemplateProcessor.php index ff3bdc0..60fafde 100644 --- a/CRM/Civioffice/DocumentRendererType/LocalUnoconv/PhpWordTemplateProcessor.php +++ b/CRM/Civioffice/DocumentRendererType/LocalUnoconv/PhpWordTemplateProcessor.php @@ -13,124 +13,219 @@ | written permission from the original author(s). | +-------------------------------------------------------*/ +declare(strict_types = 1); + use CRM_Civioffice_ExtensionUtil as E; use PhpOffice\PhpWord; -class CRM_Civioffice_DocumentRendererType_LocalUnoconv_PhpWordTemplateProcessor extends PhpWord\TemplateProcessor -{ - /** - * Replaces CiviCRM tokens with PhpWord macros (converts format from "{token}" to "${macro}"). - * - * @return array - * An array of CiviCRM tokens found in the document. - */ - public function civiTokensToMacros(): array - { - $tokens = []; - foreach ( - [ - &$this->tempDocumentHeaders, - &$this->tempDocumentMainPart, - &$this->tempDocumentFooters, - ] as &$tempDocPart - ) { - // Regex code borrowed from \Civi\Token\TokenProcessor::visitTokens(). - // Adapted to allow '"' instead of '"'. - - // The regex is a bit complicated, we so break it down into fragments. - // Consider the example '{foo.bar|whiz:"bang":"bang"}'. Each fragment matches the following: - $tokenRegex = '([\w]+)\.([\w:\.]+)'; /* MATCHES: 'foo.bar' */ - $filterArgRegex = ':([\w": %\-_()\[\]\+/#@!,\.\?]|")*'; /* MATCHES: ':"bang":"bang"' */ - // Key rule of filterArgRegex is to prohibit '{}'s because they may parse ambiguously. So you *might* relax - // it to: $filterArgRegex = ':[^{}\n]*'; /* MATCHES: ':"bang":"bang"' or ':"bang"' */ - $filterNameRegex = "\w+"; /* MATCHES: 'whiz' */ - $filterRegex = "\|($filterNameRegex(?:$filterArgRegex)?)"; /* MATCHES: '|whiz:"bang":"bang"' */ - $fullRegex = "~\{$tokenRegex(?:$filterRegex)?\}~"; - - $tempDocPart = preg_replace_callback( - $fullRegex, - /* - * The match contains: - * - $0: The entire token, possibly including filters, with surrounding "{" and "}" - * - $1: The token context (first part of the token) - * - $2: The token name (second part of the token) - * - $3: The filter, possibly including filter parameters - * - * We just prefix the token with a "$" as macro names can contain anything, - * @see \PhpOffice\PhpWord\TemplateProcessor::getVariablesForPart() - */ - function($matches) use (&$tokens) { - $token = str_replace('"', '"', $matches[0]); - $tokens[$token] = [ - 'entity' => $matches[1], - 'field' => $matches[2], - 'filter' => $matches[3] ?? NULL, - ]; - return '$' . $token; - }, - $tempDocPart - ); - } +class CRM_Civioffice_DocumentRendererType_LocalUnoconv_PhpWordTemplateProcessor extends PhpWord\TemplateProcessor { + + /** + * Replaces CiviCRM tokens with PhpWord macros (converts format from + * "{token}" to "${macro}"). + * + * @return array + * An array of CiviCRM tokens found in the document. + */ + public function civiTokensToMacros(): array { + $tokens = []; + foreach ( + [ + &$this->tempDocumentHeaders, + &$this->tempDocumentMainPart, + &$this->tempDocumentFooters, + ] as &$tempDocPart + ) { + // Regex code borrowed from \Civi\Token\TokenProcessor::visitTokens(). + // Adapted to allow '"' instead of '"'. - return $tokens; + // The regex is a bit complicated, we so break it down into fragments. + // Consider the example '{foo.bar|whiz:"bang":"bang"}'. Each fragment matches the following: + $tokenRegex = '([\w]+)\.([\w:\.]+)'; /* MATCHES: 'foo.bar' */ + $filterArgRegex = ':([\w": %\-_()\[\]\+/#@!,\.\?]|")*'; /* MATCHES: ':"bang":"bang"' */ + // Key rule of filterArgRegex is to prohibit '{}'s because they may parse ambiguously. So you *might* relax + // it to: + // MATCHES: ':"bang":"bang"' or ':"bang"' + // $filterArgRegex = ':[^{}\n]*'; + + // MATCHES: 'whiz' + $filterNameRegex = '\w+'; + $filterRegex = "\|($filterNameRegex(?:$filterArgRegex)?)"; /* MATCHES: '|whiz:"bang":"bang"' */ + $fullRegex = "~\{$tokenRegex(?:$filterRegex)?\}~"; + + $tempDocPart = preg_replace_callback( + $fullRegex, + /* + * The match contains: + * - $0: The entire token, possibly including filters, with surrounding "{" and "}" + * - $1: The token context (first part of the token) + * - $2: The token name (second part of the token) + * - $3: The filter, possibly including filter parameters + * + * We just prefix the token with a "$" as macro names can contain anything, + * @see \PhpOffice\PhpWord\TemplateProcessor::getVariablesForPart() + */ + function($matches) use (&$tokens) { + $token = str_replace('"', '"', $matches[0]); + $tokens[$token] = [ + 'entity' => $matches[1], + 'field' => $matches[2], + 'filter' => $matches[3] ?? NULL, + ]; + return '$' . $token; + }, + $tempDocPart + ); } - public function replaceHtmlToken($macro_variable, $rendered_token_message) { - static $phpWord; - if (!isset($phpWord)) { - $phpWord = new PhpWord\PhpWord(); - } + return $tokens; + } - $outputEscapingEnabled = PhpWord\Settings::isOutputEscapingEnabled(); - PhpWord\Settings::setOutputEscapingEnabled(true); - try { - // Use a temporary Section element for adding the elements. - $section = $phpWord->addSection(); - // Note: addHtml() does not accept styles, so added HTML elements do not get applied any existing - // styles. - PhpWord\Shared\Html::addHtml($section, $rendered_token_message); - $elements = $section->getElements(); - if ([] === $elements) { - $this->setValue($macro_variable, ''); - } else if (count($elements) === 1 && $elements[0] instanceof PhpWord\Element\Text) { - // ... either as plain text (if there is only a single Text element or nothing), ... - // Note: $rendered_token_message shouldn't be used directly because it may contain HTML entities. - $this->setValue($macro_variable, $elements[0]->getText()); - } else { - // ... or as HTML: Render all elements and replace the paragraph containing the macro. - // Note: This will remove the entire paragraph element around the macro. - // TODO: Save and split surrounding contents and add them to the replaced block. - // This would be a logical assumption, since HTML elements will always make for a new - // paragraph, moving text before and after the macro into their own paragraphs. - // See \PhpOffice\PhpWord\TemplateProcessor::setComplexValue(). - // Since Section is not in the required namespace for elements supported by this method, all - // elements contained in the $section will have to be wrapped inside a - // \PhpOffice\PhpWord\Writer\Word2007\Element\Container element, which in turn will have to be - // passed to \PhpOffice\PhpWord\TemplateProcessor::setComplexValue(). - $elements_data = ''; - foreach ($elements as $element) { - $elementName = substr( - get_class($element), - strrpos(get_class($element), '\\') + 1 - ); - $objectClass = 'PhpOffice\\PhpWord\\Writer\\Word2007\\Element\\' . $elementName; - - $xmlWriter = new PhpWord\Shared\XMLWriter(); - /** @var \PhpOffice\PhpWord\Writer\Word2007\Element\AbstractElement $elementWriter */ - $elementWriter = new $objectClass($xmlWriter, $element, false); - $elementWriter->write(); - $elements_data .= $xmlWriter->getData(); - } - $this->replaceXmlBlock($macro_variable, $elements_data, 'w:p'); - } - } catch (Exception $exception) { - throw new Exception( - E::ts('Error loading/writing PhpWord document: %1', [1 => $exception->getMessage()]), - $exception->getCode(), - $exception - ); - } finally { - PhpWord\Settings::setOutputEscapingEnabled($outputEscapingEnabled); + /** + * @param string $macroVariable + * @param string $renderedTokenMessage + * + * @return void + * @throws \CRM_Core_Exception + */ + public function replaceHtmlToken(string $macroVariable, string $renderedTokenMessage): void { + static $phpWord; + if (!isset($phpWord)) { + $phpWord = new PhpWord\PhpWord(); + } + + $outputEscapingEnabled = PhpWord\Settings::isOutputEscapingEnabled(); + PhpWord\Settings::setOutputEscapingEnabled(TRUE); + try { + // Use a temporary Section element for adding the elements. + $section = $phpWord->addSection(); + // Note: addHtml() does not accept styles, so added HTML elements do not get applied any existing + // styles. + PhpWord\Shared\Html::addHtml($section, $renderedTokenMessage); + $elements = $section->getElements(); + // setValue() and setElementsValue() replace only the first occurrence of macro variables in the document, so loop + // until all have been replaced. + do { + if ([] === $elements) { + // Note: If the paragraph had the macro as its only content, it + // will not be removed (i.e. leave an empty paragraph). + $this->setValue($macroVariable, ''); + } + elseif (count($elements) === 1 && $elements[0] instanceof PhpWord\Element\Text) { + // ... either as plain text (if there is only a single Text + // element, which can't have style properties), ... + // Note: $rendered_token_message shouldn't be used directly + // because it may contain HTML entities. + $this->setValue($macroVariable, $elements[0]->getText()); } + else { + // ... or as HTML: Render all elements and insert in the text + // run or paragraph containing the macro. + $this->setElementsValue($macroVariable, $elements); + } + } while (in_array($macroVariable, $this->getVariables(), TRUE)); + } + catch (Exception $exception) { + throw new CRM_Core_Exception( + E::ts('Error loading/writing PhpWord document: %1', [1 => $exception->getMessage()]), + $exception->getCode(), + [], + $exception + ); + } + finally { + PhpWord\Settings::setOutputEscapingEnabled($outputEscapingEnabled); } -} \ No newline at end of file + } + + /** + * Replaces a search string (macro) with a set of rendered elements, splitting + * surrounding texts, text runs or paragraphs before and after the macro, + * depending on the types of elements to insert. + * + * @param string $search + * @param \PhpOffice\PhpWord\Element\AbstractElement[] $elements + * + * @return void + * @throws \CRM_Core_Exception + */ + public function setElementsValue(string $search, array $elements) { + $elementsData = ''; + $hasParagraphs = FALSE; + foreach ($elements as $element) { + $elementName = substr( + get_class($element), + (int) strrpos(get_class($element), '\\') + 1 + ); + $objectClass = 'PhpOffice\\PhpWord\\Writer\\Word2007\\Element\\' . $elementName; + + // For inline elements, do not create a new paragraph. + $withParagraph = \PhpOffice\PhpWord\Writer\Word2007\Element\Text::class !== $objectClass; + $hasParagraphs = $hasParagraphs || $withParagraph; + + $xmlWriter = new PhpWord\Shared\XMLWriter(); + /** @var \PhpOffice\PhpWord\Writer\Word2007\Element\AbstractElement $elementWriter */ + $elementWriter = new $objectClass($xmlWriter, $element, !$withParagraph); + $elementWriter->write(); + $elementsData .= $xmlWriter->getData(); + } + $blockType = $hasParagraphs ? 'w:p' : 'w:r'; + $where = $this->findContainingXmlBlockForMacro($search, $blockType); + if (is_array($where)) { + /** @phpstan-var array{start: int, end: int} $where */ + $block = $this->getSlice($where['start'], $where['end']); + $parts = $hasParagraphs ? $this->splitParagraphIntoParagraphs($block) : $this->splitTextIntoTexts($block); + $this->replaceXmlBlock($search, $parts, $blockType); + $search = static::ensureMacroCompleted($search); + $this->replaceXmlBlock($search, $elementsData, $blockType); + } + } + + /** + * Splits a w:p into a list of w:p where each ${macro} is in a separate w:p. + * + * @param string $paragraph + * + * @return string + * @throws \PhpOffice\PhpWord\Exception\Exception + */ + public function splitParagraphIntoParagraphs(string $paragraph): string { + $matches = []; + if (1 === preg_match('/()/i', $paragraph, $matches)) { + $extractedStyle = $matches[0]; + } + else { + $extractedStyle = ''; + } + if (NULL === $paragraph = preg_replace('/>\s+<', $paragraph)) { + throw new PhpWord\Exception\Exception('Error processing PhpWord document.'); + } + $result = str_replace( + [ + '${', + '}', + ], + [ + '' . $extractedStyle . '${', + '}' . $extractedStyle . '', + ], + $paragraph + ); + + // Remove empty paragraphs that might have been created before/after the + // macro. + $result = str_replace( + [ + '' . $extractedStyle . '', + '', + ], + [ + '', + '', + ], + $result + ); + return $result; + } + +} diff --git a/phpcs.xml.dist b/phpcs.xml.dist index 95f1803..03f00d0 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -19,6 +19,12 @@ + + + + + +