diff options
| author | Jabier Arraiza <jabier.arraiza@marker.es> | 2017-06-12 18:20:56 +0000 |
|---|---|---|
| committer | Jabier Arraiza <jabier.arraiza@marker.es> | 2017-06-12 18:20:56 +0000 |
| commit | 59de64ad8af1ef0527100ffe09b4e85617c3226c (patch) | |
| tree | 89ed20b19fcefb4e71578a37a235a15aa9450933 | |
| parent | adding last changes (diff) | |
| parent | Small fixes to README (diff) | |
| download | inkscape-59de64ad8af1ef0527100ffe09b4e85617c3226c.tar.gz inkscape-59de64ad8af1ef0527100ffe09b4e85617c3226c.zip | |
Updating to master
| -rw-r--r-- | .gitignore | 5 | ||||
| -rw-r--r-- | .gitlab-ci.yml | 38 | ||||
| -rw-r--r-- | CMakeScripts/HelperFunctions.cmake | 27 | ||||
| -rw-r--r-- | CMakeScripts/InstallMSYS2.cmake | 6 | ||||
| -rw-r--r-- | README | 9 | ||||
| -rw-r--r-- | appveyor.sh | 38 | ||||
| -rw-r--r-- | appveyor.yml | 22 | ||||
| -rwxr-xr-x | msys2installdeps.sh | 20 | ||||
| -rw-r--r-- | src/libnrtype/Layout-TNG-Compute.cpp | 1423 | ||||
| -rw-r--r-- | src/libnrtype/Layout-TNG-Input.cpp | 113 | ||||
| -rw-r--r-- | src/libnrtype/Layout-TNG-Output.cpp | 12 | ||||
| -rw-r--r-- | src/libnrtype/Layout-TNG.h | 4 | ||||
| -rw-r--r-- | src/live_effects/lpe-bool.h | 2 | ||||
| -rw-r--r-- | src/sp-flowtext.cpp | 4 | ||||
| -rw-r--r-- | src/sp-text.cpp | 8 |
15 files changed, 970 insertions, 761 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..619692120 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +share/filters/filters.svg.h +share/palettes/palettes.h +share/patterns/patterns.svg.h +share/symbols/symbols.h +share/templates/templates.h diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 000000000..34db594a4 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,38 @@ +image: ubuntu:16.04 + +before_script: + - apt-get update -yqq + # Tools + - apt-get install -y -qq cmake intltool pkg-config python-dev libtool ccache + # Libraries + - apt-get install -y -qq libart-2.0-dev libaspell-dev libboost-dev libcdr-dev libgc-dev libgdl-3-dev libglib2.0-dev libgnomevfs2-dev libgsl-dev libgtk-3-dev libgtkmm-3.0-dev libgtkspell-dev liblcms2-dev libmagick++-dev libpango1.0-dev libpng12-dev libpoppler-glib-dev libpoppler-private-dev libpopt-dev libpotrace-dev librevenge-dev libsigc++-2.0-dev libvisio-dev libwpg-dev libxml-parser-perl libxml2-dev libxslt1-dev libyaml-dev python-lxml zlib1g-dev + # Testing Tools + - apt-get install -y -qq google-mock libgtest-dev + # CCache Config + - mkdir -p ccache + - export CCACHE_BASEDIR=${PWD} + - export CCACHE_DIR=${PWD}/ccache + +cache: + paths: + - ccache/ + +inkscape: + stage: build + script: + - mkdir -p build + - cd build + - cmake .. -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_BUILD_TYPE=Debug + - make + - cd .. + - tar vfcz inkscape-build.tgz build/ + artifacts: + paths: + - inkscape-build.tgz + +test: + stage: test + script: + - tar xvfz inkscape-build.tgz + - cd build + - make test diff --git a/CMakeScripts/HelperFunctions.cmake b/CMakeScripts/HelperFunctions.cmake index 3cd9e2736..a9d7e0ab1 100644 --- a/CMakeScripts/HelperFunctions.cmake +++ b/CMakeScripts/HelperFunctions.cmake @@ -35,16 +35,27 @@ endfunction() # Checks if the last call to execute_process() was sucessful and throws an error otherwise. # ${result} and ${stderr} should hold the value of RESULT_VARIABLE and ERROR_VARIABLE respectively -function(check_error result stderr) +# ${command} can be empty or the command that was executed during the last call of execute_process() +function(check_error result stderr command) + string(STRIP "${result}" result) + string(STRIP "${stderr}" stderr) + string(STRIP "${command}" command) + + if ("${command}" STREQUAL "") + set(command "Process") + else() + set(command "'${command}'") + endif() + if("${result}" STREQUAL 0) if(NOT "${stderr}" STREQUAL "") - MESSAGE(WARNING "Process returned sucessfully but the following was output to stderr: ${stderr}") + MESSAGE(WARNING "${command} returned sucessfully but the following was output to stderr: ${stderr}") endif() else() if("${stderr}" STREQUAL "") - MESSAGE(FATAL_ERROR "Process failed with error code: ${result}") + MESSAGE(FATAL_ERROR "${command} failed with error code: ${result}") else() - MESSAGE(FATAL_ERROR "Process failed with error code: ${result} (stderr: ${stderr})") + MESSAGE(FATAL_ERROR "${command} failed with error code: ${result} (stderr: ${stderr})") endif() endif() endfunction(check_error) @@ -64,7 +75,7 @@ function(list_files_pacman package_name file_list) RESULT_VARIABLE res ERROR_VARIABLE err ) - check_error("${res}" "${err}") + check_error("${res}" "${err}" "pacman -Ql ${MINGW_PACKAGE_PREFIX}-${package_name}") # clean up output execute_process( @@ -77,7 +88,7 @@ function(list_files_pacman package_name file_list) RESULT_VARIABLE res ERROR_VARIABLE err ) - check_error("${res}" "${err}") + check_error("${res}" "${err}" "Parsing result of 'pacman -Ql ${MINGW_PACKAGE_PREFIX}-${package_name}'") SET(${file_list} ${out} PARENT_SCOPE) file(REMOVE ${CMAKE_CURRENT_BINARY_DIR}/list_files_pacman_temp.txt) @@ -95,7 +106,7 @@ function(list_files_pip package_name file_list) RESULT_VARIABLE res ERROR_VARIABLE err ) - check_error("${res}" "${err}") + check_error("${res}" "${err}" "pip show -f ${package_name}") # clean up output execute_process( @@ -107,7 +118,7 @@ function(list_files_pip package_name file_list) RESULT_VARIABLE res ERROR_VARIABLE err ) - check_error("${res}" "${err}") + check_error("${res}" "${err}" "Parsing result of 'pip show -f ${package_name}'") SET(${file_list} ${out} PARENT_SCOPE) file(REMOVE ${CMAKE_CURRENT_BINARY_DIR}/list_files_pip_temp.txt) diff --git a/CMakeScripts/InstallMSYS2.cmake b/CMakeScripts/InstallMSYS2.cmake index d3e63c3c9..dcdc738a0 100644 --- a/CMakeScripts/InstallMSYS2.cmake +++ b/CMakeScripts/InstallMSYS2.cmake @@ -59,9 +59,9 @@ if(WIN32) ${MINGW_BIN}/libharfbuzz-0.dll
${MINGW_BIN}/libhogweed-4.dll
${MINGW_BIN}/libiconv-2.dll
- ${MINGW_BIN}/libicudt57.dll
- ${MINGW_BIN}/libicuin57.dll
- ${MINGW_BIN}/libicuuc57.dll
+ ${MINGW_BIN}/libicudt58.dll
+ ${MINGW_BIN}/libicuin58.dll
+ ${MINGW_BIN}/libicuuc58.dll
${MINGW_BIN}/libidn-11.dll
${MINGW_BIN}/libintl-8.dll
${MINGW_BIN}/libjpeg-8.dll
@@ -1,4 +1,3 @@ - Inkscape. Draw Freely. ====================== @@ -33,12 +32,12 @@ Inkscape version 0.92 and newer can be build using cmake: make install Other platforms such as Windows and Mac require a lot more and are considered -a developer and packager task. These instructions are kept on the inkscape wiki +a developer and packager task. These instructions are kept on the Inkscape wiki. Required Dependencies ===================== The Inkscape core depends on several other libraries that you will need -install, if they're not already present on your system. The most +install, if they are not already present on your system. The most typical libraries you may need to install are: * Boehm-GC @@ -59,10 +58,10 @@ installed and working. Unfortunately, there is a great deal of variability in how you can get these functioning properly. Here are some recommendations: -First, make sure you have Perl and Python. If you're on Windows you +First, make sure you have Perl and Python. If you are on Windows you should also install Cygwin. -Next, you'll need to ensure the dependencies for each extension is +Next, you will need to ensure the dependencies for each extension is present. Depending on which extensions you need, the dependencies are going to vary, but here are some you may need to install: diff --git a/appveyor.sh b/appveyor.sh new file mode 100644 index 000000000..26d75571a --- /dev/null +++ b/appveyor.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +cd "$(dirname "$0")" +mkdir build +cd build + + + +# Write an empty fonts.conf to speed up fc-cache +export FONTCONFIG_FILE=/dummy-fonts.conf +cat >"$FONTCONFIG_FILE" <<EOF +<?xml version="1.0"?> +<!DOCTYPE fontconfig SYSTEM "fonts.dtd"> +<fontconfig></fontconfig> +EOF + + + +# update/install dependecies +pacman -Suu --needed --noconfirm --noprogressbar +pacman -S $MINGW_PACKAGE_PREFIX-ccache --needed --noconfirm --noprogressbar +source ../msys2installdeps.sh + +# configure +ccache --max-size=200M +cmake .. -G Ninja -DCMAKE_C_COMPILER_LAUNCHER="ccache" -DCMAKE_CXX_COMPILER_LAUNCHER="ccache" + +# build +ccache --zero-stats +ninja +ccache --show-stats +ninja install + +# test +inkscape/inkscape.exe -V + +# package +7z a inkscape.7z inkscape diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 000000000..dd484b49e --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,22 @@ +version: '{branch}-{build}' + +branches: + only: + - master + +clone_depth: 10 + +environment: + matrix: + - MSYSTEM: MINGW64 + - MSYSTEM: MINGW32 + +build_script: + - C:\msys64\usr\bin\pacman -Syuu --needed --noconfirm --noprogressbar + - C:\msys64\usr\bin\bash --login -c "$(cygpath ${APPVEYOR_BUILD_FOLDER})/appveyor.sh" + +artifacts: + - path: build\inkscape.7z + +cache: + - C:\msys64\home\appveyor\.ccache diff --git a/msys2installdeps.sh b/msys2installdeps.sh index fd46e455c..e05411ea3 100755 --- a/msys2installdeps.sh +++ b/msys2installdeps.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# ------------------------------------------------------------------------------ -# This script installs all dependecies required for building Inkscape with MSYS2 +# ------------------------------------------------------------------------------- +# This script installs all dependencies required for building Inkscape with MSYS2 # execute it once on an MSYS shell, i.e. # - use the "MSYS2 MSYS" shortcut in the start menu or # - run "msys2.exe" in MSYS2's installation folder @@ -8,12 +8,20 @@ # MSYS2 and installed libraries can be updated later by executing # pacman -Syu --ignore=mingw-w64-*-imagemagick # in an MSYS shell -# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------- # select if you want to build 32-bit (i686), 64-bit (x86_64), or both -#ARCH=mingw-w64-i686 -#ARCH=mingw-w64-x86_64 -ARCH={mingw-w64-i686,mingw-w64-x86_64} +case "$MSYSTEM" in + MINGW32) + ARCH=mingw-w64-i686 + ;; + MINGW64) + ARCH=mingw-w64-x86_64 + ;; + *) + ARCH={mingw-w64-i686,mingw-w64-x86_64} + ;; +esac # sync package databases pacman -Sy diff --git a/src/libnrtype/Layout-TNG-Compute.cpp b/src/libnrtype/Layout-TNG-Compute.cpp index 8e173a7c7..63364ab34 100644 --- a/src/libnrtype/Layout-TNG-Compute.cpp +++ b/src/libnrtype/Layout-TNG-Compute.cpp @@ -19,6 +19,8 @@ namespace Inkscape { namespace Text { +//#define DEBUG_LAYOUT_TNG_COMPUTE + //#define IFTRACE(_code) _code #define IFTRACE(_code) @@ -53,27 +55,22 @@ class Layout::Calculator { class SpanPosition; friend class SpanPosition; - Layout &_flow; - ScanlineMaker *_scanline_maker; - unsigned _current_shape_index; /// index into Layout::_input_wrap_shapes - PangoContext *_pango_context; - Direction _block_progression; - /** for y= attributes in tspan elements et al, we do the adjustment by moving each - glyph individually by this number. The spec means that this is maintained across - paragraphs. - - To do non-flow text layout, only the first "y" attribute is normally used. If there is only one - "y" attribute in a <tspan> other than the first <tspan>, it is ignored. This allows Inkscape to - insert a new line anywhere. On output, the Inkscape determined "y" is written out so other SVG - viewers know where to place the <tspans>. - */ - + /** + * For y= attributes in tspan elements et al, we do the adjustment by moving each + * glyph individually by this number. The spec means that this is maintained across + * paragraphs. + * + * To do non-flow text layout, only the first "y" attribute is normally used. If there is only one + * "y" attribute in a <tspan> other than the first <tspan>, it is ignored. This allows Inkscape to + * insert a new line anywhere. On output, the Inkscape determined "y" is written out so other SVG + * viewers know where to place the <tspans>. + */ double _y_offset; /** to stop pango from hinting its output, the font factory creates all fonts very large. @@ -87,7 +84,20 @@ class Layout::Calculator Layout *sub_flow; // this is only set for the first input item in a sub-flow InputItemInfo() : in_sub_flow(false), sub_flow(NULL) {} - void free(); + + /* fixme: I don't like the fact that InputItemInfo etc. use the default copy constructor and + * operator= (and thus don't involve incrementing reference counts), yet they provide a free method + * that does delete or Unref. + * + * I suggest using the garbage collector to manage deletion. + */ + void free() + { + if (sub_flow) { + delete sub_flow; + sub_flow = NULL; + } + } }; /** Temporary storage associated with each item returned by the call to @@ -97,17 +107,36 @@ class Layout::Calculator font_instance *font; PangoItemInfo() : item(NULL), font(NULL) {} - void free(); + + /* fixme: I don't like the fact that InputItemInfo etc. use the default copy constructor and + * operator= (and thus don't involve incrementing reference counts), yet they provide a free method + * that does delete or Unref. + * + * I suggest using the garbage collector to manage deletion. + */ + void free() + { + if (item) { + pango_item_free(item); + item = NULL; + } + if (font) { + font->Unref(); + font = NULL; + } + } }; + /** These spans have approximately the same definition as that used for - Layout::Span (constant font, direction, etc), except that they are from - before we have located the line breaks, so bear no relation to chunks. - They are guaranteed to be in at most one PangoItem (spans with no text in - them will not have an associated PangoItem), exactly one input source and - will only have one change of x, y, dx, dy or rotate attribute, which will - be at the beginning. An UnbrokenSpan can cross a chunk boundary, c.f. - BrokenSpan. */ + * Layout::Span (constant font, direction, etc), except that they are from + * before we have located the line breaks, so bear no relation to chunks. + * They are guaranteed to be in at most one PangoItem (spans with no text in + * them will not have an associated PangoItem), exactly one input source and + * will only have one change of x, y, dx, dy or rotate attribute, which will + * be at the beginning. An UnbrokenSpan can cross a chunk boundary, c.f. + * BrokenSpan. + */ struct UnbrokenSpan { PangoGlyphString *glyph_string; int pango_item_index; /// index into _para.pango_items, or -1 if this is style only @@ -123,10 +152,48 @@ class Layout::Calculator SVGLength x, y, dx, dy, rotate; // these are reoriented copies of the <tspan> attributes. We change span when we encounter one. UnbrokenSpan() : glyph_string(NULL) {} - void free() {if (glyph_string) pango_glyph_string_free(glyph_string); glyph_string = NULL;} + void free() + { + if (glyph_string) + pango_glyph_string_free(glyph_string); + glyph_string = NULL; + } }; - /** a useful little iterator for moving char-by-char across spans. */ + + /** Used to provide storage for anything that applies to the current + paragraph only. Since we're only processing one paragraph at a time, + there's only one instantiation of this struct, on the stack of + calculate(). */ + struct ParagraphInfo { + unsigned first_input_index; ///< Index into Layout::_input_stream. + Direction direction; + Alignment alignment; + std::vector<InputItemInfo> input_items; + std::vector<PangoItemInfo> pango_items; + std::vector<PangoLogAttr> char_attributes; ///< For every character in the paragraph. + std::vector<UnbrokenSpan> unbroken_spans; + + template<typename T> static void free_sequence(T &seq) + { + for (typename T::iterator it(seq.begin()); it != seq.end(); ++it) { + it->free(); + } + seq.clear(); + } + + void free() + { + free_sequence(input_items); + free_sequence(pango_items); + free_sequence(unbroken_spans); + } + }; + + + /** + * A useful little iterator for moving char-by-char across spans. + */ struct UnbrokenSpanPosition { std::vector<UnbrokenSpan>::iterator iter_span; unsigned char_byte; @@ -140,9 +207,11 @@ class Layout::Calculator {return char_byte != other.char_byte || iter_span != other.iter_span;} }; - /** The line breaking algorithm will convert each UnbrokenSpan into one - or more of these. A BrokenSpan will never cross a chunk boundary, c.f. - UnbrokenSpan. */ + /** + * The line breaking algorithm will convert each UnbrokenSpan into one + * or more of these. A BrokenSpan will never cross a chunk boundary, + * c.f. UnbrokenSpan. + */ struct BrokenSpan { UnbrokenSpanPosition start; UnbrokenSpanPosition end; // the end of this will always be the same as the start of the next @@ -168,83 +237,11 @@ class Layout::Calculator int whitespace_count; }; - /** Used to provide storage for anything that applies to the current - paragraph only. Since we're only processing one paragraph at a time, - there's only one instantiation of this struct, on the stack of - calculate(). */ - struct ParagraphInfo { - unsigned first_input_index; ///< Index into Layout::_input_stream. - Direction direction; - Alignment alignment; - std::vector<InputItemInfo> input_items; - std::vector<PangoItemInfo> pango_items; - std::vector<PangoLogAttr> char_attributes; ///< For every character in the paragraph. - std::vector<UnbrokenSpan> unbroken_spans; - - template<typename T> static void free_sequence(T &seq); - void free(); - }; - -/* *********************************************************************************************************/ -// Initialisation of ParagraphInfo structure - - -#if 0 /* unused */ - void _initialiseInputItems(ParagraphInfo *para) const; -#endif - void _buildPangoItemizationForPara(ParagraphInfo *para) const; - - // Returns line_height_multiplier - static double _computeFontLineHeight( SPStyle const *style ); - + static double _computeFontLineHeight( SPStyle const *style ); // Returns line_height_multiplier unsigned _buildSpansForPara(ParagraphInfo *para) const; - -/* *********************************************************************************************************/ -// Per-line functions - -/** - * For debugging, not called in distributed code - * - * Input: para->first_input_index, para->pango_items - */ -static void dumpPangoItemsOut(ParagraphInfo *para){ - std::cout << "Pango items: " << para->pango_items.size() << std::endl; - font_factory * factory = font_factory::Default(); - for(unsigned pidx = 0 ; pidx < para->pango_items.size(); pidx++){ - std::cout - << "idx: " << pidx - << " offset: " - << para->pango_items[pidx].item->offset - << " length: " - << para->pango_items[pidx].item->length - << " font: " - << factory->ConstructFontSpecification( para->pango_items[pidx].font ) - << std::endl; - } -} - -/** - * For debugging, not called in distributed code - * - * Input: para->first_input_index, para->pango_items - */ -static void dumpUnbrokenSpans(ParagraphInfo *para){ - std::cout << "Unbroken Spans: " << para->unbroken_spans.size() << std::endl; - for(unsigned uidx = 0 ; uidx < para->unbroken_spans.size(); uidx++){ - std::cout - << "idx: " << uidx - << " pango_item_index: " << para->unbroken_spans[uidx].pango_item_index - << " input_index: " << para->unbroken_spans[uidx].input_index - << " char_index_in_para: " << para->unbroken_spans[uidx].char_index_in_para - << " text_bytes: " << para->unbroken_spans[uidx].text_bytes - << std::endl; - } -} - - - bool _goToNextWrapShape(); + void _createFirstScanlineMaker(); bool _findChunksForLine(ParagraphInfo const ¶, UnbrokenSpanPosition *start_span_pos, @@ -252,12 +249,6 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){ FontMetrics *line_box_height, FontMetrics const *strut_height); - static inline PangoLogAttr const &_charAttributes(ParagraphInfo const ¶, - UnbrokenSpanPosition const &span_pos) - { - return para.char_attributes[span_pos.iter_span->char_index_in_para + span_pos.char_index]; - } - bool _buildChunksInScanRun(ParagraphInfo const ¶, UnbrokenSpanPosition const &start_span_pos, ScanlineMaker::ScanRun const &scan_run, @@ -265,664 +256,675 @@ static void dumpUnbrokenSpans(ParagraphInfo *para){ FontMetrics *line_height, FontMetrics const *strut_height) const; - /** computes the width of a single UnbrokenSpan (pointed to by span->start.iter_span) - and outputs its vital statistics into the other fields of \a span. - Measuring will stop if maximum_width is reached and in that case the - function will return false. In other cases where a line break must be - done immediately the function will also return false. On return - \a last_break_span will contain the vital statistics for the span only - up to the last line breaking change. If there are no line breaking - characters in the span then \a last_break_span will not be altered. - Similarly, \a last_emergency_break_span will contain the vital - statistics for the span up to the last inter-character boundary, - or will be unaltered if there is none. */ - bool _measureUnbrokenSpan(ParagraphInfo const ¶, BrokenSpan *span, BrokenSpan *last_break_span, BrokenSpan *last_emergency_break_span, double maximum_width) const + bool _measureUnbrokenSpan(ParagraphInfo const ¶, + BrokenSpan *span, + BrokenSpan *last_break_span, + BrokenSpan *last_emergency_break_span, + double maximum_width) const; + + double _getChunkLeftWithAlignment(ParagraphInfo const ¶, + std::vector<ChunkInfo>::const_iterator it_chunk, + double *add_to_each_whitespace) const; + + void _outputLine(ParagraphInfo const ¶, + FontMetrics const &line_height, + std::vector<ChunkInfo> const &chunk_info); + + static inline PangoLogAttr const &_charAttributes(ParagraphInfo const ¶, + UnbrokenSpanPosition const &span_pos) { - TRACE((" start _measureUnbrokenSpan %g\n", maximum_width)); - span->setZero(); + return para.char_attributes[span_pos.iter_span->char_index_in_para + span_pos.char_index]; + } - if (span->start.iter_span->dx._set && span->start.char_byte == 0){ - if(para.direction == RIGHT_TO_LEFT){ - span->width -= span->start.iter_span->dx.computed; - } else { - span->width += span->start.iter_span->dx.computed; - } - } +#ifdef DEBUG_LAYOUT_TNG_COMPUTE + static void dumpPangoItemsOut(ParagraphInfo *para); + static void dumpUnbrokenSpans(ParagraphInfo *para){ +#endif //DEBUG_LAYOUT_TNG_COMPUTE - if (span->start.iter_span->pango_item_index == -1) { - // if this is a style-only span there's no text in it - // so we don't need to do very much at all - span->end.iter_span++; - return true; +public: + Calculator(Layout *text_flow) + : _flow(*text_flow) {} + + bool calculate(); +}; + + +/** + * Computes the width of a single UnbrokenSpan (pointed to by span->start.iter_span) + * and outputs its vital statistics into the other fields of \a span. + * Measuring will stop if maximum_width is reached and in that case the + * function will return false. In other cases where a line break must be + * done immediately the function will also return false. On return + * \a last_break_span will contain the vital statistics for the span only + * up to the last line breaking change. If there are no line breaking + * characters in the span then \a last_break_span will not be altered. + * Similarly, \a last_emergency_break_span will contain the vital + * statistics for the span up to the last inter-character boundary, + * or will be unaltered if there is none. + */ +bool Layout::Calculator::_measureUnbrokenSpan(ParagraphInfo const ¶, + BrokenSpan *span, + BrokenSpan *last_break_span, + BrokenSpan *last_emergency_break_span, + double maximum_width) const +{ + TRACE((" start _measureUnbrokenSpan %g\n", maximum_width)); + span->setZero(); + + if (span->start.iter_span->dx._set && span->start.char_byte == 0){ + if(para.direction == RIGHT_TO_LEFT){ + span->width -= span->start.iter_span->dx.computed; + } else { + span->width += span->start.iter_span->dx.computed; } + } - if (_flow._input_stream[span->start.iter_span->input_index]->Type() == CONTROL_CODE) { - InputStreamControlCode const *control_code = static_cast<InputStreamControlCode const *>(_flow._input_stream[span->start.iter_span->input_index]); - if (control_code->code == SHAPE_BREAK || control_code->code == PARAGRAPH_BREAK) { - *last_emergency_break_span = *last_break_span = *span; - return false; - } - if (control_code->code == ARBITRARY_GAP) { - if (span->width + control_code->width > maximum_width) - return false; - TRACE((" fitted control code, width = %f\n", control_code->width)); - span->width += control_code->width; - span->end.increment(); - } - return true; + if (span->start.iter_span->pango_item_index == -1) { + // if this is a style-only span there's no text in it + // so we don't need to do very much at all + span->end.iter_span++; + return true; + } + if (_flow._input_stream[span->start.iter_span->input_index]->Type() == CONTROL_CODE) { + InputStreamControlCode const *control_code = static_cast<InputStreamControlCode const *>(_flow._input_stream[span->start.iter_span->input_index]); + if (control_code->code == SHAPE_BREAK || control_code->code == PARAGRAPH_BREAK) { + *last_emergency_break_span = *last_break_span = *span; + return false; + } + if (control_code->code == ARBITRARY_GAP) { + if (span->width + control_code->width > maximum_width) + return false; + TRACE((" fitted control code, width = %f\n", control_code->width)); + span->width += control_code->width; + span->end.increment(); } + return true; - if (_flow._input_stream[span->start.iter_span->input_index]->Type() != TEXT_SOURCE) - return true; // never happens + } - InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_flow._input_stream[span->start.iter_span->input_index]); + if (_flow._input_stream[span->start.iter_span->input_index]->Type() != TEXT_SOURCE) + return true; // never happens - if (_directions_are_orthogonal(_block_progression, text_source->styleGetBlockProgression())) { - // TODO: block-progression altered in the middle - // Measure the precomputed flow from para.input_items - span->end.iter_span++; // for now, skip to the next span - return true; - } + InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_flow._input_stream[span->start.iter_span->input_index]); - // a normal span going with a normal block-progression - double font_size_multiplier = span->start.iter_span->font_size / (PANGO_SCALE * _font_factory_size_multiplier); - double soft_hyphen_glyph_width = 0.0; - bool soft_hyphen_in_word = false; - bool is_soft_hyphen = false; - IFTRACE(int char_count = 0); - - // if we're not at the start of the span we need to pre-init glyph_index - span->start_glyph_index = 0; - while (span->start_glyph_index < (unsigned)span->start.iter_span->glyph_string->num_glyphs - && span->start.iter_span->glyph_string->log_clusters[span->start_glyph_index] < (int)span->start.char_byte) - span->start_glyph_index++; - span->end_glyph_index = span->start_glyph_index; - - // go char-by-char summing the width, while keeping track of the previous break point - do { - PangoLogAttr const &char_attributes = _charAttributes(para, span->end); - - if (char_attributes.is_mandatory_break && span->end != span->start) { - TRACE((" is_mandatory_break ************\n")); - *last_emergency_break_span = *last_break_span = *span; - TRACE((" span %ld end of para; width = %f chars = %d\n", span->start.iter_span - para.unbroken_spans.begin(), span->width, char_count)); - return false; - } + if (_directions_are_orthogonal(_block_progression, text_source->styleGetBlockProgression())) { + // TODO: block-progression altered in the middle + // Measure the precomputed flow from para.input_items + span->end.iter_span++; // for now, skip to the next span + return true; + } - if (char_attributes.is_line_break) { - TRACE((" is_line_break ************\n")); - // a suitable position to break at, record where we are - *last_emergency_break_span = *last_break_span = *span; - if (soft_hyphen_in_word) { - // if there was a previous soft hyphen we're not going to need it any more so we can remove it - span->width -= soft_hyphen_glyph_width; - if (!is_soft_hyphen) - soft_hyphen_in_word = false; - } - } else if (char_attributes.is_char_break) { - *last_emergency_break_span = *span; + // a normal span going with a normal block-progression + double font_size_multiplier = span->start.iter_span->font_size / (PANGO_SCALE * _font_factory_size_multiplier); + double soft_hyphen_glyph_width = 0.0; + bool soft_hyphen_in_word = false; + bool is_soft_hyphen = false; + IFTRACE(int char_count = 0); + + // if we're not at the start of the span we need to pre-init glyph_index + span->start_glyph_index = 0; + while (span->start_glyph_index < (unsigned)span->start.iter_span->glyph_string->num_glyphs + && span->start.iter_span->glyph_string->log_clusters[span->start_glyph_index] < (int)span->start.char_byte) + span->start_glyph_index++; + span->end_glyph_index = span->start_glyph_index; + + // go char-by-char summing the width, while keeping track of the previous break point + do { + PangoLogAttr const &char_attributes = _charAttributes(para, span->end); + + if (char_attributes.is_mandatory_break && span->end != span->start) { + TRACE((" is_mandatory_break ************\n")); + *last_emergency_break_span = *last_break_span = *span; + TRACE((" span %ld end of para; width = %f chars = %d\n", span->start.iter_span - para.unbroken_spans.begin(), span->width, char_count)); + return false; + } + + if (char_attributes.is_line_break) { + TRACE((" is_line_break ************\n")); + // a suitable position to break at, record where we are + *last_emergency_break_span = *last_break_span = *span; + if (soft_hyphen_in_word) { + // if there was a previous soft hyphen we're not going to need it any more so we can remove it + span->width -= soft_hyphen_glyph_width; + if (!is_soft_hyphen) + soft_hyphen_in_word = false; } - // todo: break between chars if necessary (ie no word breaks present) when doing rectangular flowing + } else if (char_attributes.is_char_break) { + *last_emergency_break_span = *span; + } + // todo: break between chars if necessary (ie no word breaks present) when doing rectangular flowing - // sum the glyph widths, letter spacing, word spacing, and textLength adjustment to get the character width - double char_width = 0.0; - while (span->end_glyph_index < (unsigned)span->end.iter_span->glyph_string->num_glyphs - && span->end.iter_span->glyph_string->log_clusters[span->end_glyph_index] <= (int)span->end.char_byte) { - if (_block_progression == LEFT_TO_RIGHT || _block_progression == RIGHT_TO_LEFT) { - // Vertical text + // sum the glyph widths, letter spacing, word spacing, and textLength adjustment to get the character width + double char_width = 0.0; + while (span->end_glyph_index < (unsigned)span->end.iter_span->glyph_string->num_glyphs + && span->end.iter_span->glyph_string->log_clusters[span->end_glyph_index] <= (int)span->end.char_byte) { + if (_block_progression == LEFT_TO_RIGHT || _block_progression == RIGHT_TO_LEFT) { + // Vertical text - if( text_source->style->text_orientation.computed == SP_CSS_TEXT_ORIENTATION_SIDEWAYS || - (text_source->style->text_orientation.computed == SP_CSS_TEXT_ORIENTATION_MIXED && - para.pango_items[span->end.iter_span->pango_item_index].item->analysis.gravity == 0) ) { - // Sideways orientation - char_width += span->start.iter_span->font_size * para.pango_items[span->end.iter_span->pango_item_index].font->Advance(span->end.iter_span->glyph_string->glyphs[span->end_glyph_index].glyph, false); - } else { - // Upright orientation - char_width += span->start.iter_span->font_size * para.pango_items[span->end.iter_span->pango_item_index].font->Advance(span->end.iter_span->glyph_string->glyphs[span->end_glyph_index].glyph, true); - } + if( text_source->style->text_orientation.computed == SP_CSS_TEXT_ORIENTATION_SIDEWAYS || + (text_source->style->text_orientation.computed == SP_CSS_TEXT_ORIENTATION_MIXED && + para.pango_items[span->end.iter_span->pango_item_index].item->analysis.gravity == 0) ) { + // Sideways orientation + char_width += span->start.iter_span->font_size * para.pango_items[span->end.iter_span->pango_item_index].font->Advance(span->end.iter_span->glyph_string->glyphs[span->end_glyph_index].glyph, false); } else { - // Horizontal text - char_width += font_size_multiplier * span->end.iter_span->glyph_string->glyphs[span->end_glyph_index].geometry.width; + // Upright orientation + char_width += span->start.iter_span->font_size * para.pango_items[span->end.iter_span->pango_item_index].font->Advance(span->end.iter_span->glyph_string->glyphs[span->end_glyph_index].glyph, true); } - span->end_glyph_index++; - } - if (char_attributes.is_cursor_position) - char_width += text_source->style->letter_spacing.computed * _flow.getTextLengthMultiplierDue(); - if (char_attributes.is_white) - char_width += text_source->style->word_spacing.computed * _flow.getTextLengthMultiplierDue(); - char_width += _flow.getTextLengthIncrementDue(); - span->width += char_width; - IFTRACE(char_count++); - - if (char_attributes.is_white) { - span->whitespace_count++; - span->each_whitespace_width = char_width; + } else { + // Horizontal text + char_width += font_size_multiplier * span->end.iter_span->glyph_string->glyphs[span->end_glyph_index].geometry.width; } - span->ends_with_whitespace = char_attributes.is_white; + span->end_glyph_index++; + } + if (char_attributes.is_cursor_position) + char_width += text_source->style->letter_spacing.computed * _flow.getTextLengthMultiplierDue(); + if (char_attributes.is_white) + char_width += text_source->style->word_spacing.computed * _flow.getTextLengthMultiplierDue(); + char_width += _flow.getTextLengthIncrementDue(); + span->width += char_width; + IFTRACE(char_count++); + + if (char_attributes.is_white) { + span->whitespace_count++; + span->each_whitespace_width = char_width; + } + span->ends_with_whitespace = char_attributes.is_white; - is_soft_hyphen = (UNICODE_SOFT_HYPHEN == *Glib::ustring::const_iterator(span->end.iter_span->input_stream_first_character.base() + span->end.char_byte)); - if (is_soft_hyphen) - soft_hyphen_glyph_width = char_width; + is_soft_hyphen = (UNICODE_SOFT_HYPHEN == *Glib::ustring::const_iterator(span->end.iter_span->input_stream_first_character.base() + span->end.char_byte)); + if (is_soft_hyphen) + soft_hyphen_glyph_width = char_width; - span->end.increment(); + span->end.increment(); - // Width should not include letter_spacing (or word_spacing) after last letter at end of line. - // word_spacing is attached to white space that is already removed from line end (?) - double test_width = span->width - text_source->style->letter_spacing.computed; + // Width should not include letter_spacing (or word_spacing) after last letter at end of line. + // word_spacing is attached to white space that is already removed from line end (?) + double test_width = span->width - text_source->style->letter_spacing.computed; - // Save letter_spacing and word_spacing for subtraction later if span is last span in line. - span->letter_spacing = text_source->style->letter_spacing.computed; - span->word_spacing = text_source->style->word_spacing.computed; + // Save letter_spacing and word_spacing for subtraction later if span is last span in line. + span->letter_spacing = text_source->style->letter_spacing.computed; + span->word_spacing = text_source->style->word_spacing.computed; - if (test_width > maximum_width && !char_attributes.is_white) { // whitespaces don't matter, we can put as many as we want at eol - TRACE((" span %ld exceeded scanrun; width = %f chars = %d\n", span->start.iter_span - para.unbroken_spans.begin(), span->width, char_count)); - return false; - } + if (test_width > maximum_width && !char_attributes.is_white) { // whitespaces don't matter, we can put as many as we want at eol + TRACE((" span %ld exceeded scanrun; width = %f chars = %d\n", span->start.iter_span - para.unbroken_spans.begin(), span->width, char_count)); + return false; + } - } while (span->end.char_byte != 0); // while we haven't wrapped to the next span + } while (span->end.char_byte != 0); // while we haven't wrapped to the next span - TRACE((" fitted span %ld width = %f chars = %d\n", span->start.iter_span - para.unbroken_spans.begin(), span->width, char_count)); - TRACE((" end _measureUnbrokenSpan %g\n", maximum_width)); - return true; - } + TRACE((" fitted span %ld width = %f chars = %d\n", span->start.iter_span - para.unbroken_spans.begin(), span->width, char_count)); + TRACE((" end _measureUnbrokenSpan %g\n", maximum_width)); + return true; +} /* *********************************************************************************************************/ // Per-line functions (output) - /** Uses the paragraph alignment and the chunk information to work out - where the actual left of the final chunk must be. Also sets - \a add_to_each_whitespace to be the amount of x to add at each - whitespace character to make full justification work. */ - double _getChunkLeftWithAlignment(ParagraphInfo const ¶, std::vector<ChunkInfo>::const_iterator it_chunk, double *add_to_each_whitespace) const - { - *add_to_each_whitespace = 0.0; - if (_flow._input_wrap_shapes.empty()) { - switch (para.alignment) { - case FULL: - case LEFT: - default: - return it_chunk->x; - case RIGHT: - return it_chunk->x - it_chunk->text_width; - case CENTER: - return it_chunk->x - it_chunk->text_width/ 2; - } - } - +/** Uses the paragraph alignment and the chunk information to work out + * where the actual left of the final chunk must be. Also sets + * \a add_to_each_whitespace to be the amount of x to add at each + * whitespace character to make full justification work. + */ +double Layout::Calculator::_getChunkLeftWithAlignment(ParagraphInfo const ¶, + std::vector<ChunkInfo>::const_iterator it_chunk, + double *add_to_each_whitespace) const +{ + *add_to_each_whitespace = 0.0; + if (_flow._input_wrap_shapes.empty()) { switch (para.alignment) { case FULL: - if (!it_chunk->broken_spans.empty() - && it_chunk->broken_spans.back().end.iter_span != para.unbroken_spans.end()) { // don't justify the last chunk in the para - if (it_chunk->whitespace_count) - *add_to_each_whitespace = (it_chunk->scanrun_width - it_chunk->text_width) / it_chunk->whitespace_count; - //else - //add_to_each_charspace = something - } - return it_chunk->x; case LEFT: default: return it_chunk->x; case RIGHT: - return it_chunk->x + it_chunk->scanrun_width - it_chunk->text_width; + return it_chunk->x - it_chunk->text_width; case CENTER: - return it_chunk->x + (it_chunk->scanrun_width - it_chunk->text_width) / 2; + return it_chunk->x - it_chunk->text_width/ 2; } } - /** Once we've got here we have finished making changes to the line and - are ready to output the final result to #_flow. This method takes its - input parameters and does that. - */ - void _outputLine(ParagraphInfo const ¶, FontMetrics const &line_height, std::vector<ChunkInfo> const &chunk_info) - { - TRACE((" Start _outputLine: ascent %f, descent %f, top of box %f\n", line_height.ascent, line_height.descent, _scanline_maker->yCoordinate() )); - if (chunk_info.empty()) { - TRACE((" line too short to fit anything on it, go to next\n")); - return; - } + switch (para.alignment) { + case FULL: + if (!it_chunk->broken_spans.empty() + && it_chunk->broken_spans.back().end.iter_span != para.unbroken_spans.end()) { // don't justify the last chunk in the para + if (it_chunk->whitespace_count) + *add_to_each_whitespace = (it_chunk->scanrun_width - it_chunk->text_width) / it_chunk->whitespace_count; + //else + //add_to_each_charspace = something + } + return it_chunk->x; + case LEFT: + default: + return it_chunk->x; + case RIGHT: + return it_chunk->x + it_chunk->scanrun_width - it_chunk->text_width; + case CENTER: + return it_chunk->x + (it_chunk->scanrun_width - it_chunk->text_width) / 2; + } +} - // we've finished fiddling about with ascents and descents: create the output - TRACE((" found line fit; creating output\n")); - Layout::Line new_line; - new_line.in_paragraph = _flow._paragraphs.size() - 1; - new_line.baseline_y = _scanline_maker->yCoordinate(); - - // The y coordinate is at the beginning edge of the line box (top for horizontal text, left - // edge for vertical lr text, right edge for vertical rl text. We align, by default to the - // alphabetic baseline for horizontal text and the central baseline for vertical text. - if( _block_progression == RIGHT_TO_LEFT ) { - // Vertical text, use em box center as baseline - new_line.baseline_y -= 0.5 * line_height.emSize(); - } else if ( _block_progression == LEFT_TO_RIGHT ) { - // Vertical text, use em box center as baseline - new_line.baseline_y += 0.5 * line_height.emSize(); - } else { - new_line.baseline_y += line_height.getTypoAscent(); - } +/** + * Once we've got here we have finished making changes to the line + * and are ready to output the final result to #_flow. + * This method takes its input parameters and does that. + */ +void Layout::Calculator::_outputLine(ParagraphInfo const ¶, + FontMetrics const &line_height, + std::vector<ChunkInfo> const &chunk_info) +{ + TRACE((" Start _outputLine: ascent %f, descent %f, top of box %f\n", line_height.ascent, line_height.descent, _scanline_maker->yCoordinate() )); + if (chunk_info.empty()) { + TRACE((" line too short to fit anything on it, go to next\n")); + return; + } - new_line.in_shape = _current_shape_index; - _flow._lines.push_back(new_line); - - for (std::vector<ChunkInfo>::const_iterator it_chunk = chunk_info.begin() ; it_chunk != chunk_info.end() ; it_chunk++) { - - double add_to_each_whitespace; - // add the chunk to the list - Layout::Chunk new_chunk; - new_chunk.in_line = _flow._lines.size() - 1; - TRACE((" New chunk: in_line: %d\n", new_chunk.in_line)); - new_chunk.left_x = _getChunkLeftWithAlignment(para, it_chunk, &add_to_each_whitespace); - - // we may also have y move orders to deal with here (dx, dy and rotate are done per span) - - // Comment updated: 23 July 2010: - // We must handle two cases: - // - // 1. Inkscape SVG where the first line is placed by the read-in "y" value and the - // rest are determined by 'font-size' and 'line-height' (and not by any - // y-kerning). <tspan>s in this case are marked by sodipodi:role="line". This - // allows new lines to be inserted in the middle of a <text> object. On output, - // new "y" values are calculated for each <tspan> that represents a new line. Line - // spacing is already handled by the calling routine. - // - // 2. Plain SVG where each <text> or <tspan> is placed by its own "x" and "y" values. - // Note that in this case Inkscape treats each <text> object with any included - // <tspan>s as a single line of text. This can be confusing in the code below. - - if (!it_chunk->broken_spans.empty() // Not empty paragraph - && it_chunk->broken_spans.front().start.char_byte == 0 ) { // Beginning of unbroken span - - // If empty or new line (sodipode:role="line") - if( _flow._characters.empty() || - _flow._characters.back().chunk(&_flow).in_line != _flow._lines.size() - 1) { - - // This is the Inkscape SVG case. - // - // If <tspan> "y" attribute is set, use it (initial "y" attributes in - // <tspans> other than the first have already been stripped for <tspans> - // marked with role="line", see sp-text.cpp: SPText::_buildLayoutInput). - // NOTE: for vertical text, "y" is the user-space "x" value. - if( it_chunk->broken_spans.front().start.iter_span->y._set ) { - - // Use set "y" attribute - new_line.baseline_y = it_chunk->broken_spans.front().start.iter_span->y.computed; - // Save baseline - _flow._lines.back().baseline_y = new_line.baseline_y; - - double top_of_line_box = new_line.baseline_y; - if( _block_progression == RIGHT_TO_LEFT ) { - // Vertical text, use em box center as baseline - top_of_line_box += 0.5 * line_height.emSize(); - } else if (_block_progression == LEFT_TO_RIGHT ) { - // Vertical text, use em box center as baseline - top_of_line_box -= 0.5 * line_height.emSize(); - } else { - top_of_line_box -= line_height.getTypoAscent(); - } - TRACE((" y attribute set, next line top_of_line_box: %f\n", top_of_line_box )); - // Set the initial y coordinate of the next line (see above). - _scanline_maker->setNewYCoordinate(top_of_line_box); + // we've finished fiddling about with ascents and descents: create the output + TRACE((" found line fit; creating output\n")); + Layout::Line new_line; + new_line.in_paragraph = _flow._paragraphs.size() - 1; + new_line.baseline_y = _scanline_maker->yCoordinate(); + + // The y coordinate is at the beginning edge of the line box (top for horizontal text, left + // edge for vertical lr text, right edge for vertical rl text. We align, by default to the + // alphabetic baseline for horizontal text and the central baseline for vertical text. + if( _block_progression == RIGHT_TO_LEFT ) { + // Vertical text, use em box center as baseline + new_line.baseline_y -= 0.5 * line_height.emSize(); + } else if ( _block_progression == LEFT_TO_RIGHT ) { + // Vertical text, use em box center as baseline + new_line.baseline_y += 0.5 * line_height.emSize(); + } else { + new_line.baseline_y += line_height.getTypoAscent(); + } + + new_line.in_shape = _current_shape_index; + _flow._lines.push_back(new_line); + + for (std::vector<ChunkInfo>::const_iterator it_chunk = chunk_info.begin() ; it_chunk != chunk_info.end() ; it_chunk++) { + + double add_to_each_whitespace; + // add the chunk to the list + Layout::Chunk new_chunk; + new_chunk.in_line = _flow._lines.size() - 1; + TRACE((" New chunk: in_line: %d\n", new_chunk.in_line)); + new_chunk.left_x = _getChunkLeftWithAlignment(para, it_chunk, &add_to_each_whitespace); + + // we may also have y move orders to deal with here (dx, dy and rotate are done per span) + + // Comment updated: 23 July 2010: + // We must handle two cases: + // + // 1. Inkscape SVG where the first line is placed by the read-in "y" value and the + // rest are determined by 'font-size' and 'line-height' (and not by any + // y-kerning). <tspan>s in this case are marked by sodipodi:role="line". This + // allows new lines to be inserted in the middle of a <text> object. On output, + // new "y" values are calculated for each <tspan> that represents a new line. Line + // spacing is already handled by the calling routine. + // + // 2. Plain SVG where each <text> or <tspan> is placed by its own "x" and "y" values. + // Note that in this case Inkscape treats each <text> object with any included + // <tspan>s as a single line of text. This can be confusing in the code below. + + if (!it_chunk->broken_spans.empty() // Not empty paragraph + && it_chunk->broken_spans.front().start.char_byte == 0 ) { // Beginning of unbroken span + + // If empty or new line (sodipode:role="line") + if( _flow._characters.empty() || + _flow._characters.back().chunk(&_flow).in_line != _flow._lines.size() - 1) { + + // This is the Inkscape SVG case. + // + // If <tspan> "y" attribute is set, use it (initial "y" attributes in + // <tspans> other than the first have already been stripped for <tspans> + // marked with role="line", see sp-text.cpp: SPText::_buildLayoutInput). + // NOTE: for vertical text, "y" is the user-space "x" value. + if( it_chunk->broken_spans.front().start.iter_span->y._set ) { + + // Use set "y" attribute + new_line.baseline_y = it_chunk->broken_spans.front().start.iter_span->y.computed; + // Save baseline + _flow._lines.back().baseline_y = new_line.baseline_y; + + double top_of_line_box = new_line.baseline_y; + if( _block_progression == RIGHT_TO_LEFT ) { + // Vertical text, use em box center as baseline + top_of_line_box += 0.5 * line_height.emSize(); + } else if (_block_progression == LEFT_TO_RIGHT ) { + // Vertical text, use em box center as baseline + top_of_line_box -= 0.5 * line_height.emSize(); + } else { + top_of_line_box -= line_height.getTypoAscent(); } + TRACE((" y attribute set, next line top_of_line_box: %f\n", top_of_line_box )); + // Set the initial y coordinate of the next line (see above). + _scanline_maker->setNewYCoordinate(top_of_line_box); + } - // Reset relative y_offset ("dy" attribute is relative but should be reset at - // the beginning of each line since each line will have a new "y" written out.) - _y_offset = 0.0; + // Reset relative y_offset ("dy" attribute is relative but should be reset at + // the beginning of each line since each line will have a new "y" written out.) + _y_offset = 0.0; - } else { + } else { - // This is the plain SVG case - // - // "x" and "y" are used to place text, simulating lines as necessary - if( it_chunk->broken_spans.front().start.iter_span->y._set ) { - _y_offset = it_chunk->broken_spans.front().start.iter_span->y.computed - new_line.baseline_y; - } + // This is the plain SVG case + // + // "x" and "y" are used to place text, simulating lines as necessary + if( it_chunk->broken_spans.front().start.iter_span->y._set ) { + _y_offset = it_chunk->broken_spans.front().start.iter_span->y.computed - new_line.baseline_y; } } - _flow._chunks.push_back(new_chunk); - - double current_x; - double direction_sign; - Direction previous_direction = para.direction; - double counter_directional_width_remaining = 0.0; - float glyph_rotate = 0.0; - if (para.direction == LEFT_TO_RIGHT) { - direction_sign = +1.0; - current_x = 0.0; - } else { - direction_sign = -1.0; - if (para.alignment == FULL && !_flow._input_wrap_shapes.empty()){ - current_x = it_chunk->scanrun_width; - } - else { - current_x = it_chunk->text_width; - } + } + _flow._chunks.push_back(new_chunk); + + double current_x; + double direction_sign; + Direction previous_direction = para.direction; + double counter_directional_width_remaining = 0.0; + float glyph_rotate = 0.0; + if (para.direction == LEFT_TO_RIGHT) { + direction_sign = +1.0; + current_x = 0.0; + } else { + direction_sign = -1.0; + if (para.alignment == FULL && !_flow._input_wrap_shapes.empty()){ + current_x = it_chunk->scanrun_width; } + else { + current_x = it_chunk->text_width; + } + } - for (std::vector<BrokenSpan>::const_iterator it_span = it_chunk->broken_spans.begin() ; it_span != it_chunk->broken_spans.end() ; it_span++) { - // begin adding spans to the list - UnbrokenSpan const &unbroken_span = *it_span->start.iter_span; - double x_in_span_last = 0.0; // set at the END when a new cluster starts - double x_in_span = 0.0; // set from the preceding at the START when a new cluster starts. - - if (it_span->start.char_byte == 0) { - // Start of an unbroken span, we might have dx, dy or rotate still to process - // (x and y are done per chunk) - if (unbroken_span.dx._set) current_x += unbroken_span.dx.computed; - if (unbroken_span.dy._set) _y_offset += unbroken_span.dy.computed; - if (unbroken_span.rotate._set) glyph_rotate = unbroken_span.rotate.computed * (M_PI/180); - } - - if (_flow._input_stream[unbroken_span.input_index]->Type() == TEXT_SOURCE - && unbroken_span.pango_item_index == -1) { - // style only, nothing to output - continue; - } + for (std::vector<BrokenSpan>::const_iterator it_span = it_chunk->broken_spans.begin() ; it_span != it_chunk->broken_spans.end() ; it_span++) { + // begin adding spans to the list + UnbrokenSpan const &unbroken_span = *it_span->start.iter_span; + double x_in_span_last = 0.0; // set at the END when a new cluster starts + double x_in_span = 0.0; // set from the preceding at the START when a new cluster starts. + + if (it_span->start.char_byte == 0) { + // Start of an unbroken span, we might have dx, dy or rotate still to process + // (x and y are done per chunk) + if (unbroken_span.dx._set) current_x += unbroken_span.dx.computed; + if (unbroken_span.dy._set) _y_offset += unbroken_span.dy.computed; + if (unbroken_span.rotate._set) glyph_rotate = unbroken_span.rotate.computed * (M_PI/180); + } - Layout::Span new_span; + if (_flow._input_stream[unbroken_span.input_index]->Type() == TEXT_SOURCE + && unbroken_span.pango_item_index == -1) { + // style only, nothing to output + continue; + } - new_span.in_chunk = _flow._chunks.size() - 1; - new_span.line_height = unbroken_span.line_height; - new_span.in_input_stream_item = unbroken_span.input_index; - new_span.baseline_shift = 0.0; - new_span.block_progression = _block_progression; - new_span.text_orientation = unbroken_span.text_orientation; - if ((_flow._input_stream[unbroken_span.input_index]->Type() == TEXT_SOURCE) && (new_span.font = para.pango_items[unbroken_span.pango_item_index].font)) - { - new_span.font->Ref(); - new_span.font_size = unbroken_span.font_size; - new_span.direction = para.pango_items[unbroken_span.pango_item_index].item->analysis.level & 1 ? RIGHT_TO_LEFT : LEFT_TO_RIGHT; - new_span.input_stream_first_character = Glib::ustring::const_iterator(unbroken_span.input_stream_first_character.base() + it_span->start.char_byte); - } else { // a control code - new_span.font = NULL; - new_span.font_size = new_span.line_height.emSize(); - new_span.direction = para.direction; - } + Layout::Span new_span; + + new_span.in_chunk = _flow._chunks.size() - 1; + new_span.line_height = unbroken_span.line_height; + new_span.in_input_stream_item = unbroken_span.input_index; + new_span.baseline_shift = 0.0; + new_span.block_progression = _block_progression; + new_span.text_orientation = unbroken_span.text_orientation; + if ((_flow._input_stream[unbroken_span.input_index]->Type() == TEXT_SOURCE) && (new_span.font = para.pango_items[unbroken_span.pango_item_index].font)) + { + new_span.font->Ref(); + new_span.font_size = unbroken_span.font_size; + new_span.direction = para.pango_items[unbroken_span.pango_item_index].item->analysis.level & 1 ? RIGHT_TO_LEFT : LEFT_TO_RIGHT; + new_span.input_stream_first_character = Glib::ustring::const_iterator(unbroken_span.input_stream_first_character.base() + it_span->start.char_byte); + } else { // a control code + new_span.font = NULL; + new_span.font_size = new_span.line_height.emSize(); + new_span.direction = para.direction; + } - if (new_span.direction == para.direction) { - current_x -= counter_directional_width_remaining; - counter_directional_width_remaining = 0.0; - } else if (new_span.direction != previous_direction) { - // measure width of spans we need to switch round - counter_directional_width_remaining = 0.0; - std::vector<BrokenSpan>::const_iterator it_following_span; - for (it_following_span = it_span ; it_following_span != it_chunk->broken_spans.end() ; it_following_span++) { - Layout::Direction following_span_progression = static_cast<InputStreamTextSource const *>(_flow._input_stream[it_following_span->start.iter_span->input_index])->styleGetBlockProgression(); - if (!Layout::_directions_are_orthogonal(following_span_progression, _block_progression)) { - if (it_following_span->start.iter_span->pango_item_index == -1) { // when the span came from a control code - if (new_span.direction != para.direction) break; - } else - if (new_span.direction != (para.pango_items[it_following_span->start.iter_span->pango_item_index].item->analysis.level & 1 ? RIGHT_TO_LEFT : LEFT_TO_RIGHT)) break; - } - counter_directional_width_remaining += direction_sign * (it_following_span->width + it_following_span->whitespace_count * add_to_each_whitespace); + if (new_span.direction == para.direction) { + current_x -= counter_directional_width_remaining; + counter_directional_width_remaining = 0.0; + } else if (new_span.direction != previous_direction) { + // measure width of spans we need to switch round + counter_directional_width_remaining = 0.0; + std::vector<BrokenSpan>::const_iterator it_following_span; + for (it_following_span = it_span ; it_following_span != it_chunk->broken_spans.end() ; it_following_span++) { + Layout::Direction following_span_progression = static_cast<InputStreamTextSource const *>(_flow._input_stream[it_following_span->start.iter_span->input_index])->styleGetBlockProgression(); + if (!Layout::_directions_are_orthogonal(following_span_progression, _block_progression)) { + if (it_following_span->start.iter_span->pango_item_index == -1) { // when the span came from a control code + if (new_span.direction != para.direction) break; + } else + if (new_span.direction != (para.pango_items[it_following_span->start.iter_span->pango_item_index].item->analysis.level & 1 ? RIGHT_TO_LEFT : LEFT_TO_RIGHT)) break; } - current_x += counter_directional_width_remaining; - counter_directional_width_remaining = 0.0; // we want to go increasingly negative + counter_directional_width_remaining += direction_sign * (it_following_span->width + it_following_span->whitespace_count * add_to_each_whitespace); } - new_span.x_start = current_x; - - if (_flow._input_stream[unbroken_span.input_index]->Type() == TEXT_SOURCE) { - // the span is set up, push the glyphs and chars - InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_flow._input_stream[unbroken_span.input_index]); - Glib::ustring::const_iterator iter_source_text = Glib::ustring::const_iterator(unbroken_span.input_stream_first_character.base() + it_span->start.char_byte) ; - unsigned char_index_in_unbroken_span = it_span->start.char_index; - double font_size_multiplier = new_span.font_size / (PANGO_SCALE * _font_factory_size_multiplier); - int log_cluster_size_glyphs = 0; // Number of glyphs in this log_cluster - int log_cluster_size_chars = 0; // Number of characters in this log_cluster - unsigned end_byte = 0; - - for (unsigned glyph_index = it_span->start_glyph_index ; glyph_index < it_span->end_glyph_index ; glyph_index++) { - unsigned char_byte = iter_source_text.base() - unbroken_span.input_stream_first_character.base(); - int newcluster = 0; - if (unbroken_span.glyph_string->glyphs[glyph_index].attr.is_cluster_start){ - newcluster = 1; - x_in_span = x_in_span_last; - } - - if (unbroken_span.glyph_string->log_clusters[glyph_index] < (int)unbroken_span.text_bytes - && *iter_source_text == UNICODE_SOFT_HYPHEN - && glyph_index + 1 != it_span->end_glyph_index) { - // if we're looking at a soft hyphen and it's not the last glyph in the - // chunk we don't draw the glyph but we still need to add to _characters - Layout::Character new_character; - new_character.in_span = _flow._spans.size(); // the span hasn't been added yet, so no -1 - new_character.char_attributes = para.char_attributes[unbroken_span.char_index_in_para + char_index_in_unbroken_span]; - new_character.in_glyph = -1; - _flow._characters.push_back(new_character); - iter_source_text++; - char_index_in_unbroken_span++; - while (glyph_index < (unsigned)unbroken_span.glyph_string->num_glyphs - && unbroken_span.glyph_string->log_clusters[glyph_index] == (int)char_byte) - glyph_index++; - glyph_index--; - continue; - } + current_x += counter_directional_width_remaining; + counter_directional_width_remaining = 0.0; // we want to go increasingly negative + } + new_span.x_start = current_x; + + if (_flow._input_stream[unbroken_span.input_index]->Type() == TEXT_SOURCE) { + // the span is set up, push the glyphs and chars + InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_flow._input_stream[unbroken_span.input_index]); + Glib::ustring::const_iterator iter_source_text = Glib::ustring::const_iterator(unbroken_span.input_stream_first_character.base() + it_span->start.char_byte) ; + unsigned char_index_in_unbroken_span = it_span->start.char_index; + double font_size_multiplier = new_span.font_size / (PANGO_SCALE * _font_factory_size_multiplier); + int log_cluster_size_glyphs = 0; // Number of glyphs in this log_cluster + int log_cluster_size_chars = 0; // Number of characters in this log_cluster + unsigned end_byte = 0; + + for (unsigned glyph_index = it_span->start_glyph_index ; glyph_index < it_span->end_glyph_index ; glyph_index++) { + unsigned char_byte = iter_source_text.base() - unbroken_span.input_stream_first_character.base(); + int newcluster = 0; + if (unbroken_span.glyph_string->glyphs[glyph_index].attr.is_cluster_start){ + newcluster = 1; + x_in_span = x_in_span_last; + } - // create the Layout::Glyph - PangoGlyphInfo *unbroken_span_glyph_info = &unbroken_span.glyph_string->glyphs[glyph_index]; - Layout::Glyph new_glyph; - new_glyph.glyph = unbroken_span_glyph_info->glyph; - new_glyph.in_character = _flow._characters.size(); - new_glyph.rotation = glyph_rotate; - new_glyph.orientation = ORIENTATION_UPRIGHT; // Only effects vertical text - - // We may have scaled font size to fit textLength; now, if - // @lengthAdjust=spacingAndGlyphs, this scaling must be only horizontal, - // not vertical, so we unscale it back vertically during output - if (_flow.lengthAdjust == Inkscape::Text::Layout::LENGTHADJUST_SPACINGANDGLYPHS) - new_glyph.vertical_scale = 1.0 / _flow.getTextLengthMultiplierDue(); - else - new_glyph.vertical_scale = 1.0; - - // Position glyph -------------------- - new_glyph.x = current_x + unbroken_span_glyph_info->geometry.x_offset * font_size_multiplier; - new_glyph.y =_y_offset; - - // y-coordinate is flipped between vertical and horizontal text... delta_y is common offset but applied with opposite sign - double delta_y = unbroken_span_glyph_info->geometry.y_offset * font_size_multiplier + unbroken_span.baseline_shift; - SPCSSBaseline dominant_baseline = _flow._blockBaseline(); - - if (_block_progression == LEFT_TO_RIGHT || _block_progression == RIGHT_TO_LEFT) { - // Vertical text - - // Default dominant baseline is determined by overall block (i.e. <text>) 'text-orientation' value. - if( _flow._blockTextOrientation() != SP_CSS_TEXT_ORIENTATION_SIDEWAYS ) { - if( dominant_baseline == SP_CSS_BASELINE_AUTO ) dominant_baseline = SP_CSS_BASELINE_CENTRAL; - } else { - if( dominant_baseline == SP_CSS_BASELINE_AUTO ) dominant_baseline = SP_CSS_BASELINE_ALPHABETIC; - } + if (unbroken_span.glyph_string->log_clusters[glyph_index] < (int)unbroken_span.text_bytes + && *iter_source_text == UNICODE_SOFT_HYPHEN + && glyph_index + 1 != it_span->end_glyph_index) { + // if we're looking at a soft hyphen and it's not the last glyph in the + // chunk we don't draw the glyph but we still need to add to _characters + Layout::Character new_character; + new_character.in_span = _flow._spans.size(); // the span hasn't been added yet, so no -1 + new_character.char_attributes = para.char_attributes[unbroken_span.char_index_in_para + char_index_in_unbroken_span]; + new_character.in_glyph = -1; + _flow._characters.push_back(new_character); + iter_source_text++; + char_index_in_unbroken_span++; + while (glyph_index < (unsigned)unbroken_span.glyph_string->num_glyphs + && unbroken_span.glyph_string->log_clusters[glyph_index] == (int)char_byte) + glyph_index++; + glyph_index--; + continue; + } - new_glyph.y += delta_y; + // create the Layout::Glyph + PangoGlyphInfo *unbroken_span_glyph_info = &unbroken_span.glyph_string->glyphs[glyph_index]; + Layout::Glyph new_glyph; + new_glyph.glyph = unbroken_span_glyph_info->glyph; + new_glyph.in_character = _flow._characters.size(); + new_glyph.rotation = glyph_rotate; + new_glyph.orientation = ORIENTATION_UPRIGHT; // Only effects vertical text + + // We may have scaled font size to fit textLength; now, if + // @lengthAdjust=spacingAndGlyphs, this scaling must be only horizontal, + // not vertical, so we unscale it back vertically during output + if (_flow.lengthAdjust == Inkscape::Text::Layout::LENGTHADJUST_SPACINGANDGLYPHS) + new_glyph.vertical_scale = 1.0 / _flow.getTextLengthMultiplierDue(); + else + new_glyph.vertical_scale = 1.0; + + // Position glyph -------------------- + new_glyph.x = current_x + unbroken_span_glyph_info->geometry.x_offset * font_size_multiplier; + new_glyph.y =_y_offset; + + // y-coordinate is flipped between vertical and horizontal text... delta_y is common offset but applied with opposite sign + double delta_y = unbroken_span_glyph_info->geometry.y_offset * font_size_multiplier + unbroken_span.baseline_shift; + SPCSSBaseline dominant_baseline = _flow._blockBaseline(); + + if (_block_progression == LEFT_TO_RIGHT || _block_progression == RIGHT_TO_LEFT) { + // Vertical text + + // Default dominant baseline is determined by overall block (i.e. <text>) 'text-orientation' value. + if( _flow._blockTextOrientation() != SP_CSS_TEXT_ORIENTATION_SIDEWAYS ) { + if( dominant_baseline == SP_CSS_BASELINE_AUTO ) dominant_baseline = SP_CSS_BASELINE_CENTRAL; + } else { + if( dominant_baseline == SP_CSS_BASELINE_AUTO ) dominant_baseline = SP_CSS_BASELINE_ALPHABETIC; + } - // TODO: Should also check 'glyph_orientation_vertical' if 'text-orientation' is unset... - if( new_span.text_orientation == SP_CSS_TEXT_ORIENTATION_SIDEWAYS || - (new_span.text_orientation == SP_CSS_TEXT_ORIENTATION_MIXED && - para.pango_items[unbroken_span.pango_item_index].item->analysis.gravity == 0) ) { + new_glyph.y += delta_y; - // Sideways orientation (Latin characters, CJK punctuation), 90deg rotation done at output stage. zzzzzzz - new_glyph.orientation = ORIENTATION_SIDEWAYS; + // TODO: Should also check 'glyph_orientation_vertical' if 'text-orientation' is unset... + if( new_span.text_orientation == SP_CSS_TEXT_ORIENTATION_SIDEWAYS || + (new_span.text_orientation == SP_CSS_TEXT_ORIENTATION_MIXED && + para.pango_items[unbroken_span.pango_item_index].item->analysis.gravity == 0) ) { - new_glyph.y -= new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->GetBaselines()[ dominant_baseline ]; - new_glyph.width = new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->Advance(unbroken_span_glyph_info->glyph, false); + // Sideways orientation (Latin characters, CJK punctuation), 90deg rotation done at output stage. zzzzzzz + new_glyph.orientation = ORIENTATION_SIDEWAYS; - } else { - // Upright orientation + new_glyph.y -= new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->GetBaselines()[ dominant_baseline ]; + new_glyph.width = new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->Advance(unbroken_span_glyph_info->glyph, false); - new_glyph.x += new_span.line_height.ascent; + } else { + // Upright orientation - // Glyph reference point is center (shift: left edge to center glyph) - new_glyph.y -= unbroken_span_glyph_info->geometry.width * 0.5 * font_size_multiplier; - new_glyph.y -= new_span.font_size * (para.pango_items[unbroken_span.pango_item_index].font->GetBaselines()[ dominant_baseline ] - - para.pango_items[unbroken_span.pango_item_index].font->GetBaselines()[ SP_CSS_BASELINE_CENTRAL ] ); + new_glyph.x += new_span.line_height.ascent; - new_glyph.width = new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->Advance(unbroken_span_glyph_info->glyph, true); - if( new_glyph.width == 0 ) { - new_glyph.width = unbroken_span_glyph_info->geometry.width * font_size_multiplier; - } + // Glyph reference point is center (shift: left edge to center glyph) + new_glyph.y -= unbroken_span_glyph_info->geometry.width * 0.5 * font_size_multiplier; + new_glyph.y -= new_span.font_size * (para.pango_items[unbroken_span.pango_item_index].font->GetBaselines()[ dominant_baseline ] - + para.pango_items[unbroken_span.pango_item_index].font->GetBaselines()[ SP_CSS_BASELINE_CENTRAL ] ); + new_glyph.width = new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->Advance(unbroken_span_glyph_info->glyph, true); + if( new_glyph.width == 0 ) { + new_glyph.width = unbroken_span_glyph_info->geometry.width * font_size_multiplier; } - } else { - // Horizontal text - - if( dominant_baseline == SP_CSS_BASELINE_AUTO ) dominant_baseline = SP_CSS_BASELINE_ALPHABETIC; - new_glyph.y -= delta_y; - new_glyph.y += new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->GetBaselines()[ dominant_baseline ]; - - new_glyph.width = unbroken_span_glyph_info->geometry.width * font_size_multiplier; - if ((new_glyph.width == 0) && (para.pango_items[unbroken_span.pango_item_index].font)) - new_glyph.width = new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->Advance(unbroken_span_glyph_info->glyph, false); - // for some reason pango returns zero width for invalid glyph characters (those empty boxes), so go to freetype for the info } + } else { + // Horizontal text + if( dominant_baseline == SP_CSS_BASELINE_AUTO ) dominant_baseline = SP_CSS_BASELINE_ALPHABETIC; - if (new_span.direction == RIGHT_TO_LEFT) { - // pango wanted to give us glyphs in visual order but we refused, so we need to work - // out where the cluster start is ourselves - double cluster_width = 0.0; - for (unsigned rtl_index = glyph_index; rtl_index < it_span->end_glyph_index ; rtl_index++) { - if (unbroken_span.glyph_string->glyphs[rtl_index].attr.is_cluster_start && rtl_index != glyph_index) - break; - if (_block_progression == LEFT_TO_RIGHT || _block_progression == RIGHT_TO_LEFT) - // Vertical text - cluster_width += new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->Advance(unbroken_span.glyph_string->glyphs[rtl_index].glyph, true); - else - // Horizontal text - cluster_width += font_size_multiplier * unbroken_span.glyph_string->glyphs[rtl_index].geometry.width; - } - new_glyph.x -= cluster_width; + new_glyph.y -= delta_y; + new_glyph.y += new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->GetBaselines()[ dominant_baseline ]; + + new_glyph.width = unbroken_span_glyph_info->geometry.width * font_size_multiplier; + if ((new_glyph.width == 0) && (para.pango_items[unbroken_span.pango_item_index].font)) + new_glyph.width = new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->Advance(unbroken_span_glyph_info->glyph, false); + // for some reason pango returns zero width for invalid glyph characters (those empty boxes), so go to freetype for the info + } + + + if (new_span.direction == RIGHT_TO_LEFT) { + // pango wanted to give us glyphs in visual order but we refused, so we need to work + // out where the cluster start is ourselves + double cluster_width = 0.0; + for (unsigned rtl_index = glyph_index; rtl_index < it_span->end_glyph_index ; rtl_index++) { + if (unbroken_span.glyph_string->glyphs[rtl_index].attr.is_cluster_start && rtl_index != glyph_index) + break; + if (_block_progression == LEFT_TO_RIGHT || _block_progression == RIGHT_TO_LEFT) + // Vertical text + cluster_width += new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->Advance(unbroken_span.glyph_string->glyphs[rtl_index].glyph, true); + else + // Horizontal text + cluster_width += font_size_multiplier * unbroken_span.glyph_string->glyphs[rtl_index].geometry.width; } - _flow._glyphs.push_back(new_glyph); - - // create the Layout::Character(s) - double advance_width = new_glyph.width; - if (newcluster){ - newcluster = 0; - // find where the text ends for this log_cluster - end_byte = it_span->start.iter_span->text_bytes; // Upper limit - for(int next_glyph_index = glyph_index+1; next_glyph_index < unbroken_span.glyph_string->num_glyphs; next_glyph_index++){ - if(unbroken_span.glyph_string->glyphs[next_glyph_index].attr.is_cluster_start){ - end_byte = unbroken_span.glyph_string->log_clusters[next_glyph_index]; - break; - } - } - // Figure out how many glyphs and characters are in the log_cluster. - log_cluster_size_glyphs = 0; - log_cluster_size_chars = 0; - for(; log_cluster_size_glyphs + glyph_index < it_span->end_glyph_index; log_cluster_size_glyphs++){ - if(unbroken_span.glyph_string->log_clusters[glyph_index ] != - unbroken_span.glyph_string->log_clusters[glyph_index + log_cluster_size_glyphs])break; - } - Glib::ustring::const_iterator lclist = iter_source_text; - unsigned lcb = char_byte; - while(lcb < end_byte){ - log_cluster_size_chars++; - lclist++; - lcb = lclist.base() - unbroken_span.input_stream_first_character.base(); + new_glyph.x -= cluster_width; + } + _flow._glyphs.push_back(new_glyph); + + // create the Layout::Character(s) + double advance_width = new_glyph.width; + if (newcluster){ + newcluster = 0; + // find where the text ends for this log_cluster + end_byte = it_span->start.iter_span->text_bytes; // Upper limit + for(int next_glyph_index = glyph_index+1; next_glyph_index < unbroken_span.glyph_string->num_glyphs; next_glyph_index++){ + if(unbroken_span.glyph_string->glyphs[next_glyph_index].attr.is_cluster_start){ + end_byte = unbroken_span.glyph_string->log_clusters[next_glyph_index]; + break; } } - while (char_byte < end_byte) { - /* Hack to survive ligatures: in log_cluster keep the number of available chars >= number of glyphs remaining. - When there are no ligatures these two sizes are always the same. - */ - if(log_cluster_size_chars < log_cluster_size_glyphs){ - log_cluster_size_glyphs--; - break; - } - Layout::Character new_character; - new_character.in_span = _flow._spans.size(); - new_character.x = x_in_span; - new_character.char_attributes = para.char_attributes[unbroken_span.char_index_in_para + char_index_in_unbroken_span]; - new_character.in_glyph = _flow._glyphs.size() - 1; - _flow._characters.push_back(new_character); - if (new_character.char_attributes.is_white) - advance_width += text_source->style->word_spacing.computed * _flow.getTextLengthMultiplierDue() + add_to_each_whitespace; // justification - if (new_character.char_attributes.is_cursor_position) - advance_width += text_source->style->letter_spacing.computed * _flow.getTextLengthMultiplierDue(); - advance_width += _flow.getTextLengthIncrementDue(); - iter_source_text++; - char_index_in_unbroken_span++; - char_byte = iter_source_text.base() - unbroken_span.input_stream_first_character.base(); - log_cluster_size_chars--; + // Figure out how many glyphs and characters are in the log_cluster. + log_cluster_size_glyphs = 0; + log_cluster_size_chars = 0; + for(; log_cluster_size_glyphs + glyph_index < it_span->end_glyph_index; log_cluster_size_glyphs++){ + if(unbroken_span.glyph_string->log_clusters[glyph_index ] != + unbroken_span.glyph_string->log_clusters[glyph_index + log_cluster_size_glyphs])break; } - - advance_width *= direction_sign; - if (new_span.direction != para.direction) { - counter_directional_width_remaining -= advance_width; - current_x -= advance_width; - x_in_span_last -= advance_width; - } else { - current_x += advance_width; - x_in_span_last += advance_width; + Glib::ustring::const_iterator lclist = iter_source_text; + unsigned lcb = char_byte; + while(lcb < end_byte){ + log_cluster_size_chars++; + lclist++; + lcb = lclist.base() - unbroken_span.input_stream_first_character.base(); } } - } else if (_flow._input_stream[unbroken_span.input_index]->Type() == CONTROL_CODE) { - current_x += static_cast<InputStreamControlCode const *>(_flow._input_stream[unbroken_span.input_index])->width; - } + while (char_byte < end_byte) { + /* Hack to survive ligatures: in log_cluster keep the number of available chars >= number of glyphs remaining. + When there are no ligatures these two sizes are always the same. + */ + if(log_cluster_size_chars < log_cluster_size_glyphs){ + log_cluster_size_glyphs--; + break; + } + Layout::Character new_character; + new_character.in_span = _flow._spans.size(); + new_character.x = x_in_span; + new_character.char_attributes = para.char_attributes[unbroken_span.char_index_in_para + char_index_in_unbroken_span]; + new_character.in_glyph = _flow._glyphs.size() - 1; + _flow._characters.push_back(new_character); + if (new_character.char_attributes.is_white) + advance_width += text_source->style->word_spacing.computed * _flow.getTextLengthMultiplierDue() + add_to_each_whitespace; // justification + if (new_character.char_attributes.is_cursor_position) + advance_width += text_source->style->letter_spacing.computed * _flow.getTextLengthMultiplierDue(); + advance_width += _flow.getTextLengthIncrementDue(); + iter_source_text++; + char_index_in_unbroken_span++; + char_byte = iter_source_text.base() - unbroken_span.input_stream_first_character.base(); + log_cluster_size_chars--; + } - new_span.x_end = new_span.x_start + x_in_span_last; - _flow._spans.push_back(new_span); - previous_direction = new_span.direction; + advance_width *= direction_sign; + if (new_span.direction != para.direction) { + counter_directional_width_remaining -= advance_width; + current_x -= advance_width; + x_in_span_last -= advance_width; + } else { + current_x += advance_width; + x_in_span_last += advance_width; + } + } + } else if (_flow._input_stream[unbroken_span.input_index]->Type() == CONTROL_CODE) { + current_x += static_cast<InputStreamControlCode const *>(_flow._input_stream[unbroken_span.input_index])->width; } - // end adding spans to the list, on to the next chunk... - } - TRACE((" End _outputLine\n")); - } -/* *********************************************************************************************************/ -// Setup and top-level functions - - /** initialises the ScanlineMaker for the first shape in the flow, or - the infinite version if we're not doing wrapping. */ - void _createFirstScanlineMaker() - { - _current_shape_index = 0; - if (_flow._input_wrap_shapes.empty()) { - // create the special no-wrapping infinite scanline maker - double initial_x = 0, initial_y = 0; - InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_flow._input_stream.front()); - if (!text_source->x.empty()) - initial_x = text_source->x.front().computed; - if (!text_source->y.empty()) - initial_y = text_source->y.front().computed; - _scanline_maker = new InfiniteScanlineMaker(initial_x, initial_y, _block_progression); - TRACE((" wrapping disabled\n")); + new_span.x_end = new_span.x_start + x_in_span_last; + _flow._spans.push_back(new_span); + previous_direction = new_span.direction; } - else { - _scanline_maker = new ShapeScanlineMaker(_flow._input_wrap_shapes[_current_shape_index].shape, _block_progression); - TRACE((" begin wrap shape 0\n")); - } - } - -public: - Calculator(Layout *text_flow) - : _flow(*text_flow) {} - - bool calculate(); -}; - -/* fixme: I don't like the fact that InputItemInfo etc. use the default copy constructor and - * operator= (and thus don't involve incrementing reference counts), yet they provide a free method - * that does delete or Unref. - * - * I suggest using the garbage collector to manage deletion. - */ -void Layout::Calculator::InputItemInfo::free() -{ - if (sub_flow) { - delete sub_flow; - sub_flow = NULL; + // end adding spans to the list, on to the next chunk... } + TRACE((" End _outputLine\n")); } -void Layout::Calculator::PangoItemInfo::free() +/** + * Initialises the ScanlineMaker for the first shape in the flow, + * or the infinite version if we're not doing wrapping. + */ +void Layout::Calculator::_createFirstScanlineMaker() { - if (item) { - pango_item_free(item); - item = NULL; + _current_shape_index = 0; + if (_flow._input_wrap_shapes.empty()) { + // create the special no-wrapping infinite scanline maker + double initial_x = 0, initial_y = 0; + InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_flow._input_stream.front()); + if (!text_source->x.empty()) + initial_x = text_source->x.front().computed; + if (!text_source->y.empty()) + initial_y = text_source->y.front().computed; + _scanline_maker = new InfiniteScanlineMaker(initial_x, initial_y, _block_progression); + TRACE((" wrapping disabled\n")); } - if (font) { - font->Unref(); - font = NULL; + else { + _scanline_maker = new ShapeScanlineMaker(_flow._input_wrap_shapes[_current_shape_index].shape, _block_progression); + TRACE((" begin wrap shape 0\n")); } } @@ -949,21 +951,6 @@ void Layout::Calculator::BrokenSpan::setZero() word_spacing = 0.0; } -template<typename T> void Layout::Calculator::ParagraphInfo::free_sequence(T &seq) -{ - for (typename T::iterator it(seq.begin()); it != seq.end(); ++it) { - it->free(); - } - seq.clear(); -} - -void Layout::Calculator::ParagraphInfo::free() -{ - free_sequence(input_items); - free_sequence(pango_items); - free_sequence(unbroken_spans); -} - ///** // * For sections of text with a block-progression different to the rest // * of the flow, the best thing to do is to detect them in advance and @@ -1692,6 +1679,46 @@ bool Layout::Calculator::_buildChunksInScanRun(ParagraphInfo const ¶, return true; } +#ifdef DEBUG_LAYOUT_TNG_COMPUTE +/** + * For debugging, not called in distributed code + * + * Input: para->first_input_index, para->pango_items + */ +static void Layout::Calculator::dumpPangoItemsOut(ParagraphInfo *para){ + std::cout << "Pango items: " << para->pango_items.size() << std::endl; + font_factory * factory = font_factory::Default(); + for(unsigned pidx = 0 ; pidx < para->pango_items.size(); pidx++){ + std::cout + << "idx: " << pidx + << " offset: " + << para->pango_items[pidx].item->offset + << " length: " + << para->pango_items[pidx].item->length + << " font: " + << factory->ConstructFontSpecification( para->pango_items[pidx].font ) + << std::endl; + } +} + +/** + * For debugging, not called in distributed code + * + * Input: para->first_input_index, para->pango_items + */ +static void Layout::Calculator::dumpUnbrokenSpans(ParagraphInfo *para){ + std::cout << "Unbroken Spans: " << para->unbroken_spans.size() << std::endl; + for(unsigned uidx = 0 ; uidx < para->unbroken_spans.size(); uidx++){ + std::cout + << "idx: " << uidx + << " pango_item_index: " << para->unbroken_spans[uidx].pango_item_index + << " input_index: " << para->unbroken_spans[uidx].input_index + << " char_index_in_para: " << para->unbroken_spans[uidx].char_index_in_para + << " text_bytes: " << para->unbroken_spans[uidx].text_bytes + << std::endl; + } +} +#endif //DEBUG_LAYOUT_TNG_COMPUTE /** The management function to start the whole thing off. */ bool Layout::Calculator::calculate() diff --git a/src/libnrtype/Layout-TNG-Input.cpp b/src/libnrtype/Layout-TNG-Input.cpp index 6f5d4e5f8..98ce7d526 100644 --- a/src/libnrtype/Layout-TNG-Input.cpp +++ b/src/libnrtype/Layout-TNG-Input.cpp @@ -40,7 +40,13 @@ void Layout::_clearInputObjects() } // this function does nothing more than store all its parameters for future reference -void Layout::appendText(Glib::ustring const &text, SPStyle *style, void *source_cookie, OptionalTextTagAttrs const *optional_attributes, unsigned optional_attributes_offset, Glib::ustring::const_iterator text_begin, Glib::ustring::const_iterator text_end) +void Layout::appendText(Glib::ustring const &text, + SPStyle *style, + void *source_cookie, + OptionalTextTagAttrs const *optional_attributes, + unsigned optional_attributes_offset, + Glib::ustring::const_iterator text_begin, + Glib::ustring::const_iterator text_end) { if (style == NULL) return; @@ -120,40 +126,40 @@ void Layout::appendWrapShape(Shape const *shape, DisplayAlign display_align) int Layout::_enum_converter(int input, EnumConversionItem const *conversion_table, unsigned conversion_table_size) { - for (unsigned i = 0 ; i < conversion_table_size ; i++) + for (unsigned i = 0 ; i < conversion_table_size ; i++) { if (conversion_table[i].input == input) return conversion_table[i].output; + } return conversion_table[0].output; } Layout::Direction Layout::InputStreamTextSource::styleGetBlockProgression() const { - switch( style->writing_mode.computed ) { + switch( style->writing_mode.computed ) { + case SP_CSS_WRITING_MODE_LR_TB: + case SP_CSS_WRITING_MODE_RL_TB: + return TOP_TO_BOTTOM; - case SP_CSS_WRITING_MODE_LR_TB: - case SP_CSS_WRITING_MODE_RL_TB: - return TOP_TO_BOTTOM; - - case SP_CSS_WRITING_MODE_TB_RL: - return RIGHT_TO_LEFT; + case SP_CSS_WRITING_MODE_TB_RL: + return RIGHT_TO_LEFT; - case SP_CSS_WRITING_MODE_TB_LR: - return LEFT_TO_RIGHT; + case SP_CSS_WRITING_MODE_TB_LR: + return LEFT_TO_RIGHT; - default: - std::cerr << "Layout::InputTextStream::styleGetBlockProgression: invalid writing mode." << std::endl; - } - return TOP_TO_BOTTOM; + default: + std::cerr << "Layout::InputTextStream::styleGetBlockProgression: invalid writing mode." << std::endl; + } + return TOP_TO_BOTTOM; } SPCSSTextOrientation Layout::InputStreamTextSource::styleGetTextOrientation() const { - return ((SPCSSTextOrientation)style->text_orientation.computed); + return ((SPCSSTextOrientation)style->text_orientation.computed); } SPCSSBaseline Layout::InputStreamTextSource::styleGetDominantBaseline() const { - return ((SPCSSBaseline)style->dominant_baseline.computed); + return ((SPCSSBaseline)style->dominant_baseline.computed); } static Layout::Alignment text_anchor_to_alignment(unsigned anchor, Layout::Direction para_direction) @@ -202,22 +208,24 @@ Layout::Alignment Layout::InputStreamTextSource::styleGetAlignment(Layout::Direc static const Layout::EnumConversionItem enum_convert_spstyle_style_to_pango_style[] = { {SP_CSS_FONT_STYLE_NORMAL, PANGO_STYLE_NORMAL}, {SP_CSS_FONT_STYLE_ITALIC, PANGO_STYLE_ITALIC}, - {SP_CSS_FONT_STYLE_OBLIQUE, PANGO_STYLE_OBLIQUE}}; + {SP_CSS_FONT_STYLE_OBLIQUE, PANGO_STYLE_OBLIQUE} +}; static const Layout::EnumConversionItem enum_convert_spstyle_weight_to_pango_weight[] = { // NB: The Pango web page calls 500 "the normal font" but both CSS2 and the Pango // enumeration define 400 as normal. {SP_CSS_FONT_WEIGHT_NORMAL, PANGO_WEIGHT_NORMAL}, - {SP_CSS_FONT_WEIGHT_BOLD,PANGO_WEIGHT_BOLD}, - {SP_CSS_FONT_WEIGHT_100, PANGO_WEIGHT_THIN}, - {SP_CSS_FONT_WEIGHT_200, PANGO_WEIGHT_ULTRALIGHT}, - {SP_CSS_FONT_WEIGHT_300, PANGO_WEIGHT_LIGHT}, - {SP_CSS_FONT_WEIGHT_400, PANGO_WEIGHT_NORMAL}, - {SP_CSS_FONT_WEIGHT_500, PANGO_WEIGHT_MEDIUM}, - {SP_CSS_FONT_WEIGHT_600, PANGO_WEIGHT_SEMIBOLD}, - {SP_CSS_FONT_WEIGHT_700, PANGO_WEIGHT_BOLD}, - {SP_CSS_FONT_WEIGHT_800, PANGO_WEIGHT_ULTRABOLD}, - {SP_CSS_FONT_WEIGHT_900, PANGO_WEIGHT_HEAVY}}; + {SP_CSS_FONT_WEIGHT_BOLD, PANGO_WEIGHT_BOLD}, + {SP_CSS_FONT_WEIGHT_100, PANGO_WEIGHT_THIN}, + {SP_CSS_FONT_WEIGHT_200, PANGO_WEIGHT_ULTRALIGHT}, + {SP_CSS_FONT_WEIGHT_300, PANGO_WEIGHT_LIGHT}, + {SP_CSS_FONT_WEIGHT_400, PANGO_WEIGHT_NORMAL}, + {SP_CSS_FONT_WEIGHT_500, PANGO_WEIGHT_MEDIUM}, + {SP_CSS_FONT_WEIGHT_600, PANGO_WEIGHT_SEMIBOLD}, + {SP_CSS_FONT_WEIGHT_700, PANGO_WEIGHT_BOLD}, + {SP_CSS_FONT_WEIGHT_800, PANGO_WEIGHT_ULTRABOLD}, + {SP_CSS_FONT_WEIGHT_900, PANGO_WEIGHT_HEAVY} +}; static const Layout::EnumConversionItem enum_convert_spstyle_stretch_to_pango_stretch[] = { {SP_CSS_FONT_STRETCH_NORMAL, PANGO_STRETCH_NORMAL}, @@ -228,11 +236,13 @@ static const Layout::EnumConversionItem enum_convert_spstyle_stretch_to_pango_st {SP_CSS_FONT_STRETCH_SEMI_EXPANDED, PANGO_STRETCH_SEMI_EXPANDED}, {SP_CSS_FONT_STRETCH_EXPANDED, PANGO_STRETCH_EXPANDED}, {SP_CSS_FONT_STRETCH_EXTRA_EXPANDED, PANGO_STRETCH_EXTRA_EXPANDED}, - {SP_CSS_FONT_STRETCH_ULTRA_EXPANDED, PANGO_STRETCH_ULTRA_EXPANDED}}; + {SP_CSS_FONT_STRETCH_ULTRA_EXPANDED, PANGO_STRETCH_ULTRA_EXPANDED} +}; static const Layout::EnumConversionItem enum_convert_spstyle_variant_to_pango_variant[] = { {SP_CSS_FONT_VARIANT_NORMAL, PANGO_VARIANT_NORMAL}, - {SP_CSS_FONT_VARIANT_SMALL_CAPS, PANGO_VARIANT_SMALL_CAPS}}; + {SP_CSS_FONT_VARIANT_SMALL_CAPS, PANGO_VARIANT_SMALL_CAPS} +}; font_instance *Layout::InputStreamTextSource::styleGetFontInstance() const { @@ -265,26 +275,57 @@ PangoFontDescription *Layout::InputStreamTextSource::styleGetFontDescription() c } pango_font_description_set_family(descr,family.c_str()); - pango_font_description_set_weight(descr,(PangoWeight)_enum_converter(style->font_weight.computed, enum_convert_spstyle_weight_to_pango_weight, sizeof(enum_convert_spstyle_weight_to_pango_weight)/sizeof(enum_convert_spstyle_weight_to_pango_weight[0]))); - pango_font_description_set_style(descr,(PangoStyle)_enum_converter(style->font_style.computed, enum_convert_spstyle_style_to_pango_style, sizeof(enum_convert_spstyle_style_to_pango_style)/sizeof(enum_convert_spstyle_style_to_pango_style[0]))); - pango_font_description_set_variant(descr,(PangoVariant)_enum_converter(style->font_variant.computed, enum_convert_spstyle_variant_to_pango_variant, sizeof(enum_convert_spstyle_variant_to_pango_variant)/sizeof(enum_convert_spstyle_variant_to_pango_variant[0]))); + pango_font_description_set_weight( + descr, + (PangoWeight)_enum_converter( + style->font_weight.computed, + enum_convert_spstyle_weight_to_pango_weight, + sizeof(enum_convert_spstyle_weight_to_pango_weight) / sizeof(enum_convert_spstyle_weight_to_pango_weight[0]) + ) + ); + pango_font_description_set_style( + descr, + (PangoStyle)_enum_converter( + style->font_style.computed, + enum_convert_spstyle_style_to_pango_style, + sizeof(enum_convert_spstyle_style_to_pango_style) / sizeof(enum_convert_spstyle_style_to_pango_style[0]) + ) + ); + pango_font_description_set_variant( + descr, + (PangoVariant)_enum_converter( + style->font_variant.computed, + enum_convert_spstyle_variant_to_pango_variant, + sizeof(enum_convert_spstyle_variant_to_pango_variant) / sizeof(enum_convert_spstyle_variant_to_pango_variant[0]) + ) + ); #ifdef USE_PANGO_WIN32 // damn Pango fudges the size, so we need to unfudge. See source of pango_win32_font_map_init() - pango_font_description_set_size(descr, (int) ((font_factory::Default())->fontSize*PANGO_SCALE*72/GetDeviceCaps(pango_win32_get_dc(),LOGPIXELSY))); // mandatory huge size (hinting workaround) + pango_font_description_set_size( + descr, + (int) ((font_factory::Default())->fontSize*PANGO_SCALE*72 / GetDeviceCaps(pango_win32_get_dc(),LOGPIXELSY)) // mandatory huge size (hinting workaround) + ); // we don't set stretch on Win32, because pango-win32 has no concept of it // (Windows doesn't really provide any useful field it could use). // If we did set stretch, then any text with a font-stretch attribute would // end up falling back to Arial. #else pango_font_description_set_size(descr, (int) ((font_factory::Default())->fontSize*PANGO_SCALE)); // mandatory huge size (hinting workaround) - pango_font_description_set_stretch(descr,(PangoStretch)_enum_converter(style->font_stretch.computed, enum_convert_spstyle_stretch_to_pango_stretch, sizeof(enum_convert_spstyle_stretch_to_pango_stretch)/sizeof(enum_convert_spstyle_stretch_to_pango_stretch[0]))); + pango_font_description_set_stretch( + descr, + (PangoStretch)_enum_converter( + style->font_stretch.computed, + enum_convert_spstyle_stretch_to_pango_stretch, + sizeof(enum_convert_spstyle_stretch_to_pango_stretch) / sizeof(enum_convert_spstyle_stretch_to_pango_stretch[0]) + ) + ); #endif return descr; } Layout::InputStreamTextSource::~InputStreamTextSource() { - sp_style_unref(style); + sp_style_unref(style); } }//namespace Text diff --git a/src/libnrtype/Layout-TNG-Output.cpp b/src/libnrtype/Layout-TNG-Output.cpp index a1a19f2b3..7f20dee95 100644 --- a/src/libnrtype/Layout-TNG-Output.cpp +++ b/src/libnrtype/Layout-TNG-Output.cpp @@ -515,6 +515,7 @@ void Layout::showGlyphs(CairoRenderContext *ctx) const } #endif +#if DEBUG_TEXTLAYOUT_DUMPASTEXT // these functions are for dumpAsText() only. No need to translate static char const *direction_to_text(Layout::Direction d) { @@ -557,6 +558,7 @@ static char const *weight_to_text(PangoWeight w) } return "???"; } +#endif //DEBUG_TEXTLAYOUT_DUMPASTEXT Glib::ustring Layout::getFontFamily(unsigned span_index) const { @@ -570,6 +572,7 @@ Glib::ustring Layout::getFontFamily(unsigned span_index) const return ""; } +#if DEBUG_TEXTLAYOUT_DUMPASTEXT Glib::ustring Layout::dumpAsText() const { Glib::ustring result; @@ -609,7 +612,13 @@ Glib::ustring Layout::dumpAsText() const _lines[_chunks[_spans[span_index].in_chunk].in_line].in_shape) + Glib::ustring::compose(" in chunk %1 (x=%2, baselineshift=%3)\n", _spans[span_index].in_chunk, _chunks[_spans[span_index].in_chunk].left_x, _spans[span_index].baseline_shift); if (_spans[span_index].font) { - result += Glib::ustring::compose(" font '%1' %2 %3 %4\n", sp_font_description_get_family(_spans[span_index].font->descr), _spans[span_index].font_size, style_to_text(pango_font_description_get_style(_spans[span_index].font->descr)), weight_to_text(pango_font_description_get_weight(_spans[span_index].font->descr))); + result += Glib::ustring::compose( + " font '%1' %2 %3 %4\n", + sp_font_description_get_family(_spans[span_index].font->descr), + _spans[span_index].font_size, + style_to_text( pango_font_description_get_style(_spans[span_index].font->descr) ), + weight_to_text( pango_font_description_get_weight(_spans[span_index].font->descr) ) + ); } result += Glib::ustring::compose(" x_start = %1, x_end = %2\n", _spans[span_index].x_start, _spans[span_index].x_end) + Glib::ustring::compose(" line height: ascent %1, descent %2\n", _spans[span_index].line_height.ascent, _spans[span_index].line_height.descent) @@ -640,6 +649,7 @@ Glib::ustring Layout::dumpAsText() const result += "EOT\n"; return result; } +#endif //DEBUG_TEXTLAYOUT_DUMPASTEXT void Layout::fitToPathAlign(SVGLength const &startOffset, Path const &path) { diff --git a/src/libnrtype/Layout-TNG.h b/src/libnrtype/Layout-TNG.h index c39315106..878c76af8 100644 --- a/src/libnrtype/Layout-TNG.h +++ b/src/libnrtype/Layout-TNG.h @@ -11,6 +11,8 @@ #ifndef __LAYOUT_TNG_H__ #define __LAYOUT_TNG_H__ +//#define DEBUG_TEXTLAYOUT_DUMPASTEXT + #ifdef HAVE_CONFIG_H # include "config.h" #endif @@ -402,10 +404,12 @@ public: /** Returns the font family of the indexed span */ Glib::ustring getFontFamily(unsigned span_index) const; +#if DEBUG_TEXTLAYOUT_DUMPASTEXT /** debug and unit test method. Creates a textual representation of the contents of this object. The output is designed to be both human-readable and comprehensible when diffed with a known-good dump. */ Glib::ustring dumpAsText() const; +#endif /** Moves all the glyphs in the structure so that the baseline of all the characters sits neatly along the path specified. If the text has diff --git a/src/live_effects/lpe-bool.h b/src/live_effects/lpe-bool.h index 1c0aa0243..ce46f14d1 100644 --- a/src/live_effects/lpe-bool.h +++ b/src/live_effects/lpe-bool.h @@ -42,7 +42,7 @@ public: inline friend bool_op to_bool_op(bool_op_ex val) { assert(val <= bool_op_ex_slice); - (bool_op) val; + return (bool_op) val; } private: diff --git a/src/sp-flowtext.cpp b/src/sp-flowtext.cpp index d79652bf4..741ca7886 100644 --- a/src/sp-flowtext.cpp +++ b/src/sp-flowtext.cpp @@ -463,7 +463,9 @@ void SPFlowtext::rebuildLayout() _buildLayoutInput(this, exclusion_shape, &shapes, &pending_line_break_object); delete exclusion_shape; layout.calculateFlow(); - //g_print("%s", layout.dumpAsText().c_str()); +#if DEBUG_TEXTLAYOUT_DUMPASTEXT + g_print("%s", layout.dumpAsText().c_str()); +#endif } void SPFlowtext::_clearFlow(Inkscape::DrawingGroup *in_arena) diff --git a/src/sp-text.cpp b/src/sp-text.cpp index b066ca3de..2a88c5009 100644 --- a/src/sp-text.cpp +++ b/src/sp-text.cpp @@ -620,12 +620,16 @@ void SPText::rebuildLayout() if (SP_IS_TEXTPATH(&child)) { SPTextPath const *textpath = SP_TEXTPATH(&child); if (textpath->originalPath != NULL) { - //g_print("%s", layout.dumpAsText().c_str()); +#if DEBUG_TEXTLAYOUT_DUMPASTEXT + g_print("%s", layout.dumpAsText().c_str()); +#endif layout.fitToPathAlign(textpath->startOffset, *textpath->originalPath); } } } - //g_print("%s", layout.dumpAsText().c_str()); +#if DEBUG_TEXTLAYOUT_DUMPASTEXT + g_print("%s", layout.dumpAsText().c_str()); +#endif // set the x,y attributes on role:line spans for (auto& child: children) { |
