diff --git a/src/libnrtype/Layout-TNG.h b/src/libnrtype/Layout-TNG.h index b7b025c8ee8bc8a2ba1549cbb50095d74e6bedbd..1289ab82e702c9d323d3e69c80d9232175e19548 100644 --- a/src/libnrtype/Layout-TNG.h +++ b/src/libnrtype/Layout-TNG.h @@ -805,7 +805,6 @@ private: Path const *_path_fitted = nullptr; public: - struct Glyph; struct Character; struct Span; struct Chunk; @@ -1009,6 +1008,9 @@ public: bool operator>= (iterator const &other) const {return _char_index >= other._char_index;} + int glyphIndex() const { return _glyph_index; } + bool hasGlyph() const { return _glyph_index >= 0; } + /* **** visual-oriented methods **** */ //glyphs diff --git a/src/text-chemistry.cpp b/src/text-chemistry.cpp index 1c02845d8efed35532a6570dd6a6a498369fd8fc..30e4b9af8ac96aaea8df679c1d0d8f56b4bee092 100644 --- a/src/text-chemistry.cpp +++ b/src/text-chemistry.cpp @@ -21,10 +21,12 @@ #include "desktop.h" #include "document-undo.h" #include "document.h" +#include "inkscape-application.h" #include "inkscape.h" #include "message-stack.h" #include "preferences.h" #include "selection.h" +#include "svg/svg.h" #include "text-chemistry.h" #include "text-editing.h" @@ -556,12 +558,11 @@ text_unflow () void text_to_glyphs() { - auto desktop = SP_ACTIVE_DESKTOP; - auto selection = desktop->getSelection(); + auto doc = SP_ACTIVE_DOCUMENT; + auto selection = InkscapeApplication::instance()->get_active_selection(); std::vector results; std::vector to_delete; - auto doc = desktop->getDocument(); auto xml_doc = doc->getReprDoc(); for(auto item : selection->items()) { @@ -575,7 +576,7 @@ text_to_glyphs() auto const &layout = text->layout; auto iter = layout.end(); while (iter != layout.begin()) { - + auto end = iter; // Glyph index may not be zero leading to an infinite loop // if we don't test this here... (see issue #4767). if (!iter.prevCharacter()) { @@ -585,8 +586,42 @@ text_to_glyphs() if (layout.isWhitespace(iter)) continue; - auto str = Glib::ustring(1, layout.characterAt(iter)); - auto point = layout.characterAnchorPoint(iter); + auto it_next = iter; + int last_glyph = iter.glyphIndex(); + while (it_next.prevCharacter()) { + // multiple characters single glyph ligature + bool multi_char_glyph = it_next.glyphIndex() == last_glyph; + // In some cases diacritics are separate glyphs and separate characters, but they can not + // be used independently without base character. + // Depends on specific font. + bool helper_char = !layout.isCursorPosition(iter); + if (multi_char_glyph || helper_char) { + iter = it_next; + } else { + break; + } + last_glyph = it_next.glyphIndex(); + } + // multiple glyphs from single character, get first glyph + while (iter.glyphIndex() > 0 && layout.glyphs()[iter.glyphIndex() - 1].in_character == + layout.glyphs()[iter.glyphIndex()].in_character) { + iter.prevGlyph(); + } + + Glib::ustring str; + for (auto char_iter = iter; char_iter < end;) { + if (layout.isWhitespace(char_iter)) { + break; + } + str.push_back(layout.characterAt(char_iter)); + if (!char_iter.nextCharacter()) { + break; // should not happen + } + } + if (!iter.hasGlyph()) { + continue; + } + auto glyph = layout.glyphs()[iter.glyphIndex()]; SPObject *tspan = nullptr; layout.getSourceOfCharacter(iter, &tspan); @@ -603,13 +638,21 @@ text_to_glyphs() } result_style->merge(text->style); result_style->text_anchor.read("start"); + // reset layout properties which are already included in glyph transform + result_style->writing_mode = SP_CSS_WRITING_MODE_LR_TB; + result_style->direction = SP_CSS_DIRECTION_LTR; Glib::ustring glyph_style = result_style->writeIfDiff(text->parent->style); delete result_style; new_node->setAttributeOrRemoveIfEmpty("style", glyph_style); - new_node->setAttributeOrRemoveIfEmpty("transform", text->getAttribute("transform")); - new_node->setAttributeSvgDouble("x", point[Geom::X]); - new_node->setAttributeSvgDouble("y", point[Geom::Y]); + auto const &glyph_span = glyph.span(&layout); + auto inverse_scale = 1.0 / glyph_span.font_size; + // not ideal, assumes that in case of 1 character -> n glyphs, the first glyph is the base glyph + // and has normal position + Geom::Affine transform = + Geom::Scale(inverse_scale, -inverse_scale) // prevent font size from being applied twice + * glyph.transform(layout) * text->transform; + new_node->setAttributeOrRemoveIfEmpty("transform", sp_svg_transform_write(transform)); new_node->appendChild(xml_doc->createTextNode(str.c_str())); // Store the new object for the selection and prepare for the next glyph @@ -627,8 +670,10 @@ text_to_glyphs() } if (results.empty()) { - desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, - _("Select text(s) to convert to glyphs.")); + auto desktop = SP_ACTIVE_DESKTOP; + if (desktop) { + desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select text(s) to convert to glyphs.")); + } } else { DocumentUndo::done(doc, _("Convert text to glyphs"), INKSCAPE_ICON("text-convert-to-regular")); selection->setList(results); diff --git a/testfiles/cli_tests/CMakeLists.txt b/testfiles/cli_tests/CMakeLists.txt index 03701d72bf8aef82f0552edc726e351206849d52..2d9f9202b567f05d2f1ce7dd0c124b79a8810459 100644 --- a/testfiles/cli_tests/CMakeLists.txt +++ b/testfiles/cli_tests/CMakeLists.txt @@ -12,6 +12,9 @@ # FUZZ_PERCENTAGE - maximum allowed normalized root-mean-squared distance between compared images # RASTER_DPI - DPI setting for rasterizing vector formats before root-mean-squared comparison # PARAMETERS - additional command line parameters to pass to Inkscape +# REFERENCE_INKSCAPE - rasterize REFERENCE_FILENAME using inkscape +# Avoid this when possible! Preferably reference should be a raster image and when that's not +# practical rasterized using third party rasterizer instead of inkscape. # # Pass/fail criteria: # PASS_FOR_OUTPUT - pass if output matches the given value, otherwise fail @@ -50,7 +53,8 @@ endfunction(add_cli_test) # function(add_cli_test_run fixture testname) # parse arguments - set(oneValueArgs INPUT_FILENAME OUTPUT_FILENAME PASS_FOR_OUTPUT FAIL_FOR_OUTPUT) + set(options REFERENCE_INKSCAPE) + set(oneValueArgs INPUT_FILENAME OUTPUT_FILENAME PASS_FOR_OUTPUT FAIL_FOR_OUTPUT REFERENCE_FILENAME) set(multiValueArgs PARAMETERS ENVIRONMENT EXPECTED_FILES TEST_SCRIPT) cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) set(CMAKE_CTEST_ENV "INKSCAPE_FONTCONFIG=${CMAKE_CURRENT_SOURCE_DIR}/../rendering_tests/fonts/isolated.conf;${CMAKE_CTEST_ENV}") @@ -91,6 +95,15 @@ function(add_cli_test_run fixture testname) set_tests_properties(${testname} PROPERTIES FAIL_REGULAR_EXPRESSION ${ARG_FAIL_FOR_OUTPUT}) endif() + if (${ARG_REFERENCE_INKSCAPE} AND DEFINED ARG_REFERENCE_FILENAME) + file(TO_NATIVE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/testcases/${ARG_REFERENCE_FILENAME}" REFERENCE_FILENAME) + set(ARG_PARAMETERS ${REFERENCE_FILENAME} "--export-png-use-dithering=false" + "--export-filename=${ARG_REFERENCE_FILENAME}.reference.png" "--export-make-paths") + add_test(NAME ${testname}_ref COMMAND inkscape ${ARG_PARAMETERS}) + set_tests_properties(${testname}_ref PROPERTIES + FIXTURES_SETUP ${fixture} + ENVIRONMENT "${CMAKE_CTEST_ENV}") + endif() endfunction(add_cli_test_run) # @@ -98,6 +111,7 @@ endfunction(add_cli_test_run) # function(add_cli_test_cmp fixture testname) # parse arguments + set(options REFERENCE_INKSCAPE) set(oneValueArgs OUTPUT_FILENAME OUTPUT_PAGE REFERENCE_FILENAME FUZZYREF_FILENAME FUZZ_PERCENTAGE RASTER_DPI PNG_FILENAME) set(multiValueArgs EXPECTED_FILES TEST_SCRIPT) @@ -118,7 +132,11 @@ function(add_cli_test_cmp fixture testname) # add test to check output files if(DEFINED ARG_REFERENCE_FILENAME OR DEFINED ARG_EXPECTED_FILES OR DEFINED ARG_TEST_SCRIPT) if(DEFINED ARG_REFERENCE_FILENAME) - file(TO_NATIVE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/testcases/${ARG_REFERENCE_FILENAME}" ARG_REFERENCE_FILENAME) + if (${ARG_REFERENCE_INKSCAPE}) + set(ARG_REFERENCE_FILENAME "${ARG_REFERENCE_FILENAME}.reference.png") + else() + file(TO_NATIVE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/testcases/${ARG_REFERENCE_FILENAME}" ARG_REFERENCE_FILENAME) + endif() endif() if(DEFINED ARG_EXPECTED_FILES) string(REPLACE ";" " " ARG_EXPECTED_FILES "${ARG_EXPECTED_FILES}") @@ -159,7 +177,8 @@ endfunction(add_cli_test_cmp) # Delete all the generated files # function(add_cli_test_cln fixture testname) - set(oneValueArgs INPUT_FILENAME OUTPUT_FILENAME PASS_FOR_OUTPUT FAIL_FOR_OUTPUT) + set(options REFERENCE_INKSCAPE) + set(oneValueArgs INPUT_FILENAME OUTPUT_FILENAME PASS_FOR_OUTPUT FAIL_FOR_OUTPUT REFERENCE_FILENAME) set(multiValueArgs PARAMETERS ENVIRONMENT EXPECTED_FILES TEST_SCRIPT) cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) @@ -170,6 +189,9 @@ function(add_cli_test_cln fixture testname) if(DEFINED ARG_EXPECTED_FILES) list(APPEND CREATED_FILES ${ARG_EXPECTED_FILES}) endif() + if (${ARG_REFERENCE_INKSCAPE} AND DEFINED ARG_REFERENCE_FILENAME) + list(APPEND CREATED_FILES ${ARG_REFERENCE_FILENAME}.reference.png) + endif() if(DEFINED CREATED_FILES) add_test("${testname}" rm -f ${CREATED_FILES}) set_tests_properties(${testname} PROPERTIES @@ -1039,6 +1061,16 @@ add_cli_test(actions-svginot-text-topath2 FUZZ_PERCENTAGE 1 ) +add_cli_test(actions-text-to-glyph + INPUT_FILENAME text_to_glyph.svg + PARAMETERS + --actions=select-all$text-convert-to-glyphs$select-clear$select-by-id:text_o_1$select-by-id:text_o_2$select-by-id:text_o_3$select-by-id:text_o_4$delete$ + OUTPUT_FILENAME text_to_glyph.png + REFERENCE_FILENAME text_to_glyph_expected.svgz REFERENCE_INKSCAPE + FUZZ_PERCENTAGE 8 +) + + # The export area type should be the last set value add_cli_test(actions-export-area-drawing-then-page INPUT_FILENAME areas.svg diff --git a/testfiles/cli_tests/testcases/text_to_glyph.svg b/testfiles/cli_tests/testcases/text_to_glyph.svg new file mode 100644 index 0000000000000000000000000000000000000000..33634010bb79a58734cdca5726f06625deaf5349 --- /dev/null +++ b/testfiles/cli_tests/testcases/text_to_glyph.svg @@ -0,0 +1,173 @@ + + + + + + + + fffiflafffiflaāa b eaלּaלּלּa aͨbͩcͤr̷͓͂r̷͓͂ a a + fffiflafffiflaāa b eaלּaלּלּa aͨbͩcͤr̷͓͂r̷͓͂ a a + fffiflafffiflaāa b eaלּaלּלּa aͨbͩcͤr̷͓͂r̷͓͂ a a + ab + + + diff --git a/testfiles/cli_tests/testcases/text_to_glyph_expected.svgz b/testfiles/cli_tests/testcases/text_to_glyph_expected.svgz new file mode 100644 index 0000000000000000000000000000000000000000..a87c21c07a217d5d2b6214d3cbe3b483e923da97 Binary files /dev/null and b/testfiles/cli_tests/testcases/text_to_glyph_expected.svgz differ