diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000..7601c5e1e --- /dev/null +++ b/.clang-format @@ -0,0 +1,60 @@ +# Options are listed here: +# https://clang.llvm.org/docs/ClangFormatStyleOptions.html +--- +Language: Cpp +BasedOnStyle: Microsoft +Standard: c++17 +TabWidth: 4 +UseTab: Always +IndentWidth: 4 +PointerAlignment: Left +SpaceAfterCStyleCast: true +SpaceBeforeParens: false +BraceWrapping: + AfterCaseLabel: false + AfterClass: true + AfterControlStatement: Always + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: true + AfterStruct: true + AfterUnion: false + AfterExternBlock: true + BeforeCatch: true + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +NamespaceIndentation: All +AlignConsecutiveDeclarations: + Enabled: true + AcrossEmptyLines: false + AcrossComments: false +AlignConsecutiveAssignments: + Enabled: true + AcrossEmptyLines: true + AcrossComments: true + AlignCompound: true + AlignFunctionPointers: true + PadOperators: true +AlignConsecutiveMacros: + Enabled: true + AcrossEmptyLines: true + AcrossComments: true + AlignCompound: true + AlignFunctionPointers: true + PadOperators: true +SortIncludes: false +IndentCaseBlocks: true +InsertNewlineAtEOF: true +BreakConstructorInitializers: AfterColon +PackConstructorInitializers: Never +AllowShortLambdasOnASingleLine: true +AllowShortIfStatementsOnASingleLine: Never +AllowShortFunctionsOnASingleLine: Inline +AlignArrayOfStructures: Left +AllowShortBlocksOnASingleLine: true diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 137cea878..855ba6660 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -16,23 +16,29 @@ jobs: # well on Windows or Mac. You can convert this to a matrix build if you need # cross-platform coverage. # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v2 + - name: Packages + # Install required packages + run: | + sudo apt-get update + sudo apt-get install -yq --no-install-recommends --no-install-suggests libcairo2-dev libpango1.0-dev libgtk-3-dev + - name: Configure CMake # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type - run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + run: cmake -B ${{github.workspace}}/build -DLITEHTML_BUILD_TESTING=ON -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} - name: Build # Build your program with the given configuration - run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} + run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -j3 - name: Test working-directory: ${{github.workspace}}/build # Execute tests defined by the CMake configuration. # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail - run: ctest -C ${{env.BUILD_TYPE}} --rerun-failed --output-on-failure + run: ctest -C ${{env.BUILD_TYPE}} --test-dir litehtml-tests-build --rerun-failed --output-on-failure -j3 diff --git a/.gitignore b/.gitignore index 5dcb07d14..1c408c45b 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ _build/ _build32/ _build64/ *.inc +.cache/ # Test results *-FAILED.png @@ -148,6 +149,7 @@ csx/ AppPackages/ # Others +.DS_Store .idea/ .vs/ .vscode/ @@ -184,3 +186,7 @@ UpgradeLog*.htm # Microsoft Fakes FakesAssemblies/ + +# litehtml specific files +test/visual-test.sh + diff --git a/CMakeLists.txt b/CMakeLists.txt index 595689f85..27f8980ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,14 +2,9 @@ cmake_minimum_required(VERSION 3.11) project(litehtml LANGUAGES C CXX) +option(LITEHTML_BUILD_TESTING "enable testing for litehtml" OFF) -option(LITEHTML_BUILD_TESTING "enable testing for litehtml" ON) - -if(LITEHTML_BUILD_TESTING) - include(CTest) - enable_testing() -endif() - +if (NOT LITEHTML_BUILD_TESTING) # Soname # MAJOR is incremented when symbols are removed or changed in an incompatible way # MINOR is incremented when new symbols are added @@ -19,161 +14,169 @@ set(PROJECT_MINOR 0) option(EXTERNAL_GUMBO "Link against external gumbo instead of shipping a bundled copy" OFF) if(NOT EXTERNAL_GUMBO) - add_subdirectory(src/gumbo) + add_subdirectory(src/gumbo) endif() set(SOURCE_LITEHTML - src/codepoint.cpp - src/css_length.cpp - src/css_selector.cpp - src/document.cpp - src/document_container.cpp - src/el_anchor.cpp - src/el_base.cpp - src/el_before_after.cpp - src/el_body.cpp - src/el_break.cpp - src/el_cdata.cpp - src/el_comment.cpp - src/el_div.cpp - src/element.cpp - src/el_font.cpp - src/el_image.cpp - src/el_link.cpp - src/el_para.cpp - src/el_script.cpp - src/el_space.cpp - src/el_style.cpp - src/el_table.cpp - src/el_td.cpp - src/el_text.cpp - src/el_title.cpp - src/el_tr.cpp - src/html.cpp - src/html_tag.cpp - src/iterators.cpp - src/media_query.cpp - src/style.cpp - src/stylesheet.cpp - src/table.cpp - src/tstring_view.cpp - src/url.cpp - src/url_path.cpp - src/utf8_strings.cpp - src/web_color.cpp - src/num_cvt.cpp - src/strtod.cpp - src/string_id.cpp - src/css_properties.cpp - src/line_box.cpp - src/css_borders.cpp - src/render_item.cpp - src/render_block_context.cpp - src/render_block.cpp - src/render_inline_context.cpp - src/render_table.cpp - src/render_flex.cpp - src/render_image.cpp - src/formatting_context.cpp - src/flex_item.cpp - src/flex_line.cpp) - -set(HEADER_LITEHTML - include/litehtml.h - include/litehtml/background.h - include/litehtml/borders.h - include/litehtml/codepoint.h - include/litehtml/css_length.h - include/litehtml/css_margins.h - include/litehtml/css_offsets.h - include/litehtml/css_position.h - include/litehtml/css_selector.h - include/litehtml/document.h - include/litehtml/document_container.h - include/litehtml/el_anchor.h - include/litehtml/el_base.h - include/litehtml/el_before_after.h - include/litehtml/el_body.h - include/litehtml/el_break.h - include/litehtml/el_cdata.h - include/litehtml/el_comment.h - include/litehtml/el_div.h - include/litehtml/el_font.h - include/litehtml/el_image.h - include/litehtml/el_link.h - include/litehtml/el_para.h - include/litehtml/el_script.h - include/litehtml/el_space.h - include/litehtml/el_style.h - include/litehtml/el_table.h - include/litehtml/el_td.h - include/litehtml/el_text.h - include/litehtml/el_title.h - include/litehtml/el_tr.h - include/litehtml/element.h - include/litehtml/html.h - include/litehtml/html_tag.h - include/litehtml/iterators.h - include/litehtml/media_query.h - include/litehtml/os_types.h - include/litehtml/style.h - include/litehtml/stylesheet.h - include/litehtml/table.h - include/litehtml/tstring_view.h - include/litehtml/types.h - include/litehtml/url.h - include/litehtml/url_path.h - include/litehtml/utf8_strings.h - include/litehtml/web_color.h - include/litehtml/num_cvt.h - include/litehtml/css_properties.h - include/litehtml/line_box.h - include/litehtml/render_item.h - include/litehtml/render_flex.h - include/litehtml/render_image.h - include/litehtml/render_inline.h - include/litehtml/render_table.h - include/litehtml/render_inline_context.h - include/litehtml/render_block_context.h - include/litehtml/render_block.h - include/litehtml/master_css.h - include/litehtml/string_id.h - include/litehtml/formatting_context.h - include/litehtml/flex_item.h - include/litehtml/flex_line.h + src/codepoint.cpp + src/css_length.cpp + src/css_selector.cpp + src/css_tokenizer.cpp + src/css_parser.cpp + src/document.cpp + src/document_container.cpp + src/el_anchor.cpp + src/el_base.cpp + src/el_before_after.cpp + src/el_body.cpp + src/el_break.cpp + src/el_cdata.cpp + src/el_comment.cpp + src/el_div.cpp + src/element.cpp + src/el_font.cpp + src/el_image.cpp + src/el_link.cpp + src/el_para.cpp + src/el_script.cpp + src/el_space.cpp + src/el_style.cpp + src/el_table.cpp + src/el_td.cpp + src/el_text.cpp + src/el_title.cpp + src/el_tr.cpp + src/encodings.cpp + src/html.cpp + src/html_tag.cpp + src/html_microsyntaxes.cpp + src/iterators.cpp + src/media_query.cpp + src/style.cpp + src/stylesheet.cpp + src/table.cpp + src/tstring_view.cpp + src/url.cpp + src/url_path.cpp + src/utf8_strings.cpp + src/web_color.cpp + src/num_cvt.cpp + src/strtod.cpp + src/string_id.cpp + src/css_properties.cpp + src/line_box.cpp + src/css_borders.cpp + src/render_item.cpp + src/render_block_context.cpp + src/render_block.cpp + src/render_inline_context.cpp + src/render_table.cpp + src/render_flex.cpp + src/render_image.cpp + src/formatting_context.cpp + src/flex_item.cpp + src/flex_line.cpp + src/background.cpp + src/gradient.cpp ) -set(TEST_LITEHTML - test/cssTest.cpp - test/mediaQueryTest.cpp - test/codepoint_test.cpp - test/tstring_view_test.cpp - test/url_test.cpp - test/url_path_test.cpp - test/render_test.cpp - containers/test/test_container.cpp - containers/test/Font.cpp - containers/test/Bitmap.cpp - containers/test/lodepng.cpp +set(HEADER_LITEHTML + include/litehtml.h + include/litehtml/background.h + include/litehtml/borders.h + include/litehtml/codepoint.h + include/litehtml/css_length.h + include/litehtml/css_margins.h + include/litehtml/css_offsets.h + include/litehtml/css_position.h + include/litehtml/css_selector.h + include/litehtml/css_parser.h + include/litehtml/css_tokenizer.h + include/litehtml/document.h + include/litehtml/document_container.h + include/litehtml/el_anchor.h + include/litehtml/el_base.h + include/litehtml/el_before_after.h + include/litehtml/el_body.h + include/litehtml/el_break.h + include/litehtml/el_cdata.h + include/litehtml/el_comment.h + include/litehtml/el_div.h + include/litehtml/el_font.h + include/litehtml/el_image.h + include/litehtml/el_link.h + include/litehtml/el_para.h + include/litehtml/el_script.h + include/litehtml/el_space.h + include/litehtml/el_style.h + include/litehtml/el_table.h + include/litehtml/el_td.h + include/litehtml/el_text.h + include/litehtml/el_title.h + include/litehtml/el_tr.h + include/litehtml/element.h + include/litehtml/encodings.h + include/litehtml/html.h + include/litehtml/html_tag.h + include/litehtml/html_microsyntaxes.h + include/litehtml/iterators.h + include/litehtml/media_query.h + include/litehtml/os_types.h + include/litehtml/style.h + include/litehtml/stylesheet.h + include/litehtml/table.h + include/litehtml/tstring_view.h + include/litehtml/types.h + include/litehtml/url.h + include/litehtml/url_path.h + include/litehtml/utf8_strings.h + include/litehtml/web_color.h + include/litehtml/num_cvt.h + include/litehtml/css_properties.h + include/litehtml/line_box.h + include/litehtml/render_item.h + include/litehtml/render_flex.h + include/litehtml/render_image.h + include/litehtml/render_inline.h + include/litehtml/render_table.h + include/litehtml/render_inline_context.h + include/litehtml/render_block_context.h + include/litehtml/render_block.h + include/litehtml/master_css.h + include/litehtml/string_id.h + include/litehtml/formatting_context.h + include/litehtml/flex_item.h + include/litehtml/flex_line.h + include/litehtml/gradient.h + include/litehtml/font_description.h ) set(PROJECT_LIB_VERSION ${PROJECT_MAJOR}.${PROJECT_MINOR}.0) set(PROJECT_SO_VERSION ${PROJECT_MAJOR}) -add_library(${PROJECT_NAME} ${SOURCE_LITEHTML}) +if (MSVC) + # warning level 4 + add_compile_options(/W4) + add_compile_options(/permissive- /utf-8) +else() + # additional warnings + add_compile_options(-Wall -Wextra -Wpedantic) +endif() + +add_library(${PROJECT_NAME} ${HEADER_LITEHTML} ${SOURCE_LITEHTML}) set_target_properties(${PROJECT_NAME} PROPERTIES VERSION ${PROJECT_LIB_VERSION} SOVERSION ${PROJECT_SO_VERSION}) set_target_properties(${PROJECT_NAME} PROPERTIES - CXX_STANDARD 11 - C_STANDARD 99 - PUBLIC_HEADER "${HEADER_LITEHTML}" + CXX_STANDARD 17 + C_STANDARD 99 + PUBLIC_HEADER "${HEADER_LITEHTML}" ) # Export litehtml includes. target_include_directories(${PROJECT_NAME} PUBLIC - $ - $ - $) + $ + $ + $) target_include_directories(${PROJECT_NAME} PRIVATE include/${PROJECT_NAME}) # Gumbo @@ -181,63 +184,26 @@ target_link_libraries(${PROJECT_NAME} PUBLIC gumbo) # install and export install(TARGETS ${PROJECT_NAME} - EXPORT litehtmlTargets - RUNTIME DESTINATION bin COMPONENT libraries - ARCHIVE DESTINATION lib${LIB_SUFFIX} COMPONENT libraries - LIBRARY DESTINATION lib${LIB_SUFFIX} COMPONENT libraries - PUBLIC_HEADER DESTINATION include/litehtml + EXPORT litehtmlTargets + RUNTIME DESTINATION bin COMPONENT libraries + ARCHIVE DESTINATION lib${LIB_SUFFIX} COMPONENT libraries + LIBRARY DESTINATION lib${LIB_SUFFIX} COMPONENT libraries + PUBLIC_HEADER DESTINATION include/litehtml ) install(FILES cmake/litehtmlConfig.cmake DESTINATION lib${LIB_SUFFIX}/cmake/litehtml) install(EXPORT litehtmlTargets FILE litehtmlTargets.cmake DESTINATION lib${LIB_SUFFIX}/cmake/litehtml) # Tests -if (LITEHTML_BUILD_TESTING) - option(EXTERNAL_GTEST "Use external GoogleTest instead of fetching from GitHub" OFF) - - if (EXTERNAL_GTEST) - link_libraries("-Wl,--copy-dt-needed-entries") - else() - include(FetchContent) - FetchContent_Declare( - googletest - URL https://github.com/google/googletest/archive/609281088cfefc76f9d0ce82e1ff6c30cc3591e5.zip - ) - # For Windows: Prevent overriding the parent project's compiler/linker settings - set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) - FetchContent_GetProperties(googletest) - if(NOT googletest_POPULATED) - FetchContent_Populate(googletest) - add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR}) - endif() - endif() - - enable_testing() - - set(TEST_NAME ${PROJECT_NAME}_tests) - - add_executable( - ${TEST_NAME} - ${TEST_LITEHTML} - ) - - set_target_properties(${TEST_NAME} PROPERTIES - CXX_STANDARD 11 - C_STANDARD 99 - PUBLIC_HEADER "${HEADER_LITEHTML}" - ) - - target_include_directories( - ${TEST_NAME} - PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/containers - ) - - target_link_libraries( - ${TEST_NAME} - ${PROJECT_NAME} - gtest_main - ) - - include(GoogleTest) - gtest_discover_tests(${TEST_NAME}) +else () + include(ExternalProject) + ExternalProject_Add( + litehtml-tests + GIT_REPOSITORY https://github.com/litehtml/litehtml-tests.git + GIT_TAG 7511158e62cdac071de9071e060828e9e3c0bcc6 + SOURCE_DIR "${CMAKE_BINARY_DIR}/litehtml-tests-src" + BINARY_DIR "${CMAKE_BINARY_DIR}/litehtml-tests-build" + CMAKE_ARGS -DLITEHTML_PATH=${CMAKE_CURRENT_SOURCE_DIR} + INSTALL_COMMAND "" + ) endif() diff --git a/README.md b/README.md index ab897f517..47c0d9959 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ ## HTML Parser -**litehtml** uses the [gumbo-parser](https://github.com/google/gumbo-parser) to parse HTML. Gumbo is an implementation of the HTML5 parsing algorithm implemented as a pure C99 library with no outside dependencies. It's designed to serve as a building block for other tools and libraries such as linters, validators, templating languages, and refactoring and analysis tools. +**litehtml** uses the [gumbo-parser](https://codeberg.org/gumbo-parser/gumbo-parser) to parse HTML. Gumbo is an implementation of the HTML5 parsing algorithm implemented as a pure C99 library with no outside dependencies. It's designed to serve as a building block for other tools and libraries such as linters, validators, templating languages, and refactoring and analysis tools. ## Compatibility diff --git a/containers/cairo/cairo_borders.cpp b/containers/cairo/cairo_borders.cpp new file mode 100644 index 000000000..a567f5764 --- /dev/null +++ b/containers/cairo/cairo_borders.cpp @@ -0,0 +1,347 @@ +#include +#include "cairo_borders.h" +#include + +#ifndef M_PI +# define M_PI 3.14159265358979323846 +#endif + +void cairo::add_path_arc(cairo_t* cr, double x, double y, double rx, double ry, double a1, double a2, bool neg) +{ + if(rx > 0 && ry > 0) + { + cairo_save(cr); + + cairo_translate(cr, x, y); + cairo_scale(cr, 1, ry / rx); + cairo_translate(cr, -x, -y); + + if(neg) + { + cairo_arc_negative(cr, x, y, rx, a1, a2); + } else + { + cairo_arc(cr, x, y, rx, a1, a2); + } + + cairo_restore(cr); + } else + { + cairo_move_to(cr, x, y); + } +} + +/** + * Draw border at the left side. Use the only function to draw all border by using rotation transfer + * @param cr cairo context + * @param left left position of the border + * @param top top position of the border + * @param bottom bottom position of the border + * @param data border data + */ +void cairo::border::draw_border() +{ + cairo_save(cr); + + if(radius_top_x && radius_top_y) + { + double start_angle = M_PI; + double end_angle = start_angle + M_PI / 2.0 / ((double) top_border_width / (double) border_width + 1); + + add_path_arc(cr, + left + radius_top_x, + top + radius_top_y, + radius_top_x - border_width, + radius_top_y - border_width + (border_width - top_border_width), + start_angle, + end_angle, false); + + add_path_arc(cr, + left + radius_top_x, + top + radius_top_y, + radius_top_x, + radius_top_y, + end_angle, + start_angle, true); + } else + { + cairo_move_to(cr, left + border_width, top + top_border_width); + cairo_line_to(cr, left, top); + } + + if(radius_bottom_x && radius_bottom_y) + { + cairo_line_to(cr, left, bottom - radius_bottom_y); + + double end_angle = M_PI; + double start_angle = end_angle - M_PI / 2.0 / ((double) bottom_border_width / (double) border_width + 1); + + add_path_arc(cr, + left + radius_bottom_x, + bottom - radius_bottom_y, + radius_bottom_x, + radius_bottom_y, + end_angle, + start_angle, true); + + add_path_arc(cr, + left + radius_bottom_x, + bottom - radius_bottom_y, + radius_bottom_x - border_width, + radius_bottom_y - border_width + (border_width - bottom_border_width), + start_angle, + end_angle, false); + } else + { + cairo_line_to(cr, left, bottom); + cairo_line_to(cr, left + border_width, bottom - bottom_border_width); + } + cairo_clip(cr); + + switch (style) + { + case litehtml::border_style_dotted: + draw_dotted(); + break; + case litehtml::border_style_dashed: + draw_dashed(); + break; + case litehtml::border_style_double: + draw_double(); + break; + case litehtml::border_style_inset: + draw_inset_outset(true); + break; + case litehtml::border_style_outset: + draw_inset_outset(false); + break; + case litehtml::border_style_groove: + draw_groove_ridge(true); + break; + case litehtml::border_style_ridge: + draw_groove_ridge(false); + break; + default: + draw_solid(); + break; + } + + cairo_restore(cr); +} + +void cairo::border::draw_line(double line_offset, double top_line_offset, double bottom_line_offset) +{ + if(radius_top_x && radius_top_y) + { + double end_angle = M_PI; + double start_angle = end_angle + M_PI / 2.0 / ((double) top_border_width / (double) border_width + 1); + + add_path_arc(cr, + left + radius_top_x, + top + radius_top_y, + radius_top_x - line_offset, + radius_top_y - line_offset + (line_offset - top_line_offset), + start_angle, + end_angle, true); + } else + { + cairo_move_to(cr, left + line_offset, top); + } + + if(radius_bottom_x && radius_bottom_y) + { + cairo_line_to(cr, left + line_offset, bottom - radius_bottom_y); + + double start_angle = M_PI; + double end_angle = start_angle - M_PI / 2.0 / ((double) bottom_border_width / (double) border_width + 1); + + add_path_arc(cr, + left + radius_bottom_x, + bottom - radius_bottom_y, + radius_bottom_x - line_offset, + radius_bottom_y - line_offset + (line_offset - bottom_line_offset), + start_angle, + end_angle, true); + } else + { + cairo_line_to(cr, left + line_offset, bottom); + } +} + +void cairo::border::draw_inset_outset(bool is_inset) +{ + litehtml::web_color line_color; + litehtml::web_color light_color = color; + litehtml::web_color dark_color = color.darken(0.33); + if(color.red == 0 && color.green == 0 && color.blue == 0) + { + dark_color.red = dark_color.green = dark_color.blue = 0x4C; + light_color.red = light_color.green = light_color.blue = 0xB2; + } + + if (real_side == left_side || real_side == top_side) + { + line_color = is_inset ? dark_color : light_color; + } else + { + line_color = is_inset ? light_color : dark_color; + } + draw_line(border_width / 2.0, + top_border_width / 2.0, + bottom_border_width / 2.0); + cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT); + cairo_set_dash(cr, nullptr, 0, 0); + set_color(cr, line_color); + cairo_set_line_width(cr, border_width); + cairo_stroke(cr); +} + +void cairo::border::draw_double() +{ + if (border_width < 3) + { + draw_solid(); + } else + { + cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT); + cairo_set_dash(cr, nullptr, 0, 0); + set_color(cr, color); + + double line_width = border_width / 3.0; + cairo_set_line_width(cr, line_width); + // draw external line + draw_line(line_width / 2.0, + top_border_width / 6.0, + bottom_border_width / 6.0); + cairo_stroke(cr); + // draw internal line + draw_line(border_width - line_width / 2.0, + top_border_width - top_border_width / 6.0, + bottom_border_width - bottom_border_width / 6.0); + cairo_stroke(cr); + } +} + +void cairo::border::draw_dashed() +{ + int line_length = std::abs(bottom - top); + if(!line_length) return; + + draw_line(border_width / 2.0, + top_border_width / 2.0, + bottom_border_width / 2.0); + + int segment_length = border_width * 3; + int seg_nums = line_length / segment_length; + if(seg_nums < 2) + { + seg_nums = 2; + } if(seg_nums % 2 != 0) + { + seg_nums = seg_nums + 1; + } + seg_nums++; + double seg_len = (double) line_length / (double) seg_nums; + + double dashes[2]; + dashes[0] = seg_len; + dashes[1] = seg_len; + cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT); + cairo_set_dash(cr, dashes, 2, 0); + set_color(cr, color); + cairo_set_line_width(cr, border_width); + cairo_stroke(cr); +} + +void cairo::border::draw_solid() +{ + draw_line(border_width / 2.0, + top_border_width / 2.0, + bottom_border_width / 2.0); + cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT); + cairo_set_dash(cr, nullptr, 0, 0); + set_color(cr, color); + cairo_set_line_width(cr, border_width); + cairo_stroke(cr); +} + +void cairo::border::draw_dotted() +{ + // Zero length line + if(bottom == top) return; + + draw_line(border_width / 2.0, + top_border_width / 2.0, + bottom_border_width / 2.0); + + double line_length = std::abs(bottom - top); + + double dot_size = border_width; + int num_dots = (int) std::nearbyint(line_length / (dot_size * 2.0)); + if(num_dots < 2) + { + num_dots = 2; + } if(num_dots % 2 != 0) + { + num_dots = num_dots + 1; + } + num_dots++; + double space_len = ((double) line_length - (double) border_width) / (num_dots - 1.0); + + double dashes[2]; + dashes[0] = 0; + dashes[1] = space_len; + cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); + cairo_set_dash(cr, dashes, 2, -dot_size / 2.0); + + set_color(cr, color); + cairo_set_line_width(cr, border_width); + cairo_stroke(cr); +} + +void cairo::border::draw_groove_ridge(bool is_groove) +{ + if(border_width == 1) + { + draw_solid(); + } else + { + litehtml::web_color inner_line_color; + litehtml::web_color outer_line_color; + litehtml::web_color light_color = color; + litehtml::web_color dark_color = color.darken(0.33); + if (color.red == 0 && color.green == 0 && color.blue == 0) + { + dark_color.red = dark_color.green = dark_color.blue = 0x4C; + light_color.red = light_color.green = light_color.blue = 0xB2; + } + + if (real_side == left_side || real_side == top_side) + { + outer_line_color = is_groove ? dark_color : light_color; + inner_line_color = is_groove ? light_color : dark_color; + } else + { + outer_line_color = is_groove ? light_color : dark_color; + inner_line_color = is_groove ? dark_color : light_color; + } + + cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT); + cairo_set_dash(cr, nullptr, 0, 0); + + double line_width = border_width / 2.0; + cairo_set_line_width(cr, line_width); + // draw external line + draw_line(line_width / 2.0, + top_border_width / 4.0, + bottom_border_width / 4.0); + set_color(cr, outer_line_color); + cairo_stroke(cr); + // draw internal line + set_color(cr, inner_line_color); + draw_line(border_width - line_width / 2.0, + top_border_width - top_border_width / 4.0, + bottom_border_width - bottom_border_width / 4.0); + cairo_stroke(cr); + } +} diff --git a/containers/cairo/cairo_borders.h b/containers/cairo/cairo_borders.h new file mode 100644 index 000000000..43a212388 --- /dev/null +++ b/containers/cairo/cairo_borders.h @@ -0,0 +1,70 @@ +#ifndef LITEHTML_CAIRO_BORDERS_H +#define LITEHTML_CAIRO_BORDERS_H + +#include +#include + +namespace cairo +{ + extern void add_path_arc(cairo_t* cr, double x, double y, double rx, double ry, double a1, double a2, bool neg); + inline void set_color(cairo_t* cr, const litehtml::web_color& color) + { + cairo_set_source_rgba(cr, color.red / 255.0, color.green / 255.0, color.blue / 255.0, color.alpha / 255.0); + } + + class border + { + public: + enum real_side_t + { + left_side, + top_side, + right_side, + bottom_side + }; + + real_side_t real_side; /// real side of the border + litehtml::web_color color; + litehtml::border_style style; + + int border_width; + int top_border_width; + int bottom_border_width; + + int radius_top_x; + int radius_top_y; + int radius_bottom_x; + int radius_bottom_y; + + border(cairo_t* _cr, int _left, int _top, int _bottom) : + real_side(left_side), + color(), + style(litehtml::border_style_none), + border_width(0), + top_border_width(0), + bottom_border_width(0), + radius_top_x(0), + radius_top_y(0), + radius_bottom_x(0), + radius_bottom_y(0), + cr(_cr), left(_left), top(_top), bottom(_bottom) + {} + + void draw_border(); + + private: + cairo_t* cr; + int left; + int top; + int bottom; + void draw_line(double line_offset, double top_line_offset, double bottom_line_offset); + void draw_solid(); + void draw_dotted(); + void draw_dashed(); + void draw_double(); + void draw_inset_outset(bool is_inset); + void draw_groove_ridge(bool is_groove); + }; +} + +#endif //LITEHTML_CAIRO_BORDERS_H diff --git a/containers/cairo/cairo_container.cpp b/containers/cairo/cairo_container.cpp deleted file mode 100644 index c766d9854..000000000 --- a/containers/cairo/cairo_container.cpp +++ /dev/null @@ -1,988 +0,0 @@ -#include "cairo_container.h" -#define _USE_MATH_DEFINES -#include -#include "cairo_font.h" -#include - -cairo_container::cairo_container(void) -{ - m_temp_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 2, 2); - m_temp_cr = cairo_create(m_temp_surface); - m_font_link = NULL; - CoCreateInstance(CLSID_CMultiLanguage, NULL, CLSCTX_ALL, IID_IMLangFontLink2, (void**) &m_font_link); - InitializeCriticalSection(&m_img_sync); -} - -cairo_container::~cairo_container(void) -{ - clear_images(); - if(m_font_link) - { - m_font_link->Release(); - } - cairo_surface_destroy(m_temp_surface); - cairo_destroy(m_temp_cr); - DeleteCriticalSection(&m_img_sync); -} - -litehtml::uint_ptr cairo_container::create_font( const char* faceName, int size, int weight, litehtml::font_style italic, unsigned int decoration, litehtml::font_metrics* fm ) -{ - std::wstring fnt_name = L"sans-serif"; - - litehtml::string_vector fonts; - litehtml::split_string(faceName, fonts, ","); - if(!fonts.empty()) - { - litehtml::trim(fonts[0]); - wchar_t* f = cairo_font::utf8_to_wchar(fonts[0].c_str()); - fnt_name = f; - delete f; - if (fnt_name.front() == L'"' || fnt_name.front() == L'\'') - { - fnt_name.erase(0, 1); - } - if (fnt_name.back() == L'"' || fnt_name.back() == L'\'') - { - fnt_name.erase(fnt_name.length() - 1, 1); - } - } - - cairo_font* fnt = new cairo_font( m_font_link, - fnt_name.c_str(), - size, - weight, - (italic == litehtml::font_style_italic) ? TRUE : FALSE, - (decoration & litehtml::font_decoration_linethrough) ? TRUE : FALSE, - (decoration & litehtml::font_decoration_underline) ? TRUE : FALSE); - - cairo_save(m_temp_cr); - fnt->load_metrics(m_temp_cr); - - if(fm) - { - fm->ascent = fnt->metrics().ascent; - fm->descent = fnt->metrics().descent; - fm->height = fnt->metrics().height; - fm->x_height = fnt->metrics().x_height; - if(italic == litehtml::font_style_italic || decoration) - { - fm->draw_spaces = true; - } else - { - fm->draw_spaces = false; - } - } - - cairo_restore(m_temp_cr); - - return (litehtml::uint_ptr) fnt; -} - -void cairo_container::delete_font( litehtml::uint_ptr hFont ) -{ - cairo_font* fnt = (cairo_font*) hFont; - if(fnt) - { - delete fnt; - } -} - -int cairo_container::text_width( const char* text, litehtml::uint_ptr hFont ) -{ - cairo_font* fnt = (cairo_font*) hFont; - - cairo_save(m_temp_cr); - int ret = fnt->text_width(m_temp_cr, text); - cairo_restore(m_temp_cr); - return ret; -} - -void cairo_container::draw_text( litehtml::uint_ptr hdc, const char* text, litehtml::uint_ptr hFont, litehtml::web_color color, const litehtml::position& pos ) -{ - if(hFont) - { - cairo_font* fnt = (cairo_font*) hFont; - cairo_t* cr = (cairo_t*) hdc; - cairo_save(cr); - - apply_clip(cr); - - int x = pos.left(); - int y = pos.bottom() - fnt->metrics().descent; - - set_color(cr, color); - fnt->show_text(cr, x, y, text); - - cairo_restore(cr); - } -} - -int cairo_container::pt_to_px( int pt ) const -{ - HDC dc = GetDC(NULL); - int ret = MulDiv(pt, GetDeviceCaps(dc, LOGPIXELSY), 72); - ReleaseDC(NULL, dc); - return ret; -} - -int cairo_container::get_default_font_size() const -{ - return 16; -} - -void cairo_container::draw_list_marker( litehtml::uint_ptr hdc, const litehtml::list_marker& marker ) -{ - if(!marker.image.empty()) - { - std::wstring url; - make_url_utf8(marker.image.c_str(), marker.baseurl, url); - - lock_images_cache(); - images_map::iterator img_i = m_images.find(url.c_str()); - if(img_i != m_images.end()) - { - if(img_i->second) - { - draw_txdib((cairo_t*)hdc, img_i->second.get(), marker.pos.x, marker.pos.y, marker.pos.width, marker.pos.height); - unlock_images_cache(); - return; - } - } - unlock_images_cache(); - } - - switch(marker.marker_type) - { - case litehtml::list_style_type_circle: - { - draw_ellipse((cairo_t*) hdc, marker.pos.x, marker.pos.y, marker.pos.width, marker.pos.height, marker.color, 0.5); - } - break; - case litehtml::list_style_type_disc: - { - fill_ellipse((cairo_t*) hdc, marker.pos.x, marker.pos.y, marker.pos.width, marker.pos.height, marker.color); - } - break; - case litehtml::list_style_type_square: - if(hdc) - { - cairo_t* cr = (cairo_t*) hdc; - cairo_save(cr); - - cairo_new_path(cr); - cairo_rectangle(cr, marker.pos.x, marker.pos.y, marker.pos.width, marker.pos.height); - - set_color(cr, marker.color); - cairo_fill(cr); - cairo_restore(cr); - } - break; - } -} - -void cairo_container::load_image( const char* src, const char* baseurl, bool redraw_on_ready ) -{ - std::wstring url; - make_url_utf8(src, baseurl, url); - lock_images_cache(); - if(m_images.find(url.c_str()) == m_images.end()) - { - unlock_images_cache(); - image_ptr img = get_image(url.c_str(), redraw_on_ready); - lock_images_cache(); - m_images[url] = img; - unlock_images_cache(); - } else - { - unlock_images_cache(); - } - -} - -void cairo_container::get_image_size( const char* src, const char* baseurl, litehtml::size& sz ) -{ - std::wstring url; - make_url_utf8(src, baseurl, url); - - sz.width = 0; - sz.height = 0; - - lock_images_cache(); - images_map::iterator img = m_images.find(url.c_str()); - if(img != m_images.end()) - { - if(img->second) - { - sz.width = img->second->getWidth(); - sz.height = img->second->getHeight(); - } - } - unlock_images_cache(); -} - -void cairo_container::draw_image( litehtml::uint_ptr hdc, const char* src, const char* baseurl, const litehtml::position& pos ) -{ - cairo_t* cr = (cairo_t*) hdc; - cairo_save(cr); - apply_clip(cr); - - std::wstring url; - make_url_utf8(src, baseurl, url); - lock_images_cache(); - images_map::iterator img = m_images.find(url.c_str()); - if(img != m_images.end()) - { - if(img->second) - { - draw_txdib(cr, img->second.get(), pos.x, pos.y, pos.width, pos.height); - } - } - unlock_images_cache(); - cairo_restore(cr); -} - -void cairo_container::draw_background( litehtml::uint_ptr hdc, const std::vector& bgvec ) -{ - cairo_t* cr = (cairo_t*) hdc; - cairo_save(cr); - apply_clip(cr); - - const auto& bg = bgvec.back(); - - rounded_rectangle(cr, bg.border_box, bg.border_radius); - cairo_clip(cr); - - cairo_rectangle(cr, bg.clip_box.x, bg.clip_box.y, bg.clip_box.width, bg.clip_box.height); - cairo_clip(cr); - - if(bg.color.alpha) - { - set_color(cr, bg.color); - cairo_paint(cr); - } - - for (int i = (int)bgvec.size() - 1; i >= 0; i--) - { - const auto& bg = bgvec[i]; - - cairo_rectangle(cr, bg.clip_box.x, bg.clip_box.y, bg.clip_box.width, bg.clip_box.height); - cairo_clip(cr); - - std::wstring url; - make_url_utf8(bg.image.c_str(), bg.baseurl.c_str(), url); - - lock_images_cache(); - auto img_i = m_images.find(url); - if (img_i != m_images.end() && img_i->second) - { - image_ptr bgbmp = img_i->second; - - image_ptr new_img; - if (bg.image_size.width != bgbmp->getWidth() || bg.image_size.height != bgbmp->getHeight()) - { - new_img = image_ptr(new CTxDIB); - bgbmp->resample(bg.image_size.width, bg.image_size.height, new_img.get()); - bgbmp = new_img; - } - - - cairo_surface_t* img = cairo_image_surface_create_for_data((unsigned char*)bgbmp->getBits(), CAIRO_FORMAT_ARGB32, bgbmp->getWidth(), bgbmp->getHeight(), bgbmp->getWidth() * 4); - cairo_pattern_t* pattern = cairo_pattern_create_for_surface(img); - cairo_matrix_t flib_m; - cairo_matrix_init(&flib_m, 1, 0, 0, -1, 0, 0); - cairo_matrix_translate(&flib_m, -bg.position_x, -bg.position_y); - cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); - cairo_pattern_set_matrix(pattern, &flib_m); - - switch (bg.repeat) - { - case litehtml::background_repeat_no_repeat: - draw_txdib(cr, bgbmp.get(), bg.position_x, bg.position_y, bgbmp->getWidth(), bgbmp->getHeight()); - break; - - case litehtml::background_repeat_repeat_x: - cairo_set_source(cr, pattern); - cairo_rectangle(cr, bg.clip_box.left(), bg.position_y, bg.clip_box.width, bgbmp->getHeight()); - cairo_fill(cr); - break; - - case litehtml::background_repeat_repeat_y: - cairo_set_source(cr, pattern); - cairo_rectangle(cr, bg.position_x, bg.clip_box.top(), bgbmp->getWidth(), bg.clip_box.height); - cairo_fill(cr); - break; - - case litehtml::background_repeat_repeat: - cairo_set_source(cr, pattern); - cairo_rectangle(cr, bg.clip_box.left(), bg.clip_box.top(), bg.clip_box.width, bg.clip_box.height); - cairo_fill(cr); - break; - } - - cairo_pattern_destroy(pattern); - cairo_surface_destroy(img); - } - unlock_images_cache(); - } - - cairo_restore(cr); -} - -bool cairo_container::add_path_arc(cairo_t* cr, double x, double y, double rx, double ry, double a1, double a2, bool neg) -{ - if(rx > 0 && ry > 0) - { - cairo_save(cr); - - cairo_translate(cr, x, y); - cairo_scale(cr, 1, ry / rx); - cairo_translate(cr, -x, -y); - - if(neg) - { - cairo_arc_negative(cr, x, y, rx, a1, a2); - } else - { - cairo_arc(cr, x, y, rx, a1, a2); - } - - cairo_restore(cr); - return true; - } - return false; -} - -void cairo_container::draw_borders( litehtml::uint_ptr hdc, const litehtml::borders& borders, const litehtml::position& draw_pos, bool root ) -{ - cairo_t* cr = (cairo_t*) hdc; - cairo_save(cr); - apply_clip(cr); - - cairo_new_path(cr); - - int bdr_top = 0; - int bdr_bottom = 0; - int bdr_left = 0; - int bdr_right = 0; - - if(borders.top.width != 0 && borders.top.style > litehtml::border_style_hidden) - { - bdr_top = (int) borders.top.width; - } - if(borders.bottom.width != 0 && borders.bottom.style > litehtml::border_style_hidden) - { - bdr_bottom = (int) borders.bottom.width; - } - if(borders.left.width != 0 && borders.left.style > litehtml::border_style_hidden) - { - bdr_left = (int) borders.left.width; - } - if(borders.right.width != 0 && borders.right.style > litehtml::border_style_hidden) - { - bdr_right = (int) borders.right.width; - } - - // draw right border - if (bdr_right) - { - set_color(cr, borders.right.color); - - double r_top = (double) borders.radius.top_right_x; - double r_bottom = (double) borders.radius.bottom_right_x; - - if(r_top) - { - double end_angle = 2.0 * M_PI; - double start_angle = end_angle - M_PI / 2.0 / ((double) bdr_top / (double) bdr_right + 0.5); - - if (!add_path_arc(cr, - draw_pos.right() - r_top, - draw_pos.top() + r_top, - r_top - bdr_right, - r_top - bdr_right + (bdr_right - bdr_top), - end_angle, - start_angle, true)) - { - cairo_move_to(cr, draw_pos.right() - bdr_right, draw_pos.top() + bdr_top); - } - - if (!add_path_arc(cr, - draw_pos.right() - r_top, - draw_pos.top() + r_top, - r_top, - r_top, - start_angle, - end_angle, false)) - { - cairo_line_to(cr, draw_pos.right(), draw_pos.top()); - } - } else - { - cairo_move_to(cr, draw_pos.right() - bdr_right, draw_pos.top() + bdr_top); - cairo_line_to(cr, draw_pos.right(), draw_pos.top()); - } - - if(r_bottom) - { - cairo_line_to(cr, draw_pos.right(), draw_pos.bottom() - r_bottom); - - double start_angle = 0; - double end_angle = start_angle + M_PI / 2.0 / ((double) bdr_bottom / (double) bdr_right + 0.5); - - if (!add_path_arc(cr, - draw_pos.right() - r_bottom, - draw_pos.bottom() - r_bottom, - r_bottom, - r_bottom, - start_angle, - end_angle, false)) - { - cairo_line_to(cr, draw_pos.right(), draw_pos.bottom()); - } - - if (!add_path_arc(cr, - draw_pos.right() - r_bottom, - draw_pos.bottom() - r_bottom, - r_bottom - bdr_right, - r_bottom - bdr_right + (bdr_right - bdr_bottom), - end_angle, - start_angle, true)) - { - cairo_line_to(cr, draw_pos.right() - bdr_right, draw_pos.bottom() - bdr_bottom); - } - } else - { - cairo_line_to(cr, draw_pos.right(), draw_pos.bottom()); - cairo_line_to(cr, draw_pos.right() - bdr_right, draw_pos.bottom() - bdr_bottom); - } - - cairo_fill(cr); - } - - // draw bottom border - if(bdr_bottom) - { - set_color(cr, borders.bottom.color); - - double r_left = borders.radius.bottom_left_x; - double r_right = borders.radius.bottom_right_x; - - if(r_left) - { - double start_angle = M_PI / 2.0; - double end_angle = start_angle + M_PI / 2.0 / ((double) bdr_left / (double) bdr_bottom + 0.5); - - if (!add_path_arc(cr, - draw_pos.left() + r_left, - draw_pos.bottom() - r_left, - r_left - bdr_bottom + (bdr_bottom - bdr_left), - r_left - bdr_bottom, - start_angle, - end_angle, false)) - { - cairo_move_to(cr, draw_pos.left() + bdr_left, draw_pos.bottom() - bdr_bottom); - } - - if (!add_path_arc(cr, - draw_pos.left() + r_left, - draw_pos.bottom() - r_left, - r_left, - r_left, - end_angle, - start_angle, true)) - { - cairo_line_to(cr, draw_pos.left(), draw_pos.bottom()); - } - } else - { - cairo_move_to(cr, draw_pos.left(), draw_pos.bottom()); - cairo_line_to(cr, draw_pos.left() + bdr_left, draw_pos.bottom() - bdr_bottom); - } - - if(r_right) - { - cairo_line_to(cr, draw_pos.right() - r_right, draw_pos.bottom()); - - double end_angle = M_PI / 2.0; - double start_angle = end_angle - M_PI / 2.0 / ((double) bdr_right / (double) bdr_bottom + 0.5); - - if (!add_path_arc(cr, - draw_pos.right() - r_right, - draw_pos.bottom() - r_right, - r_right, - r_right, - end_angle, - start_angle, true)) - { - cairo_line_to(cr, draw_pos.right(), draw_pos.bottom()); - } - - if (!add_path_arc(cr, - draw_pos.right() - r_right, - draw_pos.bottom() - r_right, - r_right - bdr_bottom + (bdr_bottom - bdr_right), - r_right - bdr_bottom, - start_angle, - end_angle, false)) - { - cairo_line_to(cr, draw_pos.right() - bdr_right, draw_pos.bottom() - bdr_bottom); - } - } else - { - cairo_line_to(cr, draw_pos.right() - bdr_right, draw_pos.bottom() - bdr_bottom); - cairo_line_to(cr, draw_pos.right(), draw_pos.bottom()); - } - - cairo_fill(cr); - } - - // draw top border - if(bdr_top) - { - set_color(cr, borders.top.color); - - double r_left = borders.radius.top_left_x; - double r_right = borders.radius.top_right_x; - - if(r_left) - { - double end_angle = M_PI * 3.0 / 2.0; - double start_angle = end_angle - M_PI / 2.0 / ((double) bdr_left / (double) bdr_top + 0.5); - - if (!add_path_arc(cr, - draw_pos.left() + r_left, - draw_pos.top() + r_left, - r_left, - r_left, - end_angle, - start_angle, true)) - { - cairo_move_to(cr, draw_pos.left(), draw_pos.top()); - } - - if (!add_path_arc(cr, - draw_pos.left() + r_left, - draw_pos.top() + r_left, - r_left - bdr_top + (bdr_top - bdr_left), - r_left - bdr_top, - start_angle, - end_angle, false)) - { - cairo_line_to(cr, draw_pos.left() + bdr_left, draw_pos.top() + bdr_top); - } - } else - { - cairo_move_to(cr, draw_pos.left(), draw_pos.top()); - cairo_line_to(cr, draw_pos.left() + bdr_left, draw_pos.top() + bdr_top); - } - - if(r_right) - { - cairo_line_to(cr, draw_pos.right() - r_right, draw_pos.top() + bdr_top); - - double start_angle = M_PI * 3.0 / 2.0; - double end_angle = start_angle + M_PI / 2.0 / ((double) bdr_right / (double) bdr_top + 0.5); - - if (!add_path_arc(cr, - draw_pos.right() - r_right, - draw_pos.top() + r_right, - r_right - bdr_top + (bdr_top - bdr_right), - r_right - bdr_top, - start_angle, - end_angle, false)) - { - cairo_line_to(cr, draw_pos.right() - bdr_right, draw_pos.top() + bdr_top); - } - - if (!add_path_arc(cr, - draw_pos.right() - r_right, - draw_pos.top() + r_right, - r_right, - r_right, - end_angle, - start_angle, true)) - { - cairo_line_to(cr, draw_pos.right(), draw_pos.top()); - } - } else - { - cairo_line_to(cr, draw_pos.right() - bdr_right, draw_pos.top() + bdr_top); - cairo_line_to(cr, draw_pos.right(), draw_pos.top()); - } - - cairo_fill(cr); - } - - // draw left border - if (bdr_left) - { - set_color(cr, borders.left.color); - - double r_top = borders.radius.top_left_x; - double r_bottom = borders.radius.bottom_left_x; - - if(r_top) - { - double start_angle = M_PI; - double end_angle = start_angle + M_PI / 2.0 / ((double) bdr_top / (double) bdr_left + 0.5); - - if (!add_path_arc(cr, - draw_pos.left() + r_top, - draw_pos.top() + r_top, - r_top - bdr_left, - r_top - bdr_left + (bdr_left - bdr_top), - start_angle, - end_angle, false)) - { - cairo_move_to(cr, draw_pos.left() + bdr_left, draw_pos.top() + bdr_top); - } - - if (!add_path_arc(cr, - draw_pos.left() + r_top, - draw_pos.top() + r_top, - r_top, - r_top, - end_angle, - start_angle, true)) - { - cairo_line_to(cr, draw_pos.left(), draw_pos.top()); - } - } else - { - cairo_move_to(cr, draw_pos.left() + bdr_left, draw_pos.top() + bdr_top); - cairo_line_to(cr, draw_pos.left(), draw_pos.top()); - } - - if(r_bottom) - { - cairo_line_to(cr, draw_pos.left(), draw_pos.bottom() - r_bottom); - - double end_angle = M_PI; - double start_angle = end_angle - M_PI / 2.0 / ((double) bdr_bottom / (double) bdr_left + 0.5); - - if (!add_path_arc(cr, - draw_pos.left() + r_bottom, - draw_pos.bottom() - r_bottom, - r_bottom, - r_bottom, - end_angle, - start_angle, true)) - { - cairo_line_to(cr, draw_pos.left(), draw_pos.bottom()); - } - - if (!add_path_arc(cr, - draw_pos.left() + r_bottom, - draw_pos.bottom() - r_bottom, - r_bottom - bdr_left, - r_bottom - bdr_left + (bdr_left - bdr_bottom), - start_angle, - end_angle, false)) - { - cairo_line_to(cr, draw_pos.left() + bdr_left, draw_pos.bottom() - bdr_bottom); - } - } else - { - cairo_line_to(cr, draw_pos.left(), draw_pos.bottom()); - cairo_line_to(cr, draw_pos.left() + bdr_left, draw_pos.bottom() - bdr_bottom); - } - - cairo_fill(cr); - } - cairo_restore(cr); -} - -void cairo_container::set_clip(const litehtml::position& pos, const litehtml::border_radiuses& bdr_radius) -{ - m_clips.emplace_back(pos, bdr_radius); -} - -void cairo_container::del_clip() -{ - if(!m_clips.empty()) - { - m_clips.pop_back(); - } -} - -void cairo_container::apply_clip( cairo_t* cr ) -{ - for(const auto& clip_box : m_clips) - { - rounded_rectangle(cr, clip_box.box, clip_box.radius); - cairo_clip(cr); - } -} - -void cairo_container::draw_ellipse( cairo_t* cr, int x, int y, int width, int height, const litehtml::web_color& color, double line_width ) -{ - if(!cr) return; - cairo_save(cr); - - apply_clip(cr); - - cairo_new_path(cr); - - cairo_translate (cr, x + width / 2.0, y + height / 2.0); - cairo_scale (cr, width / 2.0, height / 2.0); - cairo_arc (cr, 0, 0, 1, 0, 2 * M_PI); - - set_color(cr, color); - cairo_set_line_width(cr, line_width); - cairo_stroke(cr); - - cairo_restore(cr); -} - -void cairo_container::fill_ellipse( cairo_t* cr, int x, int y, int width, int height, const litehtml::web_color& color ) -{ - if(!cr) return; - cairo_save(cr); - - apply_clip(cr); - - cairo_new_path(cr); - - cairo_translate (cr, x + width / 2.0, y + height / 2.0); - cairo_scale (cr, width / 2.0, height / 2.0); - cairo_arc (cr, 0, 0, 1, 0, 2 * M_PI); - - set_color(cr, color); - cairo_fill(cr); - - cairo_restore(cr); -} - -void cairo_container::clear_images() -{ - lock_images_cache(); - m_images.clear(); - unlock_images_cache(); -} - -const char* cairo_container::get_default_font_name() const -{ - return "Times New Roman"; -} - -void cairo_container::draw_txdib( cairo_t* cr, CTxDIB* bmp, int x, int y, int cx, int cy ) -{ - cairo_save(cr); - - cairo_matrix_t flib_m; - cairo_matrix_init(&flib_m, 1, 0, 0, -1, 0, 0); - - cairo_surface_t* img = NULL; - - CTxDIB rbmp; - - if(cx != bmp->getWidth() || cy != bmp->getHeight()) - { - bmp->resample(cx, cy, &rbmp); - img = cairo_image_surface_create_for_data((unsigned char*) rbmp.getBits(), CAIRO_FORMAT_ARGB32, rbmp.getWidth(), rbmp.getHeight(), rbmp.getWidth() * 4); - cairo_matrix_translate(&flib_m, 0, -rbmp.getHeight()); - cairo_matrix_translate(&flib_m, x, -y); - } else - { - img = cairo_image_surface_create_for_data((unsigned char*) bmp->getBits(), CAIRO_FORMAT_ARGB32, bmp->getWidth(), bmp->getHeight(), bmp->getWidth() * 4); - cairo_matrix_translate(&flib_m, 0, -bmp->getHeight()); - cairo_matrix_translate(&flib_m, x, -y); - } - - cairo_transform(cr, &flib_m); - cairo_set_source_surface(cr, img, 0, 0); - cairo_paint(cr); - - cairo_restore(cr); - cairo_surface_destroy(img); -} - -void cairo_container::rounded_rectangle(cairo_t* cr, const litehtml::position& pos, const litehtml::border_radiuses& radius) -{ - cairo_new_path(cr); - if(radius.top_left_x) - { - cairo_arc(cr, pos.left() + radius.top_left_x, pos.top() + radius.top_left_x, radius.top_left_x, M_PI, M_PI * 3.0 / 2.0); - } else - { - cairo_move_to(cr, pos.left(), pos.top()); - } - - cairo_line_to(cr, pos.right() - radius.top_right_x, pos.top()); - - if(radius.top_right_x) - { - cairo_arc(cr, pos.right() - radius.top_right_x, pos.top() + radius.top_right_x, radius.top_right_x, M_PI * 3.0 / 2.0, 2.0 * M_PI); - } - - cairo_line_to(cr, pos.right(), pos.bottom() - radius.bottom_right_x); - - if(radius.bottom_right_x) - { - cairo_arc(cr, pos.right() - radius.bottom_right_x, pos.bottom() - radius.bottom_right_x, radius.bottom_right_x, 0, M_PI / 2.0); - } - - cairo_line_to(cr, pos.left() - radius.bottom_left_x, pos.bottom()); - - if(radius.bottom_left_x) - { - cairo_arc(cr, pos.left() + radius.bottom_left_x, pos.bottom() - radius.bottom_left_x, radius.bottom_left_x, M_PI / 2.0, M_PI); - } -} - -void cairo_container::remove_image( std::wstring& url ) -{ - lock_images_cache(); - images_map::iterator i = m_images.find(url); - if(i != m_images.end()) - { - m_images.erase(i); - } - unlock_images_cache(); -} - -void cairo_container::add_image(std::wstring& url, image_ptr& img) -{ - lock_images_cache(); - images_map::iterator i = m_images.find(url); - if(i != m_images.end()) - { - if(img) - { - i->second = img; - } else - { - m_images.erase(i); - } - } - unlock_images_cache(); -} - -void cairo_container::lock_images_cache() -{ - EnterCriticalSection(&m_img_sync); -} - -void cairo_container::unlock_images_cache() -{ - LeaveCriticalSection(&m_img_sync); -} - -std::shared_ptr cairo_container::create_element(const char* tag_name, const litehtml::string_map& attributes, const std::shared_ptr& doc) -{ - return 0; -} - -void cairo_container::get_media_features(litehtml::media_features& media) const -{ - litehtml::position client; - get_client_rect(client); - HDC hdc = GetDC(NULL); - - media.type = litehtml::media_type_screen; - media.width = client.width; - media.height = client.height; - media.color = 8; - media.monochrome = 0; - media.color_index = 256; - media.resolution = GetDeviceCaps(hdc, LOGPIXELSX); - media.device_width = GetDeviceCaps(hdc, HORZRES); - media.device_height = GetDeviceCaps(hdc, VERTRES); - - ReleaseDC(NULL, hdc); -} - -void cairo_container::get_language(litehtml::string& language, litehtml::string & culture) const -{ - language = "en"; - culture = ""; -} - -void cairo_container::make_url_utf8( const char* url, const char* basepath, std::wstring& out ) -{ - wchar_t* urlW = cairo_font::utf8_to_wchar(url); - wchar_t* basepathW = cairo_font::utf8_to_wchar(basepath); - make_url(urlW, basepathW, out); - - if(urlW) delete urlW; - if(basepathW) delete basepathW; -} - -void cairo_container::transform_text( litehtml::string& text, litehtml::text_transform tt ) -{ - if(text.empty()) return; - - LPWSTR txt = cairo_font::utf8_to_wchar(text.c_str()); - switch(tt) - { - case litehtml::text_transform_capitalize: - CharUpperBuff(txt, 1); - break; - case litehtml::text_transform_uppercase: - CharUpperBuff(txt, lstrlen(txt)); - break; - case litehtml::text_transform_lowercase: - CharLowerBuff(txt, lstrlen(txt)); - break; - } - LPSTR txtA = cairo_font::wchar_to_utf8(txt); - text = txtA; - delete txtA; - delete txt; -} - -void cairo_container::link(const std::shared_ptr& doc, const litehtml::element::ptr& el) -{ -} - -litehtml::string cairo_container::resolve_color(const litehtml::string& color) const -{ - struct custom_color - { - const char* name; - int color_index; - }; - - static custom_color colors[] = { - { "ActiveBorder", COLOR_ACTIVEBORDER}, - { "ActiveCaption", COLOR_ACTIVECAPTION}, - { "AppWorkspace", COLOR_APPWORKSPACE }, - { "Background", COLOR_BACKGROUND }, - { "ButtonFace", COLOR_BTNFACE }, - { "ButtonHighlight", COLOR_BTNHIGHLIGHT }, - { "ButtonShadow", COLOR_BTNSHADOW }, - { "ButtonText", COLOR_BTNTEXT }, - { "CaptionText", COLOR_CAPTIONTEXT }, - { "GrayText", COLOR_GRAYTEXT }, - { "Highlight", COLOR_HIGHLIGHT }, - { "HighlightText", COLOR_HIGHLIGHTTEXT }, - { "InactiveBorder", COLOR_INACTIVEBORDER }, - { "InactiveCaption", COLOR_INACTIVECAPTION }, - { "InactiveCaptionText", COLOR_INACTIVECAPTIONTEXT }, - { "InfoBackground", COLOR_INFOBK }, - { "InfoText", COLOR_INFOTEXT }, - { "Menu", COLOR_MENU }, - { "MenuText", COLOR_MENUTEXT }, - { "Scrollbar", COLOR_SCROLLBAR }, - { "ThreeDDarkShadow", COLOR_3DDKSHADOW }, - { "ThreeDFace", COLOR_3DFACE }, - { "ThreeDHighlight", COLOR_3DHILIGHT }, - { "ThreeDLightShadow", COLOR_3DLIGHT }, - { "ThreeDShadow", COLOR_3DSHADOW }, - { "Window", COLOR_WINDOW }, - { "WindowFrame", COLOR_WINDOWFRAME }, - { "WindowText", COLOR_WINDOWTEXT } - }; - - for (auto& clr : colors) - { - if (!litehtml::t_strcasecmp(clr.name, color.c_str())) - { - char str_clr[20]; - DWORD rgb_color = GetSysColor(clr.color_index); - StringCchPrintfA(str_clr, 20, "#%02X%02X%02X", GetRValue(rgb_color), GetGValue(rgb_color), GetBValue(rgb_color)); - return std::move(litehtml::string(str_clr)); - } - } - return std::move(litehtml::string()); -} diff --git a/containers/cairo/cairo_container.h b/containers/cairo/cairo_container.h deleted file mode 100644 index 8b68f8de7..000000000 --- a/containers/cairo/cairo_container.h +++ /dev/null @@ -1,103 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include "cairo.h" -#include "cairo-win32.h" -#include -#include -#include - -struct cairo_clip_box -{ - typedef std::vector vector; - litehtml::position box; - litehtml::border_radiuses radius; - - cairo_clip_box(const litehtml::position& vBox, litehtml::border_radiuses vRad) - { - box = vBox; - radius = vRad; - } - - cairo_clip_box(const cairo_clip_box& val) - { - box = val.box; - radius = val.radius; - } - cairo_clip_box& operator=(const cairo_clip_box& val) - { - box = val.box; - radius = val.radius; - return *this; - } -}; - -class cairo_container : public litehtml::document_container -{ -public: - typedef std::shared_ptr image_ptr; - typedef std::map images_map; - -protected: - cairo_surface_t* m_temp_surface; - cairo_t* m_temp_cr; - images_map m_images; - cairo_clip_box::vector m_clips; - IMLangFontLink2* m_font_link; - CRITICAL_SECTION m_img_sync; -public: - cairo_container(void); - virtual ~cairo_container(void); - - virtual litehtml::uint_ptr create_font(const char* faceName, int size, int weight, litehtml::font_style italic, unsigned int decoration, litehtml::font_metrics* fm) override; - virtual void delete_font(litehtml::uint_ptr hFont) override; - virtual int text_width(const char* text, litehtml::uint_ptr hFont) override; - virtual void draw_text(litehtml::uint_ptr hdc, const char* text, litehtml::uint_ptr hFont, litehtml::web_color color, const litehtml::position& pos) override; - - virtual int pt_to_px(int pt) const override; - virtual int get_default_font_size() const override; - virtual const char* get_default_font_name() const override; - virtual void draw_list_marker(litehtml::uint_ptr hdc, const litehtml::list_marker& marker) override; - virtual void load_image(const char* src, const char* baseurl, bool redraw_on_ready) override; - virtual void get_image_size(const char* src, const char* baseurl, litehtml::size& sz) override; - virtual void draw_image(litehtml::uint_ptr hdc, const char* src, const char* baseurl, const litehtml::position& pos); - virtual void draw_background(litehtml::uint_ptr hdc, const std::vector& bg) override; - virtual void draw_borders(litehtml::uint_ptr hdc, const litehtml::borders& borders, const litehtml::position& draw_pos, bool root) override; - - virtual void transform_text(litehtml::string& text, litehtml::text_transform tt) override; - virtual void set_clip(const litehtml::position& pos, const litehtml::border_radiuses& bdr_radius) override; - virtual void del_clip() override; - virtual std::shared_ptr create_element(const char* tag_name, const litehtml::string_map& attributes, const std::shared_ptr& doc) override; - virtual void get_media_features(litehtml::media_features& media) const override; - virtual void get_language(litehtml::string& language, litehtml::string& culture) const override; - virtual void link(const std::shared_ptr& doc, const litehtml::element::ptr& el) override; - virtual litehtml::string resolve_color(const litehtml::string& color) const override; - - - virtual void make_url( LPCWSTR url, LPCWSTR basepath, std::wstring& out ) = 0; - virtual image_ptr get_image(LPCWSTR url, bool redraw_on_ready) = 0; - void clear_images(); - void add_image(std::wstring& url, image_ptr& img); - void remove_image(std::wstring& url); - void make_url_utf8( const char* url, const char* basepath, std::wstring& out ); - -protected: - virtual void draw_ellipse(cairo_t* cr, int x, int y, int width, int height, const litehtml::web_color& color, double line_width); - virtual void fill_ellipse(cairo_t* cr, int x, int y, int width, int height, const litehtml::web_color& color); - virtual void rounded_rectangle( cairo_t* cr, const litehtml::position &pos, const litehtml::border_radiuses &radius ); - - void set_color(cairo_t* cr, litehtml::web_color color) { cairo_set_source_rgba(cr, color.red / 255.0, color.green / 255.0, color.blue / 255.0, color.alpha / 255.0); } -private: - simpledib::dib* get_dib(litehtml::uint_ptr hdc) { return (simpledib::dib*) hdc; } - void apply_clip(cairo_t* cr); - bool add_path_arc(cairo_t* cr, double x, double y, double rx, double ry, double a1, double a2, bool neg); - - void draw_txdib(cairo_t* cr, CTxDIB* bmp, int x, int y, int cx, int cy); - void lock_images_cache(); - void unlock_images_cache(); -}; diff --git a/containers/cairo/cairo_images_cache.h b/containers/cairo/cairo_images_cache.h new file mode 100644 index 000000000..bbf2d2136 --- /dev/null +++ b/containers/cairo/cairo_images_cache.h @@ -0,0 +1,91 @@ +#ifndef LITEHTML_CAIRO_IMAGES_CACHE_H +#define LITEHTML_CAIRO_IMAGES_CACHE_H + +#include +#include +#include +#include + +class cairo_surface_wrapper +{ + cairo_surface_t* surface; +public: + cairo_surface_wrapper() : surface(nullptr) {} + cairo_surface_wrapper(const cairo_surface_wrapper& v) : surface(v.surface) + { + if(v.surface) + { + surface = cairo_surface_reference(v.surface); + } + } + explicit cairo_surface_wrapper(cairo_surface_t* v) : surface(v) {} + cairo_surface_wrapper(cairo_surface_wrapper&& v) noexcept + { + surface = v.surface; + v.surface = nullptr; + } + cairo_surface_wrapper& operator=(const cairo_surface_wrapper& v) + { + if(surface != v.surface) + { + if(surface) + { + cairo_surface_destroy(surface); + } + surface = cairo_surface_reference(v.surface); + } + return *this; + } + ~cairo_surface_wrapper() + { + if(surface) + { + cairo_surface_destroy(surface); + } + } + cairo_surface_t* get() { return cairo_surface_reference(surface); } +}; + +class cairo_images_cache +{ + std::mutex m_mutex; + std::map m_images; +public: + void add_image(const std::string& url, cairo_surface_t* image) + { + std::unique_lock lock(m_mutex); + m_images[url] = cairo_surface_wrapper(image); + } + + cairo_surface_t* get_image(const std::string& url) + { + std::unique_lock lock(m_mutex); + auto iter = m_images.find(url); + if(iter != m_images.end()) + { + return iter->second.get(); + } + return nullptr; + } + + bool reserve(const std::string& url) + { + std::unique_lock lock(m_mutex); + auto iter = m_images.find(url); + if (iter == m_images.end()) + { + m_images[url] = cairo_surface_wrapper(); + return true; + } + return false; + } + + bool exists(const std::string& url) + { + std::unique_lock lock(m_mutex); + auto iter = m_images.find(url); + return iter != m_images.end(); + } +}; + +#endif //LITEHTML_CAIRO_IMAGES_CACHE_H diff --git a/containers/cairo/conic_gradient.cpp b/containers/cairo/conic_gradient.cpp new file mode 100644 index 000000000..36b3430fe --- /dev/null +++ b/containers/cairo/conic_gradient.cpp @@ -0,0 +1,99 @@ +#include "conic_gradient.h" + +#define INTERPOLATE_COLOR(C1, C2, t) (((C2) - (C1)) * (t) + (C1)) + +static void sector_patch (cairo_pattern_t *pattern, double radius, + double angle_A, + double A_r, double A_g, double A_b, double A_a, + double angle_B, + double B_r, double B_g, double B_b, double B_a) +{ + double r_sin_A, r_cos_A; + double r_sin_B, r_cos_B; + double h; + + r_sin_A = radius * sin (angle_A); + r_cos_A = radius * cos (angle_A); + r_sin_B = radius * sin (angle_B); + r_cos_B = radius * cos (angle_B); + + h = 4.0/3.0 * tan ((angle_B - angle_A) / 4.0); + + double x0 = r_cos_A; + double y0 = r_sin_A; + double x1 = r_cos_A - h * r_sin_A; + double y1 = r_sin_A + h * r_cos_A; + double x2 = r_cos_B + h * r_sin_B; + double y2 = r_sin_B - h * r_cos_B; + double x3 = r_cos_B; + double y3 = r_sin_B; + + cairo_mesh_pattern_begin_patch (pattern); + + cairo_mesh_pattern_move_to (pattern, 0, 0); + cairo_mesh_pattern_line_to (pattern, x0, y0); + + cairo_mesh_pattern_curve_to (pattern, + x1, y1, + x2, y2, + x3, y3); + cairo_mesh_pattern_line_to (pattern, 0, 0); + + cairo_mesh_pattern_set_corner_color_rgba (pattern, 0, A_r, A_g, A_b, A_a); + cairo_mesh_pattern_set_corner_color_rgba (pattern, 1, A_r, A_g, A_b, A_a); + cairo_mesh_pattern_set_corner_color_rgba (pattern, 2, B_r, B_g, B_b, B_a); + cairo_mesh_pattern_set_corner_color_rgba (pattern, 3, B_r, B_g, B_b, B_a); + + cairo_mesh_pattern_end_patch (pattern); +} + +cairo_pattern_t* create_conic_gradient_pattern(double angle, double radius, const std::vector& color_points) +{ + if (color_points.empty()) + { + return nullptr; + } + + if(color_points.size() == 2) + { + std::vector points; + points.push_back(color_points[0]); + bg_color_point cp; + cp.offset = 0.5f; + cp.color.red = INTERPOLATE_COLOR(color_points[0].color.red, color_points[1].color.red, 0.5f); + cp.color.green = INTERPOLATE_COLOR(color_points[0].color.green, color_points[1].color.green, 0.5f); + cp.color.blue = INTERPOLATE_COLOR(color_points[0].color.blue, color_points[1].color.blue, 0.5f); + cp.color.alpha = INTERPOLATE_COLOR(color_points[0].color.alpha, color_points[1].color.alpha, 0.5f); + points.push_back(cp); + points.push_back(color_points[1]); + return create_conic_gradient_pattern(angle, radius, points); + } + + const double two_pi = 2.0 * M_PI; + + cairo_pattern_t* pattern = cairo_pattern_create_mesh(); + + for(size_t i = 0; i < color_points.size() - 1; ++i) + { + const bg_color_point& cp_A = color_points[i]; + const bg_color_point& cp_B = color_points[i + 1]; + double angle_A = cp_A.offset * two_pi + angle; + double angle_B = color_points[i + 1].offset * two_pi + angle; + + double A_r = cp_A.color.red / 255.0; + double A_g = cp_A.color.green / 255.0; + double A_b = cp_A.color.blue / 255.0; + double A_a = cp_A.color.alpha / 255.0; + + double B_r = cp_B.color.red / 255.0; + double B_g = cp_B.color.green / 255.0; + double B_b = cp_B.color.blue / 255.0; + double B_a = cp_B.color.alpha / 255.0; + + sector_patch(pattern, radius, + angle_A, A_r, A_g, A_b, A_a, + angle_B, B_r, B_g, B_b, B_a); + } + + return pattern; +} diff --git a/containers/cairo/conic_gradient.h b/containers/cairo/conic_gradient.h new file mode 100644 index 000000000..e83779c34 --- /dev/null +++ b/containers/cairo/conic_gradient.h @@ -0,0 +1,11 @@ +#ifndef LITEHTML_CONIC_GRADIENT_H +#define LITEHTML_CONIC_GRADIENT_H + +#include +#include + +using bg_color_point = litehtml::background_layer::color_point; + +cairo_pattern_t* create_conic_gradient_pattern(double angle, double radius, const std::vector& color_points); + +#endif //LITEHTML_CONIC_GRADIENT_H diff --git a/containers/cairo/container_cairo.cpp b/containers/cairo/container_cairo.cpp new file mode 100644 index 000000000..60dc3969b --- /dev/null +++ b/containers/cairo/container_cairo.cpp @@ -0,0 +1,772 @@ +#include "container_cairo.h" +#include "cairo_borders.h" +#include "conic_gradient.h" +#include + +#ifndef M_PI +# define M_PI 3.14159265358979323846 +#endif + + +int container_cairo::pt_to_px(int pt ) const +{ + double dpi = get_screen_dpi(); + + return (int) ((double) pt * dpi / 72.0); +} + +int container_cairo::get_default_font_size() const +{ + return pt_to_px(12); +} + +void container_cairo::draw_list_marker(litehtml::uint_ptr hdc, const litehtml::list_marker& marker ) +{ + if(!marker.image.empty()) + { + litehtml::string url; + make_url(marker.image.c_str(), marker.baseurl, url); + + auto img = get_image(url); + if(img) + { + draw_pixbuf((cairo_t*) hdc, img, marker.pos.x, marker.pos.y, cairo_image_surface_get_width(img), + cairo_image_surface_get_height(img)); + cairo_surface_destroy(img); + } + } else + { + switch(marker.marker_type) + { + case litehtml::list_style_type_circle: + { + draw_ellipse((cairo_t*) hdc, marker.pos.x, marker.pos.y, marker.pos.width, marker.pos.height, marker.color, 1); + } + break; + case litehtml::list_style_type_disc: + { + fill_ellipse((cairo_t*) hdc, marker.pos.x, marker.pos.y, marker.pos.width, marker.pos.height, marker.color); + } + break; + case litehtml::list_style_type_square: + if(hdc) + { + auto* cr = (cairo_t*) hdc; + cairo_save(cr); + + cairo_new_path(cr); + cairo_rectangle(cr, marker.pos.x, marker.pos.y, marker.pos.width, marker.pos.height); + + set_color(cr, marker.color); + cairo_fill(cr); + cairo_restore(cr); + } + break; + default: + /*do nothing*/ + break; + } + } +} + +void container_cairo::get_image_size(const char* src, const char* baseurl, litehtml::size& sz ) +{ + litehtml::string url; + make_url(src, baseurl, url); + + auto img = get_image(url); + if(img) + { + sz.width = cairo_image_surface_get_width(img); + sz.height = cairo_image_surface_get_height(img); + cairo_surface_destroy(img); + } else + { + sz.width = 0; + sz.height = 0; + } +} + +void container_cairo::clip_background_layer(cairo_t* cr, const litehtml::background_layer& layer) +{ + rounded_rectangle(cr, layer.border_box, layer.border_radius); + cairo_clip(cr); + + cairo_rectangle(cr, layer.clip_box.x, layer.clip_box.y, layer.clip_box.width, layer.clip_box.height); + cairo_clip(cr); +} + +void container_cairo::draw_image(litehtml::uint_ptr hdc, const litehtml::background_layer& layer, const std::string& url, const std::string& base_url) +{ + if(url.empty() || (!layer.clip_box.width && !layer.clip_box.height) ) + { + return; + } + + auto* cr = (cairo_t*) hdc; + cairo_save(cr); + apply_clip(cr); + + clip_background_layer(cr, layer); + + std::string img_url; + make_url(url.c_str(), base_url.c_str(), img_url); + + auto bgbmp = get_image(img_url); + if (bgbmp) + { + if (layer.origin_box.width != cairo_image_surface_get_width(bgbmp) || + layer.origin_box.height != cairo_image_surface_get_height(bgbmp)) + { + auto new_img = scale_surface(bgbmp, layer.origin_box.width, layer.origin_box.height); + cairo_surface_destroy(bgbmp); + bgbmp = new_img; + } + + cairo_pattern_t *pattern = cairo_pattern_create_for_surface(bgbmp); + cairo_matrix_t flib_m; + cairo_matrix_init_identity(&flib_m); + cairo_matrix_translate(&flib_m, -layer.origin_box.x, -layer.origin_box.y); + cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); + cairo_pattern_set_matrix(pattern, &flib_m); + + switch (layer.repeat) + { + case litehtml::background_repeat_no_repeat: + draw_pixbuf(cr, bgbmp, layer.origin_box.x, layer.origin_box.y, cairo_image_surface_get_width(bgbmp), + cairo_image_surface_get_height(bgbmp)); + break; + + case litehtml::background_repeat_repeat_x: + cairo_set_source(cr, pattern); + cairo_rectangle(cr, layer.clip_box.left(), layer.origin_box.y, layer.clip_box.width, + cairo_image_surface_get_height(bgbmp)); + cairo_fill(cr); + break; + + case litehtml::background_repeat_repeat_y: + cairo_set_source(cr, pattern); + cairo_rectangle(cr, layer.origin_box.x, layer.clip_box.top(), cairo_image_surface_get_width(bgbmp), + layer.clip_box.height); + cairo_fill(cr); + break; + + case litehtml::background_repeat_repeat: + cairo_set_source(cr, pattern); + cairo_rectangle(cr, layer.clip_box.left(), layer.clip_box.top(), layer.clip_box.width, + layer.clip_box.height); + cairo_fill(cr); + break; + } + + cairo_pattern_destroy(pattern); + cairo_surface_destroy(bgbmp); + } + cairo_restore(cr); +} + +void container_cairo::draw_solid_fill(litehtml::uint_ptr hdc, const litehtml::background_layer& layer, const litehtml::web_color& color) +{ + if(color == litehtml::web_color::transparent) + { + return; + } + + auto* cr = (cairo_t*) hdc; + cairo_save(cr); + apply_clip(cr); + + clip_background_layer(cr, layer); + + set_color(cr, color); + cairo_paint(cr); + + cairo_restore(cr); +} + +/** + * @brief Draw pattern using layer.repeat property. + * + * Pattern must be defined relatively to the (layer.origin_box.x, layer.origin_box.y) point. + * Function calculates rectangles for repeat-x/repeat-y properties and transform pattern to the correct position. + * Then call draw callback to draw single pattern. + * + * @param cr - cairo context + * @param pattern - cairo pattern + * @param layer - background layer + * @param draw - pattern draw function + */ +static void draw_pattern(cairo_t* cr, cairo_pattern_t* pattern, + const litehtml::background_layer& layer, + const std::function& draw) +{ + int start_x = layer.origin_box.x; + int num_x = 1; + int start_y = layer.origin_box.y; + int num_y = 1; + if(layer.repeat == litehtml::background_repeat_repeat_x || layer.repeat == litehtml::background_repeat_repeat) + { + if(layer.origin_box.left() > layer.clip_box.left()) + { + int num_left = (layer.origin_box.left() - layer.clip_box.left()) / layer.origin_box.width; + if(layer.origin_box.left() - num_left * layer.origin_box.width > layer.clip_box.left()) + { + num_left++; + } + start_x = layer.origin_box.left() - num_left * layer.origin_box.width; + num_x += num_left; + } + if(layer.origin_box.right() < layer.clip_box.right()) + { + int num_right = (layer.clip_box.right() - layer.origin_box.right()) / layer.origin_box.width; + if(layer.origin_box.left() + num_right * layer.origin_box.width < layer.clip_box.right()) + { + num_right++; + } + num_x += num_right; + } + } + if(layer.repeat == litehtml::background_repeat_repeat_y || layer.repeat == litehtml::background_repeat_repeat) + { + if(layer.origin_box.top() > layer.clip_box.top()) + { + int num_top = (layer.origin_box.top() - layer.clip_box.top()) / layer.origin_box.height; + if(layer.origin_box.top() - num_top * layer.origin_box.height > layer.clip_box.top()) + { + num_top++; + } + start_y = layer.origin_box.top() - num_top * layer.origin_box.height; + num_y += num_top; + } + if(layer.origin_box.bottom() < layer.clip_box.bottom()) + { + int num_bottom = (layer.clip_box.bottom() - layer.origin_box.bottom()) / layer.origin_box.height; + if(layer.origin_box.bottom() + num_bottom * layer.origin_box.height < layer.clip_box.bottom()) + { + num_bottom++; + } + num_y += num_bottom; + } + } + + for(int i_x = 0; i_x < num_x; i_x++) + { + for(int i_y= 0; i_y < num_y; i_y++) + { + cairo_matrix_t flib_m; + cairo_matrix_init_translate(&flib_m, -(start_x + i_x * layer.origin_box.width), -(start_y + i_y * layer.origin_box.height)); + cairo_pattern_set_matrix(pattern, &flib_m); + draw(cr, pattern, + start_x + i_x * layer.origin_box.width, + start_y + i_y * layer.origin_box.height, + layer.origin_box.width, + layer.origin_box.height); + } + } +} + +void container_cairo::draw_linear_gradient(litehtml::uint_ptr hdc, const litehtml::background_layer& layer, const litehtml::background_layer::linear_gradient& gradient) +{ + auto* cr = (cairo_t*) hdc; + cairo_save(cr); + apply_clip(cr); + + clip_background_layer(cr, layer); + + // Translate pattern to the (layer.origin_box.x, layer.origin_box.y) point + cairo_pattern_t* pattern = cairo_pattern_create_linear(gradient.start.x - (float) layer.origin_box.x, + gradient.start.y - (float) layer.origin_box.y, + gradient.end.x - (float) layer.origin_box.x, + gradient.end.y - (float) layer.origin_box.y); + + for(const auto& color_stop : gradient.color_points) + { + cairo_pattern_add_color_stop_rgba(pattern, + color_stop.offset, + color_stop.color.red / 255.0, + color_stop.color.green / 255.0, + color_stop.color.blue / 255.0, + color_stop.color.alpha / 255.0); + } + + draw_pattern(cr, pattern, layer, [](cairo_t* cr, cairo_pattern_t* pattern, int x, int y, int width, int height) + { + cairo_set_source(cr, pattern); + cairo_rectangle(cr, x, y, width, height); + cairo_fill(cr); + } + ); + + cairo_pattern_destroy(pattern); + cairo_restore(cr); +} + +void container_cairo::make_url(const char* url, const char* /*basepath*/, litehtml::string& out) +{ + out = url; +} + +void container_cairo::add_path_arc(cairo_t* cr, double x, double y, double rx, double ry, double a1, double a2, bool neg) +{ + if(rx > 0 && ry > 0) + { + + cairo_save(cr); + + cairo_translate(cr, x, y); + cairo_scale(cr, 1, ry / rx); + cairo_translate(cr, -x, -y); + + if(neg) + { + cairo_arc_negative(cr, x, y, rx, a1, a2); + } else + { + cairo_arc(cr, x, y, rx, a1, a2); + } + + cairo_restore(cr); + } else + { + cairo_move_to(cr, x, y); + } +} + +void container_cairo::draw_borders(litehtml::uint_ptr hdc, const litehtml::borders& borders, const litehtml::position& draw_pos, bool /*root*/) +{ + auto* cr = (cairo_t*) hdc; + cairo_save(cr); + apply_clip(cr); + + cairo_new_path(cr); + + int bdr_top = 0; + int bdr_bottom = 0; + int bdr_left = 0; + int bdr_right = 0; + + if(borders.top.width != 0 && borders.top.style > litehtml::border_style_hidden) + { + bdr_top = (int) borders.top.width; + } + if(borders.bottom.width != 0 && borders.bottom.style > litehtml::border_style_hidden) + { + bdr_bottom = (int) borders.bottom.width; + } + if(borders.left.width != 0 && borders.left.style > litehtml::border_style_hidden) + { + bdr_left = (int) borders.left.width; + } + if(borders.right.width != 0 && borders.right.style > litehtml::border_style_hidden) + { + bdr_right = (int) borders.right.width; + } + + // draw right border + if(bdr_right) + { + cairo_matrix_t save_matrix; + cairo_get_matrix(cr, &save_matrix); + cairo_translate(cr, draw_pos.left(), draw_pos.top()); + cairo_rotate(cr, M_PI); + cairo_translate(cr, -draw_pos.left(), -draw_pos.top()); + + cairo::border border(cr, draw_pos.left() - draw_pos.width, draw_pos.top() - draw_pos.height, draw_pos.top()); + border.real_side = cairo::border::right_side; + border.color = borders.right.color; + border.style = borders.right.style; + border.border_width = bdr_right; + border.top_border_width = bdr_bottom; + border.bottom_border_width = bdr_top; + border.radius_top_x = borders.radius.bottom_right_x; + border.radius_top_y = borders.radius.bottom_right_y; + border.radius_bottom_x = borders.radius.top_right_x; + border.radius_bottom_y = borders.radius.top_right_y; + border.draw_border(); + + cairo_set_matrix(cr, &save_matrix); + } + + // draw bottom border + if(bdr_bottom) + { + cairo_matrix_t save_matrix; + cairo_get_matrix(cr, &save_matrix); + cairo_translate(cr, draw_pos.left(), draw_pos.top()); + cairo_rotate(cr, - M_PI / 2.0); + cairo_translate(cr, -draw_pos.left(), -draw_pos.top()); + + cairo::border border(cr, draw_pos.left() - draw_pos.height, draw_pos.top(), draw_pos.top() + draw_pos.width); + border.real_side = cairo::border::bottom_side; + border.color = borders.bottom.color; + border.style = borders.bottom.style; + border.border_width = bdr_bottom; + border.top_border_width = bdr_left; + border.bottom_border_width = bdr_right; + border.radius_top_x = borders.radius.bottom_left_x; + border.radius_top_y = borders.radius.bottom_left_y; + border.radius_bottom_x = borders.radius.bottom_right_x; + border.radius_bottom_y = borders.radius.bottom_right_y; + border.draw_border(); + + cairo_set_matrix(cr, &save_matrix); + } + + // draw top border + if(bdr_top) + { + cairo_matrix_t save_matrix; + cairo_get_matrix(cr, &save_matrix); + cairo_translate(cr, draw_pos.left(), draw_pos.top()); + cairo_rotate(cr, M_PI / 2.0); + cairo_translate(cr, -draw_pos.left(), -draw_pos.top()); + + cairo::border border(cr, draw_pos.left(), draw_pos.top() - draw_pos.width, draw_pos.top()); + border.real_side = cairo::border::top_side; + border.color = borders.top.color; + border.style = borders.top.style; + border.border_width = bdr_top; + border.top_border_width = bdr_right; + border.bottom_border_width = bdr_left; + border.radius_top_x = borders.radius.top_right_x; + border.radius_top_y = borders.radius.top_right_y; + border.radius_bottom_x = borders.radius.top_left_x; + border.radius_bottom_y = borders.radius.top_left_y; + border.draw_border(); + + cairo_set_matrix(cr, &save_matrix); + } + + // draw left border + if(bdr_left) + { + cairo::border border(cr, draw_pos.left(), draw_pos.top(), draw_pos.bottom()); + border.real_side = cairo::border::left_side; + border.color = borders.left.color; + border.style = borders.left.style; + border.border_width = bdr_left; + border.top_border_width = bdr_top; + border.bottom_border_width = bdr_bottom; + border.radius_top_x = borders.radius.top_left_x; + border.radius_top_y = borders.radius.top_left_y; + border.radius_bottom_x = borders.radius.bottom_left_x; + border.radius_bottom_y = borders.radius.bottom_left_y; + border.draw_border(); + } + cairo_restore(cr); +} + +void container_cairo::transform_text(litehtml::string& /*text*/, litehtml::text_transform /*tt*/) +{ + +} + +void container_cairo::set_clip(const litehtml::position& pos, const litehtml::border_radiuses& bdr_radius ) +{ + m_clips.emplace_back(pos, bdr_radius); +} + +void container_cairo::del_clip() +{ + if(!m_clips.empty()) + { + m_clips.pop_back(); + } +} + +void container_cairo::apply_clip(cairo_t* cr ) +{ + for(const auto& clip_box : m_clips) + { + rounded_rectangle(cr, clip_box.box, clip_box.radius); + cairo_clip(cr); + } +} + +void container_cairo::draw_ellipse(cairo_t* cr, int x, int y, int width, int height, const litehtml::web_color& color, int line_width ) +{ + if(!cr || !width || !height) return; + cairo_save(cr); + + apply_clip(cr); + + cairo_new_path(cr); + + cairo_translate (cr, x + width / 2.0, y + height / 2.0); + cairo_scale (cr, width / 2.0, height / 2.0); + cairo_arc (cr, 0, 0, 1, 0, 2 * M_PI); + + set_color(cr, color); + cairo_set_line_width(cr, line_width); + cairo_stroke(cr); + + cairo_restore(cr); +} + +void container_cairo::fill_ellipse(cairo_t* cr, int x, int y, int width, int height, const litehtml::web_color& color ) +{ + if(!cr || !width || !height) return; + cairo_save(cr); + + apply_clip(cr); + + cairo_new_path(cr); + + cairo_translate (cr, x + width / 2.0, y + height / 2.0); + cairo_scale (cr, width / 2.0, height / 2.0); + cairo_arc (cr, 0, 0, 1, 0, 2 * M_PI); + + set_color(cr, color); + cairo_fill(cr); + + cairo_restore(cr); +} + +void container_cairo::clear_images() +{ +/* for(images_map::iterator i = m_images.begin(); i != m_images.end(); i++) + { + if(i->second) + { + delete i->second; + } + } + m_images.clear(); +*/ +} + +const char* container_cairo::get_default_font_name() const +{ + return "Times New Roman"; +} + +std::shared_ptr container_cairo::create_element(const char */*tag_name*/, + const litehtml::string_map &/*attributes*/, + const std::shared_ptr &/*doc*/) +{ + return nullptr; +} + +void container_cairo::rounded_rectangle(cairo_t* cr, const litehtml::position &pos, const litehtml::border_radiuses &radius ) +{ + cairo_new_path(cr); + if(radius.top_left_x && radius.top_left_y) + { + add_path_arc(cr, + pos.left() + radius.top_left_x, + pos.top() + radius.top_left_y, + radius.top_left_x, + radius.top_left_y, + M_PI, + M_PI * 3.0 / 2.0, false); + } else + { + cairo_move_to(cr, pos.left(), pos.top()); + } + + cairo_line_to(cr, pos.right() - radius.top_right_x, pos.top()); + + if(radius.top_right_x && radius.top_right_y) + { + add_path_arc(cr, + pos.right() - radius.top_right_x, + pos.top() + radius.top_right_y, + radius.top_right_x, + radius.top_right_y, + M_PI * 3.0 / 2.0, + 2.0 * M_PI, false); + } + + cairo_line_to(cr, pos.right(), pos.bottom() - radius.bottom_right_x); + + if(radius.bottom_right_x && radius.bottom_right_y) + { + add_path_arc(cr, + pos.right() - radius.bottom_right_x, + pos.bottom() - radius.bottom_right_y, + radius.bottom_right_x, + radius.bottom_right_y, + 0, + M_PI / 2.0, false); + } + + cairo_line_to(cr, pos.left() - radius.bottom_left_x, pos.bottom()); + + if(radius.bottom_left_x && radius.bottom_left_y) + { + add_path_arc(cr, + pos.left() + radius.bottom_left_x, + pos.bottom() - radius.bottom_left_y, + radius.bottom_left_x, + radius.bottom_left_y, + M_PI / 2.0, + M_PI, false); + } +} + +cairo_surface_t* container_cairo::scale_surface(cairo_surface_t* surface, int width, int height) +{ + int s_width = cairo_image_surface_get_width(surface); + int s_height = cairo_image_surface_get_height(surface); + cairo_surface_t *result = cairo_surface_create_similar(surface, cairo_surface_get_content(surface), width, height); + cairo_pattern_t *pattern = cairo_pattern_create_for_surface(surface); + cairo_t *cr = cairo_create(result); + cairo_pattern_set_filter(pattern, CAIRO_FILTER_BILINEAR); + cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD); + cairo_scale(cr, (double) width / (double) s_width, (double) height / (double) s_height); + cairo_set_source(cr, pattern); + cairo_rectangle(cr, 0, 0, s_width, s_height); + cairo_fill(cr); + cairo_destroy(cr); + cairo_pattern_destroy(pattern); + return result; +} + +void container_cairo::draw_pixbuf(cairo_t* cr, cairo_surface_t* bmp, int x, int y, int cx, int cy) +{ + cairo_save(cr); + + { + cairo_matrix_t flip_m; + cairo_matrix_init(&flip_m, 1, 0, 0, -1, 0, 0); + + if(cx != cairo_image_surface_get_width(bmp) || cy != cairo_image_surface_get_height(bmp)) + { + auto bmp_scaled = scale_surface(bmp, cx, cy); + cairo_set_source_surface(cr, bmp_scaled, x, y); + cairo_paint(cr); + cairo_surface_destroy(bmp_scaled); + } else + { + cairo_set_source_surface(cr, bmp, x, y); + cairo_paint(cr); + } + } + + cairo_restore(cr); +} + +void container_cairo::get_media_features(litehtml::media_features& media) const +{ + litehtml::position viewport; + get_viewport(viewport); + media.type = litehtml::media_type_screen; + media.width = viewport.width; + media.height = viewport.height; + media.device_width = get_screen_width(); + media.device_height = get_screen_height(); + media.color = 8; + media.monochrome = 0; + media.color_index = 256; + media.resolution = 96; +} + +void container_cairo::get_language(litehtml::string& language, litehtml::string& culture) const +{ + language = "en"; + culture = ""; +} + +void container_cairo::link(const std::shared_ptr &/*ptr*/, const litehtml::element::ptr& /*el*/) +{ + +} + +void container_cairo::draw_radial_gradient(litehtml::uint_ptr hdc, const litehtml::background_layer &layer, + const litehtml::background_layer::radial_gradient &gradient) +{ + auto* cr = (cairo_t*) hdc; + cairo_save(cr); + apply_clip(cr); + + clip_background_layer(cr, layer); + + // Translate pattern to the (layer.origin_box.x, layer.origin_box.y) point + litehtml::pointF position = gradient.position; + position.x -= (float) layer.origin_box.x; + position.y -= (float) layer.origin_box.y; + + cairo_pattern_t* pattern = cairo_pattern_create_radial(position.x, + position.y, + 0, + position.x, + position.y, + gradient.radius.x); + + for(const auto& color_stop : gradient.color_points) + { + cairo_pattern_add_color_stop_rgba(pattern, + color_stop.offset, + color_stop.color.red / 255.0, + color_stop.color.green / 255.0, + color_stop.color.blue / 255.0, + color_stop.color.alpha / 255.0); + } + + draw_pattern(cr, pattern, layer, [&gradient, &position](cairo_t* cr, cairo_pattern_t* pattern, int x, int y, int w, int h) + { + cairo_matrix_t save_matrix; + cairo_get_matrix(cr, &save_matrix); + + auto top = (float) y; + auto height = (float) h; + if(gradient.radius.x != gradient.radius.y) + { + litehtml::pointF pos = position; + pos.x += (float) x; + pos.y += (float) y; + // Scale height and top of the origin box + float aspect_ratio = gradient.radius.x / gradient.radius.y; + height *= aspect_ratio; + auto center_y = (pos.y - (float ) y) * aspect_ratio; + top = pos.y - center_y; + + cairo_translate(cr, pos.x, pos.y); + cairo_scale(cr, 1, gradient.radius.y / gradient.radius.x); + cairo_translate(cr, -pos.x, -pos.y); + } + + cairo_set_source(cr, pattern); + cairo_rectangle(cr, x, top, w, height); + cairo_fill(cr); + + cairo_set_matrix(cr, &save_matrix); + } + ); + + cairo_pattern_destroy(pattern); + cairo_restore(cr); +} + +void container_cairo::draw_conic_gradient(litehtml::uint_ptr hdc, const litehtml::background_layer &layer, + const litehtml::background_layer::conic_gradient &gradient) +{ + auto* cr = (cairo_t*) hdc; + cairo_save(cr); + apply_clip(cr); + + clip_background_layer(cr, layer); + + cairo_pattern_t* pattern = create_conic_gradient_pattern(gradient.angle * M_PI / 180.0 - M_PI / 2.0, gradient.radius, gradient.color_points); + if(!pattern) return; + + // Translate a pattern to the (layer.origin_box.x, layer.origin_box.y) point + litehtml::pointF position = gradient.position; + position.x -= (float) layer.origin_box.x; + position.y -= (float) layer.origin_box.y; + + draw_pattern(cr, pattern, layer, [&position](cairo_t* cr, cairo_pattern_t* pattern, int x, int y, int w, int h) + { + cairo_matrix_t flib_m; + cairo_matrix_init_translate(&flib_m, -(position.x + (float ) x), -(position.y + (float ) y)); + cairo_pattern_set_matrix(pattern, &flib_m); + + cairo_set_source(cr, pattern); + cairo_rectangle(cr, x, y, w, h); + cairo_fill(cr); + } + ); + + cairo_pattern_destroy(pattern); + cairo_restore(cr); +} diff --git a/containers/linux/container_linux.h b/containers/cairo/container_cairo.h similarity index 59% rename from containers/linux/container_linux.h rename to containers/cairo/container_cairo.h index 5acb64860..621d1f6ac 100644 --- a/containers/linux/container_linux.h +++ b/containers/cairo/container_cairo.h @@ -1,10 +1,9 @@ #ifndef LH_CONTAINER_LINUX_H #define LH_CONTAINER_LINUX_H -#include "../../include/litehtml.h" +#include #include -#include -#include +#include struct cairo_clip_box { @@ -31,43 +30,23 @@ struct cairo_clip_box } }; -struct cairo_font +class container_cairo : public litehtml::document_container { - PangoFontDescription* font; - int size; - bool underline; - bool strikeout; - int ascent; - int descent; - int underline_thickness; - int underline_position; - int strikethrough_thickness; - int strikethrough_position; -}; - -class container_linux : public litehtml::document_container -{ - typedef std::map > images_map; - protected: - cairo_surface_t* m_temp_surface; - cairo_t* m_temp_cr; - images_map m_images; cairo_clip_box::vector m_clips; public: - container_linux(); - virtual ~container_linux(); + container_cairo() = default; + virtual ~container_cairo() = default; - litehtml::uint_ptr create_font(const char* faceName, int size, int weight, litehtml::font_style italic, unsigned int decoration, litehtml::font_metrics* fm) override; - void delete_font(litehtml::uint_ptr hFont) override; - int text_width(const char* text, litehtml::uint_ptr hFont) override; - void draw_text(litehtml::uint_ptr hdc, const char* text, litehtml::uint_ptr hFont, litehtml::web_color color, const litehtml::position& pos) override; int pt_to_px(int pt) const override; int get_default_font_size() const override; const char* get_default_font_name() const override; - void load_image(const char* src, const char* baseurl, bool redraw_on_ready) override; void get_image_size(const char* src, const char* baseurl, litehtml::size& sz) override; - void draw_background(litehtml::uint_ptr hdc, const std::vector& bg) override; + void draw_image(litehtml::uint_ptr hdc, const litehtml::background_layer& layer, const std::string& url, const std::string& base_url) override; + void draw_solid_fill(litehtml::uint_ptr hdc, const litehtml::background_layer& layer, const litehtml::web_color& color) override; + void draw_linear_gradient(litehtml::uint_ptr hdc, const litehtml::background_layer& layer, const litehtml::background_layer::linear_gradient& gradient) override; + void draw_radial_gradient(litehtml::uint_ptr hdc, const litehtml::background_layer& layer, const litehtml::background_layer::radial_gradient& gradient) override; + void draw_conic_gradient(litehtml::uint_ptr hdc, const litehtml::background_layer& layer, const litehtml::background_layer::conic_gradient& gradient) override; void draw_borders(litehtml::uint_ptr hdc, const litehtml::borders& borders, const litehtml::position& draw_pos, bool root) override; void draw_list_marker(litehtml::uint_ptr hdc, const litehtml::list_marker& marker) override; std::shared_ptr create_element(const char *tag_name, @@ -83,7 +62,10 @@ class container_linux : public litehtml::document_container void del_clip() override; virtual void make_url( const char* url, const char* basepath, litehtml::string& out ); - virtual Glib::RefPtr get_image(const char* url, bool redraw_on_ready) = 0; + virtual cairo_surface_t* get_image(const std::string& url) = 0; + virtual double get_screen_dpi() const = 0; + virtual int get_screen_width() const = 0; + virtual int get_screen_height() const = 0; void clear_images(); @@ -92,13 +74,21 @@ class container_linux : public litehtml::document_container virtual void fill_ellipse(cairo_t* cr, int x, int y, int width, int height, const litehtml::web_color& color); virtual void rounded_rectangle( cairo_t* cr, const litehtml::position &pos, const litehtml::border_radiuses &radius ); -private: + void clip_background_layer(cairo_t* cr, const litehtml::background_layer& layer); void apply_clip(cairo_t* cr); + static void set_color(cairo_t* cr, const litehtml::web_color& color) + { + cairo_set_source_rgba(cr, + color.red / 255.0, + color.green / 255.0, + color.blue / 255.0, + color.alpha / 255.0); + } +private: static void add_path_arc(cairo_t* cr, double x, double y, double rx, double ry, double a1, double a2, bool neg); - static void set_color(cairo_t* cr, const litehtml::web_color& color) { cairo_set_source_rgba(cr, color.red / 255.0, color.green / 255.0, color.blue / 255.0, color.alpha / 255.0); } - static cairo_surface_t* surface_from_pixbuf(const Glib::RefPtr& bmp); - static void draw_pixbuf(cairo_t* cr, const Glib::RefPtr& bmp, int x, int y, int cx, int cy); + static void draw_pixbuf(cairo_t* cr, cairo_surface_t* bmp, int x, int y, int cx, int cy); + static cairo_surface_t* scale_surface(cairo_surface_t* surface, int width, int height); }; #endif diff --git a/containers/cairo/container_cairo_pango.cpp b/containers/cairo/container_cairo_pango.cpp new file mode 100644 index 000000000..10c805c50 --- /dev/null +++ b/containers/cairo/container_cairo_pango.cpp @@ -0,0 +1,444 @@ +#include +#include "container_cairo_pango.h" + +container_cairo_pango::container_cairo_pango() +{ + m_temp_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 2, 2); + m_temp_cr = cairo_create(m_temp_surface); + cairo_save(m_temp_cr); + PangoLayout *layout = pango_cairo_create_layout(m_temp_cr); + PangoContext *context = pango_layout_get_context(layout); + PangoFontFamily** families; + int n; + pango_context_list_families(context, &families, &n); + for(int i = 0; i < n; i++) + { + PangoFontFamily* family = families[i]; + if (!PANGO_IS_FONT_FAMILY(family)) continue; + std::string font_name = pango_font_family_get_name(family); + litehtml::lcase(font_name); + m_all_fonts.insert(font_name); + } + g_free(families); + cairo_restore(m_temp_cr); + g_object_unref(layout); +} + +container_cairo_pango::~container_cairo_pango() +{ + clear_images(); + cairo_surface_destroy(m_temp_surface); + cairo_destroy(m_temp_cr); +} + +litehtml::uint_ptr container_cairo_pango::create_font(const litehtml::font_description& descr, const litehtml::document* doc, litehtml::font_metrics *fm) +{ + litehtml::string_vector tokens; + litehtml::split_string(descr.family, tokens, ","); + std::string fonts; + + for(auto& font : tokens) + { + litehtml::trim(font, " \t\r\n\f\v\"\'"); + if(litehtml::value_in_list(font, "serif;sans-serif;monospace;cursive;fantasy;")) + { + fonts = font + ","; + break; + } + litehtml::lcase(font); + if(m_all_fonts.find(font) != m_all_fonts.end()) + { + fonts = font; + fonts += ","; + break; + } + } + + if(fonts.empty()) + { + fonts = "serif,"; + } + + PangoFontDescription *desc = pango_font_description_from_string (fonts.c_str()); + pango_font_description_set_absolute_size(desc, descr.size * PANGO_SCALE); + if(descr.style == litehtml::font_style_italic) + { + pango_font_description_set_style(desc, PANGO_STYLE_ITALIC); + } else + { + pango_font_description_set_style(desc, PANGO_STYLE_NORMAL); + } + + pango_font_description_set_weight(desc, (PangoWeight) descr.weight); + + cairo_font* ret = nullptr; + + if(fm) + { + fm->font_size = descr.size; + + cairo_save(m_temp_cr); + PangoLayout *layout = pango_cairo_create_layout(m_temp_cr); + PangoContext *context = pango_layout_get_context(layout); + PangoLanguage *language = pango_language_get_default(); + pango_layout_set_font_description(layout, desc); + PangoFontMetrics *metrics = pango_context_get_metrics(context, desc, language); + + fm->ascent = PANGO_PIXELS((double)pango_font_metrics_get_ascent(metrics)); + fm->height = PANGO_PIXELS((double)pango_font_metrics_get_height(metrics)); + fm->descent = fm->height - fm->ascent; + fm->x_height = fm->height; + fm->draw_spaces = (descr.decoration_line != litehtml::text_decoration_line_none); + fm->sub_shift = descr.size / 5; + fm->super_shift = descr.size / 3; + + pango_layout_set_text(layout, "x", 1); + + PangoRectangle ink_rect; + PangoRectangle logical_rect; + pango_layout_get_pixel_extents(layout, &ink_rect, &logical_rect); + fm->x_height = ink_rect.height; + if(fm->x_height == fm->height) fm->x_height = fm->x_height * 4 / 5; + + pango_layout_set_text(layout, "0", 1); + + pango_layout_get_pixel_extents(layout, &ink_rect, &logical_rect); + fm->ch_width = logical_rect.width; + + cairo_restore(m_temp_cr); + + ret = new cairo_font; + ret->font = desc; + ret->size = descr.size; + ret->strikeout = (descr.decoration_line & litehtml::text_decoration_line_line_through) != 0; + ret->underline = (descr.decoration_line & litehtml::text_decoration_line_underline) != 0; + ret->overline = (descr.decoration_line & litehtml::text_decoration_line_overline) != 0; + ret->ascent = fm->ascent; + ret->descent = fm->descent; + ret->decoration_color = descr.decoration_color; + ret->decoration_style = descr.decoration_style; + + auto thinkness = descr.decoration_thickness; + if(!thinkness.is_predefined()) + { + litehtml::css_length one_em(1.0, litehtml::css_units_em); + doc->cvt_units(one_em, *fm, 0); + doc->cvt_units(thinkness, *fm, (int) one_em.val()); + } + + + ret->underline_position = -pango_font_metrics_get_underline_position(metrics); + if(thinkness.is_predefined()) + { + ret->underline_thickness = pango_font_metrics_get_underline_thickness(metrics); + } else + { + ret->underline_thickness = (int)(thinkness.val() * PANGO_SCALE); + } + pango_quantize_line_geometry(&ret->underline_thickness, &ret->underline_position); + ret->underline_thickness = PANGO_PIXELS(ret->underline_thickness); + ret->underline_position = PANGO_PIXELS(ret->underline_position); + + ret->strikethrough_position = pango_font_metrics_get_strikethrough_position(metrics); + if(thinkness.is_predefined()) + { + ret->strikethrough_thickness = pango_font_metrics_get_strikethrough_thickness(metrics); + } else + { + ret->strikethrough_thickness = (int)(thinkness.val() * PANGO_SCALE); + } + pango_quantize_line_geometry(&ret->strikethrough_thickness, &ret->strikethrough_position); + ret->strikethrough_thickness = PANGO_PIXELS(ret->strikethrough_thickness); + ret->strikethrough_position = PANGO_PIXELS(ret->strikethrough_position); + + ret->overline_position = fm->ascent * PANGO_SCALE; + if(thinkness.is_predefined()) + { + ret->overline_thickness = pango_font_metrics_get_underline_thickness(metrics); + } else + { + ret->overline_thickness = (int)(thinkness.val() * PANGO_SCALE); + } + pango_quantize_line_geometry(&ret->overline_thickness, &ret->overline_position); + ret->overline_thickness = PANGO_PIXELS(ret->overline_thickness); + ret->overline_position = PANGO_PIXELS(ret->overline_position); + + g_object_unref(layout); + pango_font_metrics_unref(metrics); + } + + return (litehtml::uint_ptr) ret; +} + +void container_cairo_pango::delete_font(litehtml::uint_ptr hFont) +{ + auto* fnt = (cairo_font*) hFont; + if(fnt) + { + pango_font_description_free(fnt->font); + delete fnt; + } +} + +int container_cairo_pango::text_width(const char *text, litehtml::uint_ptr hFont) +{ + auto* fnt = (cairo_font*) hFont; + + cairo_save(m_temp_cr); + + PangoLayout *layout = pango_cairo_create_layout(m_temp_cr); + pango_layout_set_font_description(layout, fnt->font); + + pango_layout_set_text(layout, text, -1); + pango_cairo_update_layout (m_temp_cr, layout); + + int x_width, x_height; + pango_layout_get_pixel_size(layout, &x_width, &x_height); + + cairo_restore(m_temp_cr); + + g_object_unref(layout); + + return (int) x_width; +} + +enum class draw_type +{ + DRAW_OVERLINE, + DRAW_STRIKETHROUGH, + DRAW_UNDERLINE +}; + +static inline void draw_single_line(cairo_t* cr, int x, int y, int width, int thickness, draw_type type) +{ + double top; + switch (type) + { + case draw_type::DRAW_UNDERLINE: + top = y + (double)thickness / 2.0; + break; + case draw_type::DRAW_OVERLINE: + top = y - (double)thickness / 2.0; + break; + case draw_type::DRAW_STRIKETHROUGH: + top = y + 0.5; + break; + default: + top = y; + break; + } + cairo_move_to(cr, x, top); + cairo_line_to(cr, x + width, top); +} + +static void draw_solid_line(cairo_t* cr, int x, int y, int width, int thickness, draw_type type, litehtml::web_color& color) +{ + draw_single_line(cr, x, y, width, thickness, type); + + cairo_set_source_rgba(cr, (double) color.red / 255.0, + (double) color.green / 255.0, + (double) color.blue / 255.0, + (double) color.alpha / 255.0); + cairo_set_line_width(cr, thickness); + cairo_stroke(cr); +} + +static void draw_dotted_line(cairo_t* cr, int x, int y, int width, int thickness, draw_type type, litehtml::web_color& color) +{ + draw_single_line(cr, x, y, width, thickness, type); + + std::array dashes {0, thickness * 2.0}; + if(thickness == 1) dashes[1] += thickness / 2.0; + cairo_set_line_width(cr, thickness); + cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); + cairo_set_dash(cr, dashes.data(), 2, x); + + cairo_set_source_rgba(cr, (double) color.red / 255.0, + (double) color.green / 255.0, + (double) color.blue / 255.0, + (double) color.alpha / 255.0); + cairo_stroke(cr); +} + +static void draw_dashed_line(cairo_t* cr, int x, int y, int width, int thickness, draw_type type, litehtml::web_color& color) +{ + draw_single_line(cr, x, y, width, thickness, type); + + std::array dashes {thickness * 2.0, thickness * 3.0}; + cairo_set_line_width(cr, thickness); + cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); + cairo_set_dash(cr, dashes.data(), 2, x); + + cairo_set_source_rgba(cr, (double) color.red / 255.0, + (double) color.green / 255.0, + (double) color.blue / 255.0, + (double) color.alpha / 255.0); + cairo_stroke(cr); +} + +static void draw_wavy_line(cairo_t* cr, int x, int y, int width, int thickness, draw_type type, litehtml::web_color& color) +{ + double h_pad = 1.0; + int brush_height = (int) thickness * 3 + h_pad * 2; + int brush_width = brush_height * 2 - 2 * thickness; + + double top; + switch (type) + { + case draw_type::DRAW_UNDERLINE: + top = y + (double)brush_height / 2.0; + break; + case draw_type::DRAW_OVERLINE: + top = y - (double)brush_height / 2.0; + break; + default: + top = y; + break; + } + + cairo_surface_t* brush_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, brush_width, brush_height); + cairo_t* brush_cr = cairo_create(brush_surface); + + cairo_set_source_rgba(brush_cr, (double) color.red / 255.0, + (double) color.green / 255.0, + (double) color.blue / 255.0, + (double) color.alpha / 255.0); + cairo_set_line_width(brush_cr, thickness); + double w = thickness / 2.0; + cairo_move_to(brush_cr, 0, brush_height - (double) thickness / 2.0 - h_pad); + cairo_line_to(brush_cr, w, brush_height - (double) thickness / 2.0 - h_pad); + cairo_line_to(brush_cr, brush_width / 2.0 - w, (double) thickness / 2.0 + h_pad); + cairo_line_to(brush_cr, brush_width / 2.0 + w, (double) thickness / 2.0 + h_pad); + cairo_line_to(brush_cr, brush_width - w, brush_height - (double) thickness / 2.0 - h_pad); + cairo_line_to(brush_cr, brush_width, brush_height - (double) thickness / 2.0 - h_pad); + cairo_stroke(brush_cr); + cairo_destroy(brush_cr); + + cairo_pattern_t *pattern = cairo_pattern_create_for_surface(brush_surface); + cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); + cairo_matrix_t patterm_matrix; + cairo_matrix_init_translate(&patterm_matrix, 0, -top + brush_height / 2.0); + cairo_pattern_set_matrix(pattern, &patterm_matrix); + cairo_set_source(cr, pattern); + + cairo_set_line_width(cr, brush_height); + cairo_move_to(cr, x, top); + cairo_line_to(cr, x + width, top); + cairo_stroke(cr); + + cairo_pattern_destroy(pattern); + cairo_surface_destroy(brush_surface); +} + +static void draw_double_line(cairo_t* cr, int x, int y, int width, int thickness, draw_type type, litehtml::web_color& color) +{ + cairo_set_line_width(cr, thickness); + double top1; + double top2; + switch (type) + { + case draw_type::DRAW_UNDERLINE: + top1 = y + (double)thickness / 2.0; + top2 = top1 + (double)thickness + (double)thickness / 2.0 + 0.5; + break; + case draw_type::DRAW_OVERLINE: + top1 = y - (double)thickness / 2.0; + top2 = top1 - (double)thickness - (double)thickness / 2.0 - 0.5; + break; + case draw_type::DRAW_STRIKETHROUGH: + top1 = y - (double)thickness + 0.5; + top2 = y + (double)thickness + 0.5; + break; + default: + top1 = y; + top2 = y; + break; + } + cairo_move_to(cr, x, top1); + cairo_line_to(cr, x + width, top1); + cairo_stroke(cr); + cairo_move_to(cr, x, top2); + cairo_line_to(cr, x + width, top2); + cairo_set_source_rgba(cr, (double) color.red / 255.0, + (double) color.green / 255.0, + (double) color.blue / 255.0, + (double) color.alpha / 255.0); + cairo_stroke(cr); +} + +void container_cairo_pango::draw_text(litehtml::uint_ptr hdc, const char *text, litehtml::uint_ptr hFont, + litehtml::web_color color, const litehtml::position &pos) +{ + auto* fnt = (cairo_font*) hFont; + auto* cr = (cairo_t*) hdc; + cairo_save(cr); + + apply_clip(cr); + + set_color(cr, color); + + litehtml::web_color decoration_color = color; + + PangoLayout *layout = pango_cairo_create_layout(cr); + pango_layout_set_font_description (layout, fnt->font); + pango_layout_set_text (layout, text, -1); + + auto font_options = get_font_options(); + if(font_options) + { + auto ctx = pango_layout_get_context(layout); + pango_cairo_context_set_font_options(ctx, font_options); + } + + PangoRectangle ink_rect, logical_rect; + pango_layout_get_pixel_extents(layout, &ink_rect, &logical_rect); + + int text_baseline = pos.height - fnt->descent; + + int x = pos.left() + logical_rect.x; + int y = pos.top(); + + cairo_move_to(cr, x, y); + pango_cairo_update_layout (cr, layout); + pango_cairo_show_layout (cr, layout); + + int tw = 0; + + if(fnt->underline || fnt->strikeout || fnt->overline) + { + tw = text_width(text, hFont); + } + + if(!fnt->decoration_color.is_current_color) + { + decoration_color = fnt->decoration_color; + } + + std::array draw_funcs { + draw_solid_line, // text_decoration_style_solid + draw_double_line, // text_decoration_style_double + draw_dotted_line, // text_decoration_style_dotted + draw_dashed_line, // text_decoration_style_dashed + draw_wavy_line, // text_decoration_style_wavy + }; + + if(fnt->underline) + { + draw_funcs[fnt->decoration_style](cr, x, pos.top() + text_baseline + fnt->underline_position, tw, fnt->underline_thickness, draw_type::DRAW_UNDERLINE, decoration_color); + } + + if(fnt->strikeout) + { + draw_funcs[fnt->decoration_style](cr, x, pos.top() + text_baseline - fnt->strikethrough_position, tw, fnt->strikethrough_thickness, draw_type::DRAW_STRIKETHROUGH, decoration_color); + } + + if(fnt->overline) + { + draw_funcs[fnt->decoration_style](cr, x, pos.top() + text_baseline - fnt->overline_position, tw, fnt->overline_thickness, draw_type::DRAW_OVERLINE, decoration_color); + } + + cairo_restore(cr); + + g_object_unref(layout); +} + diff --git a/containers/cairo/container_cairo_pango.h b/containers/cairo/container_cairo_pango.h new file mode 100644 index 000000000..7d91698e2 --- /dev/null +++ b/containers/cairo/container_cairo_pango.h @@ -0,0 +1,46 @@ +#ifndef LITEBROWSER_CONTAINER_CAIRO_PANGO_H +#define LITEBROWSER_CONTAINER_CAIRO_PANGO_H + +#include +#include "container_cairo.h" +#include +#include +#include +#include + +struct cairo_font +{ + PangoFontDescription* font; + int size; + bool underline; + bool strikeout; + bool overline; + int ascent; + int descent; + int underline_thickness; + int underline_position; + int strikethrough_thickness; + int strikethrough_position; + int overline_thickness; + int overline_position; + int decoration_style; + litehtml::web_color decoration_color; +}; + +class container_cairo_pango : public container_cairo +{ + cairo_surface_t* m_temp_surface; + cairo_t* m_temp_cr; + std::set m_all_fonts; +public: + container_cairo_pango(); + ~container_cairo_pango() override; + litehtml::uint_ptr create_font(const litehtml::font_description& descr, const litehtml::document* doc, litehtml::font_metrics* fm) override; + void delete_font(litehtml::uint_ptr hFont) override; + int text_width(const char* text, litehtml::uint_ptr hFont) override; + void draw_text(litehtml::uint_ptr hdc, const char* text, litehtml::uint_ptr hFont, litehtml::web_color color, const litehtml::position& pos) override; + + virtual cairo_font_options_t* get_font_options() { return nullptr; } +}; + +#endif //LITEBROWSER_CONTAINER_CAIRO_PANGO_H diff --git a/containers/cairo/render2png.cpp b/containers/cairo/render2png.cpp new file mode 100644 index 000000000..bd6e7bb88 --- /dev/null +++ b/containers/cairo/render2png.cpp @@ -0,0 +1,320 @@ +#include "render2png.h" +#include "container_cairo_pango.h" +#include +#include +#include +#include +#include +#include +#include "cairo_images_cache.h" + +namespace fs = std::filesystem; + +namespace html2png +{ + class html_config + { + std::map m_data; + public: + explicit html_config(const std::string& html_file) + { + fs::path cfg_path = html_file + ".cfg"; + if(exists(cfg_path)) + { + std::ifstream infile(cfg_path); + if(infile.is_open()) + { + for(std::string line; std::getline(infile, line);) + { + auto parts = litehtml::split_string(line, ":"); + if(parts.size() == 2) + { + m_data.emplace( + litehtml::trim(parts[0], litehtml::split_delims_spaces), + litehtml::trim(parts[1], litehtml::split_delims_spaces) + ); + } + } + } + } + } + + int get_int(const std::string& key, int default_value) + { + auto iter = m_data.find(key); + if(iter != m_data.end()) + { + return std::stoi(iter->second); + } + return default_value; + } + + bool get_bool(const std::string& key, bool default_value) + { + auto iter = m_data.find(key); + if(iter != m_data.end()) + { + if(iter->second == "true") return true; + if(iter->second == "false") return false; + } + return default_value; + } + }; + + class container : public container_cairo_pango + { + std::string m_base_path; + converter* m_converter; + cairo_images_cache m_images; + public: + explicit container(const std::string& base_path, converter* cvt) : m_base_path(base_path), m_converter(cvt) + { + } + + void load_image([[maybe_unused]] const char *src, [[maybe_unused]] const char *baseurl, [[maybe_unused]] bool redraw_on_ready) override {} + void set_caption([[maybe_unused]] const char *caption) override {} + + std::string urlDecode(const std::string &SRC) { + std::string ret; + char ch; + uint32_t i, ii; + for (i=0; i < (uint32_t) SRC.length(); i++) { + if (SRC[i] == '%') { + sscanf(SRC.substr(i + 1, 2).c_str(), "%x", &ii); + ch = static_cast(ii); + ret += ch; + i = i + 2; + } else + { + ret += SRC[i]; + } + } + return (ret); + } + cairo_surface_t *get_image(const std::string& _url) override + { + std::string url = urlDecode(_url); + if(url.empty()) + return nullptr; + + cairo_surface_t* image_surface = m_images.get_image(url); + + if(!image_surface) + { + auto pixbuf = gdk_pixbuf_new_from_file(url.c_str(), nullptr); + + if (pixbuf) + { + if (gdk_pixbuf_get_has_alpha(pixbuf)) + { + image_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, gdk_pixbuf_get_width(pixbuf), + gdk_pixbuf_get_height(pixbuf)); + } else + { + image_surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, gdk_pixbuf_get_width(pixbuf), + gdk_pixbuf_get_height(pixbuf)); + } + if(image_surface) + { + auto cr = cairo_create(image_surface); + gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0); + cairo_paint(cr); + cairo_destroy(cr); + m_images.add_image(url, image_surface); + // add_image doesn't increase reference. We must do it manually + image_surface = cairo_surface_reference(image_surface); + } + + g_object_unref(pixbuf); + } + } + + return image_surface; + } + + double get_screen_dpi() const override { return m_converter->get_dpi(); } + int get_screen_width() const override { return m_converter->get_screen_width(); } + int get_screen_height() const override { return m_converter->get_screen_height(); } + void set_base_url([[maybe_unused]] const char *base_url) override + { + if(base_url) + { + m_base_path = base_url; + } + } + void on_anchor_click([[maybe_unused]] const char *url, [[maybe_unused]] const litehtml::element::ptr &el) override {} + void on_mouse_event([[maybe_unused]] const litehtml::element::ptr &el, [[maybe_unused]] litehtml::mouse_event event) override {} + void set_cursor([[maybe_unused]] const char *cursor) override {} + void import_css([[maybe_unused]] std::string &text, [[maybe_unused]] const std::string &url, [[maybe_unused]] std::string &baseurl) override + { + std::string path; + make_url(url.c_str(), baseurl.c_str(), path); + path = urlDecode(path); + + std::stringstream ss; + std::ifstream(path, std::ios::binary) >> ss.rdbuf(); + text = ss.str(); + } + + void get_viewport(litehtml::position& viewport) const override + { + viewport.width = m_converter->get_screen_width(); + viewport.height = m_converter->get_screen_height(); + viewport.x = 0; + viewport.y = 0; + } + + const char* get_default_font_name() const override { return m_converter->get_default_font(); } + cairo_font_options_t* get_font_options() override { return m_converter->get_font_options(); } + + void make_url(const char *url, const char *basepath, std::string &out) override + { + if(basepath && *basepath) + { + out = std::filesystem::path(basepath) / url; + } else if(!m_base_path.empty()) + { + out = std::filesystem::path(m_base_path) / url; + } else + { + out = url; + } + } + }; + + converter::converter(int screen_width /*= 800*/, int screen_height /*= 600*/, double dpi /*= 96*/, const char* default_font /*= nullptr*/, cairo_font_options_t* font_options /*= nullptr*/) + { + m_screen_width = screen_width; + m_screen_height = screen_height; + m_dpi = dpi; + m_font_options = font_options; + if(default_font) + { + m_default_font = default_font; + } else + { + m_default_font = "serif"; + } + } + + GdkPixbuf* converter::to_pixbuf(const std::string &html_file) + { + html_config cfg(html_file); + + std::stringstream ss; + std::ifstream(html_file, std::ios::binary) >> ss.rdbuf(); + + std::string base_path = std::filesystem::path(html_file).parent_path().string(); + + int width, height; + container cont(base_path, this); + auto doc = litehtml::document::createFromString(ss.str(), &cont); + // document::render returns "best fit" width. We can use this width to rerender document + int best_width = doc->render(m_screen_width); + if(best_width > 0 && cfg.get_bool("bestfit", true)) + { + best_width = cfg.get_int("width", best_width); + std::swap(m_screen_width, best_width); + doc->render(m_screen_width); + std::swap(m_screen_width, best_width); + } + + width = cfg.get_int("width", doc->content_width() > 0 ? doc->content_width() : 1); + height = cfg.get_int("height", doc->content_height() > 0 ? doc->content_height() : 1); + + auto surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); + if(surface) + { + auto cr = cairo_create(surface); + + // Fill background with white color + cairo_save(cr); + cairo_rectangle(cr, 0, 0, width, height); + cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0); + cairo_fill(cr); + cairo_restore(cr); + + // Draw document + litehtml::position clip(0, 0, width, height); + doc->draw((litehtml::uint_ptr) cr, 0, 0, &clip); + + cairo_surface_flush(surface); + cairo_destroy(cr); + + GdkPixbuf* pixbuf = gdk_pixbuf_get_from_surface(surface, 0, 0, width, height); + cairo_surface_destroy(surface); + return pixbuf; + } + + return nullptr; + } + + bool converter::to_png(const std::string &html_file, const std::string &png_file) + { + auto pixbuf = to_pixbuf(html_file); + bool ret = false; + if(pixbuf) + { + ret = gdk_pixbuf_save(pixbuf, png_file.c_str(), "png", nullptr, nullptr) == TRUE; + g_object_unref(pixbuf); + } + return ret; + } + + png_diff_t pngcmp(const GdkPixbuf* pixbuf1, const GdkPixbuf* pixbuf2) + { + if(gdk_pixbuf_get_height(pixbuf1) != gdk_pixbuf_get_height(pixbuf2) || + gdk_pixbuf_get_width(pixbuf1) != gdk_pixbuf_get_width(pixbuf2)) + { + return png_diff_dimensions; + } + + if(gdk_pixbuf_get_byte_length(pixbuf1) != gdk_pixbuf_get_byte_length(pixbuf2)) + { + return png_diff_data; + } + + const auto sz = gdk_pixbuf_get_byte_length(pixbuf1); + + if(memcmp(gdk_pixbuf_get_pixels(pixbuf1), gdk_pixbuf_get_pixels(pixbuf2), sz)) + { + return png_diff_data; + } + + return png_diff_same; + } + + png_diff_t pngcmp(const std::string &png_file1, const std::string &png_file2) + { + auto pixbuf1 = gdk_pixbuf_new_from_file(png_file1.c_str(), nullptr); + if(!pixbuf1) + { + return png_diff_error; + } + auto pixbuf2 = gdk_pixbuf_new_from_file(png_file2.c_str(), nullptr); + if(!pixbuf2) + { + return png_diff_error; + } + + auto res = pngcmp(pixbuf1, pixbuf2); + + g_object_unref(pixbuf1); + g_object_unref(pixbuf2); + + return res; + } + + png_diff_t pngcmp(const GdkPixbuf* pixbuf, const std::string &png_file) + { + auto pixbuf2 = gdk_pixbuf_new_from_file(png_file.c_str(), nullptr); + if(!pixbuf2) + { + return png_diff_error; + } + auto res = pngcmp(pixbuf, pixbuf2); + g_object_unref(pixbuf2); + return res; + } + +} diff --git a/containers/cairo/render2png.h b/containers/cairo/render2png.h new file mode 100644 index 000000000..7aabc97d2 --- /dev/null +++ b/containers/cairo/render2png.h @@ -0,0 +1,42 @@ +#ifndef LITEHTML_RENDER2PNG_H +#define LITEHTML_RENDER2PNG_H + +#include +#include +#include + +namespace html2png +{ + class converter + { + int m_screen_width; + int m_screen_height; + double m_dpi; + const char* m_default_font; + cairo_font_options_t* m_font_options; + public: + converter(int screen_width = 800, int screen_height = 600, double dpi = 96, const char* default_font = nullptr, cairo_font_options_t* font_options = nullptr); + bool to_png(const std::string &html_file, const std::string &png_file); + GdkPixbuf* to_pixbuf(const std::string &html_file); + + int get_screen_width() const { return m_screen_width; } + int get_screen_height() const { return m_screen_height; } + const char* get_default_font() const { return m_default_font; } + double get_dpi() const { return m_dpi; } + cairo_font_options_t* get_font_options() { return m_font_options; } + }; + + enum png_diff_t + { + png_diff_same, + png_diff_dimensions, + png_diff_data, + png_diff_error, + }; + + extern png_diff_t pngcmp(const std::string &png_file1, const std::string &png_file2); + extern png_diff_t pngcmp(const GdkPixbuf* pixbuf1, const GdkPixbuf* pixbuf2); + extern png_diff_t pngcmp(const GdkPixbuf* pixbuf, const std::string &png_file); +} + +#endif //LITEHTML_RENDER2PNG_H diff --git a/containers/haiku/container_haiku.cpp b/containers/haiku/container_haiku.cpp index 7c49f22b6..412697229 100644 --- a/containers/haiku/container_haiku.cpp +++ b/containers/haiku/container_haiku.cpp @@ -441,17 +441,17 @@ LiteHtmlView::draw_borders(litehtml::uint_ptr hdc, const litehtml::borders& bord if(borders.top.width != 0 && borders.top.style > litehtml::border_style_hidden) { bdr_top = (int) borders.top.width; - std::cout << " Border top: " << bdr_right << std::endl; + std::cout << " Border top: " << bdr_top << std::endl; } if(borders.bottom.width != 0 && borders.bottom.style > litehtml::border_style_hidden) { bdr_bottom = (int) borders.bottom.width; - std::cout << " Border bottom: " << bdr_right << std::endl; + std::cout << " Border bottom: " << bdr_bottom << std::endl; } if(borders.left.width != 0 && borders.left.style > litehtml::border_style_hidden) { bdr_left = (int) borders.left.width; - std::cout << " Border left: " << bdr_right << std::endl; + std::cout << " Border left: " << bdr_left << std::endl; } if(borders.right.width != 0 && borders.right.style > litehtml::border_style_hidden) { diff --git a/containers/linux/container_linux.cpp b/containers/linux/container_linux.cpp deleted file mode 100644 index b5d984d44..000000000 --- a/containers/linux/container_linux.cpp +++ /dev/null @@ -1,908 +0,0 @@ -#include "container_linux.h" -#include - -#ifndef M_PI -# define M_PI 3.14159265358979323846 -#endif - -container_linux::container_linux() -{ - m_temp_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 2, 2); - m_temp_cr = cairo_create(m_temp_surface); -} - -container_linux::~container_linux() -{ - clear_images(); - cairo_surface_destroy(m_temp_surface); - cairo_destroy(m_temp_cr); -} - -litehtml::uint_ptr container_linux::create_font( const char* faceName, int size, int weight, litehtml::font_style italic, unsigned int decoration, litehtml::font_metrics* fm ) -{ - PangoFontDescription *desc = pango_font_description_from_string (faceName); - pango_font_description_set_absolute_size(desc, size * PANGO_SCALE); - if(italic == litehtml::font_style_italic) - { - pango_font_description_set_style(desc, PANGO_STYLE_ITALIC); - } else - { - pango_font_description_set_style(desc, PANGO_STYLE_NORMAL); - } - PangoWeight fnt_weight; - if(weight >= 0 && weight < 150) fnt_weight = PANGO_WEIGHT_THIN; - else if(weight >= 150 && weight < 250) fnt_weight = PANGO_WEIGHT_ULTRALIGHT; - else if(weight >= 250 && weight < 350) fnt_weight = PANGO_WEIGHT_LIGHT; - else if(weight >= 350 && weight < 450) fnt_weight = PANGO_WEIGHT_NORMAL; - else if(weight >= 450 && weight < 550) fnt_weight = PANGO_WEIGHT_MEDIUM; - else if(weight >= 550 && weight < 650) fnt_weight = PANGO_WEIGHT_SEMIBOLD; - else if(weight >= 650 && weight < 750) fnt_weight = PANGO_WEIGHT_BOLD; - else if(weight >= 750 && weight < 850) fnt_weight = PANGO_WEIGHT_ULTRABOLD; - else fnt_weight = PANGO_WEIGHT_HEAVY; - - pango_font_description_set_weight(desc, fnt_weight); - - cairo_font* ret = nullptr; - - if(fm) - { - cairo_save(m_temp_cr); - PangoLayout *layout = pango_cairo_create_layout(m_temp_cr); - PangoContext *context = pango_layout_get_context(layout); - PangoLanguage *language = pango_language_get_default(); - pango_layout_set_font_description(layout, desc); - PangoFontMetrics *metrics = pango_context_get_metrics(context, desc, language); - - fm->ascent = PANGO_PIXELS((double)pango_font_metrics_get_ascent(metrics)); - fm->descent = PANGO_PIXELS((double)pango_font_metrics_get_descent(metrics)); - fm->height = fm->ascent + fm->descent; - fm->x_height = fm->height; - - pango_layout_set_text(layout, "x", 1); - - int x_width, x_height; - pango_layout_get_pixel_size(layout, &x_width, &x_height); - - fm->x_height = x_height; - - cairo_restore(m_temp_cr); - - g_object_unref(layout); - pango_font_metrics_unref(metrics); - - ret = new cairo_font; - ret->font = desc; - ret->size = size; - ret->strikeout = (decoration & litehtml::font_decoration_linethrough) != 0; - ret->underline = (decoration & litehtml::font_decoration_underline) != 0; - ret->ascent = fm->ascent; - ret->descent = fm->descent; - - ret->underline_thickness = pango_font_metrics_get_underline_thickness(metrics); - ret->underline_position = -pango_font_metrics_get_underline_position(metrics); - pango_quantize_line_geometry(&ret->underline_thickness, &ret->underline_position); - ret->underline_thickness = PANGO_PIXELS(ret->underline_thickness); - ret->underline_position = -1;//PANGO_PIXELS(ret->underline_position); - - ret->strikethrough_thickness = pango_font_metrics_get_strikethrough_thickness(metrics); - ret->strikethrough_position = pango_font_metrics_get_strikethrough_position(metrics); - pango_quantize_line_geometry(&ret->strikethrough_thickness, &ret->strikethrough_position); - ret->strikethrough_thickness = PANGO_PIXELS(ret->strikethrough_thickness); - ret->strikethrough_position = PANGO_PIXELS(ret->strikethrough_position); - } - - return (litehtml::uint_ptr) ret; -} - -void container_linux::delete_font( litehtml::uint_ptr hFont ) -{ - auto* fnt = (cairo_font*) hFont; - if(fnt) - { - pango_font_description_free(fnt->font); - delete fnt; - } -} - -int container_linux::text_width( const char* text, litehtml::uint_ptr hFont ) -{ - auto* fnt = (cairo_font*) hFont; - - cairo_save(m_temp_cr); - - PangoLayout *layout = pango_cairo_create_layout(m_temp_cr); - pango_layout_set_font_description(layout, fnt->font); - - pango_layout_set_text(layout, text, -1); - pango_cairo_update_layout (m_temp_cr, layout); - - int x_width, x_height; - pango_layout_get_pixel_size(layout, &x_width, &x_height); - - cairo_restore(m_temp_cr); - - g_object_unref(layout); - - return (int) x_width; -} - -void container_linux::draw_text( litehtml::uint_ptr hdc, const char* text, litehtml::uint_ptr hFont, litehtml::web_color color, const litehtml::position& pos ) -{ - auto* fnt = (cairo_font*) hFont; - auto* cr = (cairo_t*) hdc; - cairo_save(cr); - - apply_clip(cr); - - set_color(cr, color); - - PangoLayout *layout = pango_cairo_create_layout(cr); - pango_layout_set_font_description (layout, fnt->font); - pango_layout_set_text (layout, text, -1); - - int baseline = PANGO_PIXELS(pango_layout_get_baseline(layout)); - - PangoRectangle ink_rect, logical_rect; - pango_layout_get_pixel_extents(layout, &ink_rect, &logical_rect); - - int text_baseline = pos.height - fnt->descent; - - int x = pos.left() + logical_rect.x; - int y = pos.top() + logical_rect.y + text_baseline - baseline; - - cairo_move_to(cr, x, y); - pango_cairo_update_layout (cr, layout); - pango_cairo_show_layout (cr, layout); - - int tw = 0; - - if(fnt->underline || fnt->strikeout) - { - tw = text_width(text, hFont); - } - - if(fnt->underline) - { - cairo_set_line_width(cr, fnt->underline_thickness); - cairo_move_to(cr, x, pos.top() + text_baseline - fnt->underline_position + 0.5); - cairo_line_to(cr, x + tw, pos.top() + text_baseline - fnt->underline_position + 0.5); - cairo_stroke(cr); - } - if(fnt->strikeout) - { - cairo_set_line_width(cr, fnt->strikethrough_thickness); - cairo_move_to(cr, x, pos.top() + text_baseline - fnt->strikethrough_position - 0.5); - cairo_line_to(cr, x + tw, pos.top() + text_baseline - fnt->strikethrough_position - 0.5); - cairo_stroke(cr); - } - - cairo_restore(cr); - - g_object_unref(layout); -} - -int container_linux::pt_to_px( int pt ) const -{ - GdkScreen* screen = gdk_screen_get_default(); - double dpi = gdk_screen_get_resolution(screen); - - return (int) ((double) pt * dpi / 72.0); -} - -int container_linux::get_default_font_size() const -{ - return pt_to_px(12); -} - -void container_linux::draw_list_marker( litehtml::uint_ptr hdc, const litehtml::list_marker& marker ) -{ - if(!marker.image.empty()) - { - /*litehtml::string url; - make_url(marker.image.c_str(), marker.baseurl, url); - - lock_images_cache(); - images_map::iterator img_i = m_images.find(url.c_str()); - if(img_i != m_images.end()) - { - if(img_i->second) - { - draw_txdib((cairo_t*) hdc, img_i->second, marker.pos.x, marker.pos.y, marker.pos.width, marker.pos.height); - } - } - unlock_images_cache();*/ - } else - { - switch(marker.marker_type) - { - case litehtml::list_style_type_circle: - { - draw_ellipse((cairo_t*) hdc, marker.pos.x, marker.pos.y, marker.pos.width, marker.pos.height, marker.color, 1); - } - break; - case litehtml::list_style_type_disc: - { - fill_ellipse((cairo_t*) hdc, marker.pos.x, marker.pos.y, marker.pos.width, marker.pos.height, marker.color); - } - break; - case litehtml::list_style_type_square: - if(hdc) - { - auto* cr = (cairo_t*) hdc; - cairo_save(cr); - - cairo_new_path(cr); - cairo_rectangle(cr, marker.pos.x, marker.pos.y, marker.pos.width, marker.pos.height); - - set_color(cr, marker.color); - cairo_fill(cr); - cairo_restore(cr); - } - break; - default: - /*do nothing*/ - break; - } - } -} - -void container_linux::load_image( const char* src, const char* baseurl, bool redraw_on_ready ) -{ - litehtml::string url; - make_url(src, baseurl, url); - if(m_images.find(url) == m_images.end()) - { - try - { - Glib::RefPtr img = get_image(url.c_str(), true); - if(img) - { - m_images[url.c_str()] = img; - } - } catch(...) - { - m_images[url.c_str()] = Glib::RefPtr(nullptr); - } - } -} - -void container_linux::get_image_size( const char* src, const char* baseurl, litehtml::size& sz ) -{ - litehtml::string url; - make_url(src, baseurl, url); - - auto img = m_images.find(url); - if(img != m_images.end()) - { - if(img->second) - { - sz.width = img->second->get_width(); - sz.height = img->second->get_height(); - } else - { - sz.width = 0; - sz.height = 0; - } - } else - { - sz.width = 0; - sz.height = 0; - } -} - -void container_linux::draw_background( litehtml::uint_ptr hdc, const std::vector& bgvec ) -{ - auto* cr = (cairo_t*) hdc; - cairo_save(cr); - apply_clip(cr); - - const auto& bg = bgvec.back(); - - rounded_rectangle(cr, bg.border_box, bg.border_radius); - cairo_clip(cr); - - cairo_rectangle(cr, bg.clip_box.x, bg.clip_box.y, bg.clip_box.width, bg.clip_box.height); - cairo_clip(cr); - - if(bg.color.alpha) - { - set_color(cr, bg.color); - cairo_paint(cr); - } - - for (int i = (int)bgvec.size() - 1; i >= 0; i--) - { - const auto& bg = bgvec[i]; - - if(bg.image_size.height == 0 || bg.image_size.width == 0) continue; - - cairo_rectangle(cr, bg.clip_box.x, bg.clip_box.y, bg.clip_box.width, bg.clip_box.height); - cairo_clip(cr); - - std::string url; - make_url(bg.image.c_str(), bg.baseurl.c_str(), url); - - //lock_images_cache(); - auto img_i = m_images.find(url); - if(img_i != m_images.end() && img_i->second) - { - Glib::RefPtr bgbmp = img_i->second; - - Glib::RefPtr new_img; - if(bg.image_size.width != bgbmp->get_width() || bg.image_size.height != bgbmp->get_height()) - { - new_img = bgbmp->scale_simple(bg.image_size.width, bg.image_size.height, Gdk::INTERP_BILINEAR); - bgbmp = new_img; - } - - cairo_surface_t* img = surface_from_pixbuf(bgbmp); - cairo_pattern_t *pattern = cairo_pattern_create_for_surface(img); - cairo_matrix_t flib_m; - cairo_matrix_init_identity(&flib_m); - cairo_matrix_translate(&flib_m, -bg.position_x, -bg.position_y); - cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT); - cairo_pattern_set_matrix (pattern, &flib_m); - - switch(bg.repeat) - { - case litehtml::background_repeat_no_repeat: - draw_pixbuf(cr, bgbmp, bg.position_x, bg.position_y, bgbmp->get_width(), bgbmp->get_height()); - break; - - case litehtml::background_repeat_repeat_x: - cairo_set_source(cr, pattern); - cairo_rectangle(cr, bg.clip_box.left(), bg.position_y, bg.clip_box.width, bgbmp->get_height()); - cairo_fill(cr); - break; - - case litehtml::background_repeat_repeat_y: - cairo_set_source(cr, pattern); - cairo_rectangle(cr, bg.position_x, bg.clip_box.top(), bgbmp->get_width(), bg.clip_box.height); - cairo_fill(cr); - break; - - case litehtml::background_repeat_repeat: - cairo_set_source(cr, pattern); - cairo_rectangle(cr, bg.clip_box.left(), bg.clip_box.top(), bg.clip_box.width, bg.clip_box.height); - cairo_fill(cr); - break; - } - - cairo_pattern_destroy(pattern); - cairo_surface_destroy(img); - } - //unlock_images_cache(); - } - - cairo_restore(cr); -} - -void container_linux::make_url(const char* url, const char* basepath, litehtml::string& out) -{ - out = url; -} - -void container_linux::add_path_arc(cairo_t* cr, double x, double y, double rx, double ry, double a1, double a2, bool neg) -{ - if(rx > 0 && ry > 0) - { - - cairo_save(cr); - - cairo_translate(cr, x, y); - cairo_scale(cr, 1, ry / rx); - cairo_translate(cr, -x, -y); - - if(neg) - { - cairo_arc_negative(cr, x, y, rx, a1, a2); - } else - { - cairo_arc(cr, x, y, rx, a1, a2); - } - - cairo_restore(cr); - } else - { - cairo_move_to(cr, x, y); - } -} - -void container_linux::draw_borders(litehtml::uint_ptr hdc, const litehtml::borders& borders, const litehtml::position& draw_pos, bool root) -{ - auto* cr = (cairo_t*) hdc; - cairo_save(cr); - apply_clip(cr); - - cairo_new_path(cr); - - int bdr_top = 0; - int bdr_bottom = 0; - int bdr_left = 0; - int bdr_right = 0; - - if(borders.top.width != 0 && borders.top.style > litehtml::border_style_hidden) - { - bdr_top = (int) borders.top.width; - } - if(borders.bottom.width != 0 && borders.bottom.style > litehtml::border_style_hidden) - { - bdr_bottom = (int) borders.bottom.width; - } - if(borders.left.width != 0 && borders.left.style > litehtml::border_style_hidden) - { - bdr_left = (int) borders.left.width; - } - if(borders.right.width != 0 && borders.right.style > litehtml::border_style_hidden) - { - bdr_right = (int) borders.right.width; - } - - // draw right border - if(bdr_right) - { - set_color(cr, borders.right.color); - - if(borders.radius.top_right_x && borders.radius.top_right_y) - { - double end_angle = 2 * M_PI; - double start_angle = end_angle - M_PI / 2.0 / ((double) bdr_top / (double) bdr_right + 1); - - add_path_arc(cr, - draw_pos.right() - borders.radius.top_right_x, - draw_pos.top() + borders.radius.top_right_y, - borders.radius.top_right_x - bdr_right, - borders.radius.top_right_y - bdr_right + (bdr_right - bdr_top), - end_angle, - start_angle, true); - - add_path_arc(cr, - draw_pos.right() - borders.radius.top_right_x, - draw_pos.top() + borders.radius.top_right_y, - borders.radius.top_right_x, - borders.radius.top_right_y, - start_angle, - end_angle, false); - } else - { - cairo_move_to(cr, draw_pos.right() - bdr_right, draw_pos.top() + bdr_top); - cairo_line_to(cr, draw_pos.right(), draw_pos.top()); - } - - if(borders.radius.bottom_right_x && borders.radius.bottom_right_y) - { - cairo_line_to(cr, draw_pos.right(), draw_pos.bottom() - borders.radius.bottom_right_y); - - double start_angle = 0; - double end_angle = start_angle + M_PI / 2.0 / ((double) bdr_bottom / (double) bdr_right + 1); - - add_path_arc(cr, - draw_pos.right() - borders.radius.bottom_right_x, - draw_pos.bottom() - borders.radius.bottom_right_y, - borders.radius.bottom_right_x, - borders.radius.bottom_right_y, - start_angle, - end_angle, false); - - add_path_arc(cr, - draw_pos.right() - borders.radius.bottom_right_x, - draw_pos.bottom() - borders.radius.bottom_right_y, - borders.radius.bottom_right_x - bdr_right, - borders.radius.bottom_right_y - bdr_right + (bdr_right - bdr_bottom), - end_angle, - start_angle, true); - } else - { - cairo_line_to(cr, draw_pos.right(), draw_pos.bottom()); - cairo_line_to(cr, draw_pos.right() - bdr_right, draw_pos.bottom() - bdr_bottom); - } - - cairo_fill(cr); - } - - // draw bottom border - if(bdr_bottom) - { - set_color(cr, borders.bottom.color); - - if(borders.radius.bottom_left_x && borders.radius.bottom_left_y) - { - double start_angle = M_PI / 2.0; - double end_angle = start_angle + M_PI / 2.0 / ((double) bdr_left / (double) bdr_bottom + 1); - - add_path_arc(cr, - draw_pos.left() + borders.radius.bottom_left_x, - draw_pos.bottom() - borders.radius.bottom_left_y, - borders.radius.bottom_left_x - bdr_bottom + (bdr_bottom - bdr_left), - borders.radius.bottom_left_y - bdr_bottom, - start_angle, - end_angle, false); - - add_path_arc(cr, - draw_pos.left() + borders.radius.bottom_left_x, - draw_pos.bottom() - borders.radius.bottom_left_y, - borders.radius.bottom_left_x, - borders.radius.bottom_left_y, - end_angle, - start_angle, true); - } else - { - cairo_move_to(cr, draw_pos.left(), draw_pos.bottom()); - cairo_line_to(cr, draw_pos.left() + bdr_left, draw_pos.bottom() - bdr_bottom); - } - - if(borders.radius.bottom_right_x && borders.radius.bottom_right_y) - { - cairo_line_to(cr, draw_pos.right() - borders.radius.bottom_right_x, draw_pos.bottom()); - - double end_angle = M_PI / 2.0; - double start_angle = end_angle - M_PI / 2.0 / ((double) bdr_right / (double) bdr_bottom + 1); - - add_path_arc(cr, - draw_pos.right() - borders.radius.bottom_right_x, - draw_pos.bottom() - borders.radius.bottom_right_y, - borders.radius.bottom_right_x, - borders.radius.bottom_right_y, - end_angle, - start_angle, true); - - add_path_arc(cr, - draw_pos.right() - borders.radius.bottom_right_x, - draw_pos.bottom() - borders.radius.bottom_right_y, - borders.radius.bottom_right_x - bdr_bottom + (bdr_bottom - bdr_right), - borders.radius.bottom_right_y - bdr_bottom, - start_angle, - end_angle, false); - } else - { - cairo_line_to(cr, draw_pos.right() - bdr_right, draw_pos.bottom() - bdr_bottom); - cairo_line_to(cr, draw_pos.right(), draw_pos.bottom()); - } - - cairo_fill(cr); - } - - // draw top border - if(bdr_top) - { - set_color(cr, borders.top.color); - - if(borders.radius.top_left_x && borders.radius.top_left_y) - { - double end_angle = M_PI * 3.0 / 2.0; - double start_angle = end_angle - M_PI / 2.0 / ((double) bdr_left / (double) bdr_top + 1); - - add_path_arc(cr, - draw_pos.left() + borders.radius.top_left_x, - draw_pos.top() + borders.radius.top_left_y, - borders.radius.top_left_x, - borders.radius.top_left_y, - end_angle, - start_angle, true); - - add_path_arc(cr, - draw_pos.left() + borders.radius.top_left_x, - draw_pos.top() + borders.radius.top_left_y, - borders.radius.top_left_x - bdr_top + (bdr_top - bdr_left), - borders.radius.top_left_y - bdr_top, - start_angle, - end_angle, false); - } else - { - cairo_move_to(cr, draw_pos.left(), draw_pos.top()); - cairo_line_to(cr, draw_pos.left() + bdr_left, draw_pos.top() + bdr_top); - } - - if(borders.radius.top_right_x && borders.radius.top_right_y) - { - cairo_line_to(cr, draw_pos.right() - borders.radius.top_right_x, draw_pos.top() + bdr_top); - - double start_angle = M_PI * 3.0 / 2.0; - double end_angle = start_angle + M_PI / 2.0 / ((double) bdr_right / (double) bdr_top + 1); - - add_path_arc(cr, - draw_pos.right() - borders.radius.top_right_x, - draw_pos.top() + borders.radius.top_right_y, - borders.radius.top_right_x - bdr_top + (bdr_top - bdr_right), - borders.radius.top_right_y - bdr_top, - start_angle, - end_angle, false); - - add_path_arc(cr, - draw_pos.right() - borders.radius.top_right_x, - draw_pos.top() + borders.radius.top_right_y, - borders.radius.top_right_x, - borders.radius.top_right_y, - end_angle, - start_angle, true); - } else - { - cairo_line_to(cr, draw_pos.right() - bdr_right, draw_pos.top() + bdr_top); - cairo_line_to(cr, draw_pos.right(), draw_pos.top()); - } - - cairo_fill(cr); - } - - // draw left border - if(bdr_left) - { - set_color(cr, borders.left.color); - - if(borders.radius.top_left_x && borders.radius.top_left_y) - { - double start_angle = M_PI; - double end_angle = start_angle + M_PI / 2.0 / ((double) bdr_top / (double) bdr_left + 1); - - add_path_arc(cr, - draw_pos.left() + borders.radius.top_left_x, - draw_pos.top() + borders.radius.top_left_y, - borders.radius.top_left_x - bdr_left, - borders.radius.top_left_y - bdr_left + (bdr_left - bdr_top), - start_angle, - end_angle, false); - - add_path_arc(cr, - draw_pos.left() + borders.radius.top_left_x, - draw_pos.top() + borders.radius.top_left_y, - borders.radius.top_left_x, - borders.radius.top_left_y, - end_angle, - start_angle, true); - } else - { - cairo_move_to(cr, draw_pos.left() + bdr_left, draw_pos.top() + bdr_top); - cairo_line_to(cr, draw_pos.left(), draw_pos.top()); - } - - if(borders.radius.bottom_left_x && borders.radius.bottom_left_y) - { - cairo_line_to(cr, draw_pos.left(), draw_pos.bottom() - borders.radius.bottom_left_y); - - double end_angle = M_PI; - double start_angle = end_angle - M_PI / 2.0 / ((double) bdr_bottom / (double) bdr_left + 1); - - add_path_arc(cr, - draw_pos.left() + borders.radius.bottom_left_x, - draw_pos.bottom() - borders.radius.bottom_left_y, - borders.radius.bottom_left_x, - borders.radius.bottom_left_y, - end_angle, - start_angle, true); - - add_path_arc(cr, - draw_pos.left() + borders.radius.bottom_left_x, - draw_pos.bottom() - borders.radius.bottom_left_y, - borders.radius.bottom_left_x - bdr_left, - borders.radius.bottom_left_y - bdr_left + (bdr_left - bdr_bottom), - start_angle, - end_angle, false); - } else - { - cairo_line_to(cr, draw_pos.left(), draw_pos.bottom()); - cairo_line_to(cr, draw_pos.left() + bdr_left, draw_pos.bottom() - bdr_bottom); - } - - cairo_fill(cr); - } - cairo_restore(cr); -} - -void container_linux::transform_text(litehtml::string& text, litehtml::text_transform tt) -{ - -} - -void container_linux::set_clip( const litehtml::position& pos, const litehtml::border_radiuses& bdr_radius ) -{ - m_clips.emplace_back(pos, bdr_radius); -} - -void container_linux::del_clip() -{ - if(!m_clips.empty()) - { - m_clips.pop_back(); - } -} - -void container_linux::apply_clip( cairo_t* cr ) -{ - for(const auto& clip_box : m_clips) - { - rounded_rectangle(cr, clip_box.box, clip_box.radius); - cairo_clip(cr); - } -} - -void container_linux::draw_ellipse( cairo_t* cr, int x, int y, int width, int height, const litehtml::web_color& color, int line_width ) -{ - if(!cr || !width || !height) return; - cairo_save(cr); - - apply_clip(cr); - - cairo_new_path(cr); - - cairo_translate (cr, x + width / 2.0, y + height / 2.0); - cairo_scale (cr, width / 2.0, height / 2.0); - cairo_arc (cr, 0, 0, 1, 0, 2 * M_PI); - - set_color(cr, color); - cairo_set_line_width(cr, line_width); - cairo_stroke(cr); - - cairo_restore(cr); -} - -void container_linux::fill_ellipse( cairo_t* cr, int x, int y, int width, int height, const litehtml::web_color& color ) -{ - if(!cr || !width || !height) return; - cairo_save(cr); - - apply_clip(cr); - - cairo_new_path(cr); - - cairo_translate (cr, x + width / 2.0, y + height / 2.0); - cairo_scale (cr, width / 2.0, height / 2.0); - cairo_arc (cr, 0, 0, 1, 0, 2 * M_PI); - - set_color(cr, color); - cairo_fill(cr); - - cairo_restore(cr); -} - -void container_linux::clear_images() -{ -/* for(images_map::iterator i = m_images.begin(); i != m_images.end(); i++) - { - if(i->second) - { - delete i->second; - } - } - m_images.clear(); -*/ -} - -const char* container_linux::get_default_font_name() const -{ - return "Times New Roman"; -} - -std::shared_ptr container_linux::create_element(const char *tag_name, - const litehtml::string_map &attributes, - const std::shared_ptr &doc) -{ - return nullptr; -} - -void container_linux::rounded_rectangle( cairo_t* cr, const litehtml::position &pos, const litehtml::border_radiuses &radius ) -{ - cairo_new_path(cr); - if(radius.top_left_x && radius.top_left_y) - { - add_path_arc(cr, - pos.left() + radius.top_left_x, - pos.top() + radius.top_left_y, - radius.top_left_x, - radius.top_left_y, - M_PI, - M_PI * 3.0 / 2.0, false); - } else - { - cairo_move_to(cr, pos.left(), pos.top()); - } - - cairo_line_to(cr, pos.right() - radius.top_right_x, pos.top()); - - if(radius.top_right_x && radius.top_right_y) - { - add_path_arc(cr, - pos.right() - radius.top_right_x, - pos.top() + radius.top_right_y, - radius.top_right_x, - radius.top_right_y, - M_PI * 3.0 / 2.0, - 2.0 * M_PI, false); - } - - cairo_line_to(cr, pos.right(), pos.bottom() - radius.bottom_right_x); - - if(radius.bottom_right_x && radius.bottom_right_y) - { - add_path_arc(cr, - pos.right() - radius.bottom_right_x, - pos.bottom() - radius.bottom_right_y, - radius.bottom_right_x, - radius.bottom_right_y, - 0, - M_PI / 2.0, false); - } - - cairo_line_to(cr, pos.left() - radius.bottom_left_x, pos.bottom()); - - if(radius.bottom_left_x && radius.bottom_left_y) - { - add_path_arc(cr, - pos.left() + radius.bottom_left_x, - pos.bottom() - radius.bottom_left_y, - radius.bottom_left_x, - radius.bottom_left_y, - M_PI / 2.0, - M_PI, false); - } -} - -void container_linux::draw_pixbuf(cairo_t* cr, const Glib::RefPtr& bmp, int x, int y, int cx, int cy) -{ - cairo_save(cr); - - { - Cairo::RefPtr crobj(new Cairo::Context(cr, false)); - - cairo_matrix_t flib_m; - cairo_matrix_init(&flib_m, 1, 0, 0, -1, 0, 0); - - if(cx != bmp->get_width() || cy != bmp->get_height()) - { - Glib::RefPtr new_img = bmp->scale_simple(cx, cy, Gdk::INTERP_BILINEAR); - Gdk::Cairo::set_source_pixbuf(crobj, new_img, x, y); - crobj->paint(); - } else - { - Gdk::Cairo::set_source_pixbuf(crobj, bmp, x, y); - crobj->paint(); - } - } - - cairo_restore(cr); -} - -cairo_surface_t* container_linux::surface_from_pixbuf(const Glib::RefPtr& bmp) -{ - cairo_surface_t* ret; - - if(bmp->get_has_alpha()) - { - ret = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, bmp->get_width(), bmp->get_height()); - } else - { - ret = cairo_image_surface_create(CAIRO_FORMAT_RGB24, bmp->get_width(), bmp->get_height()); - } - - Cairo::RefPtr surface(new Cairo::Surface(ret, false)); - Cairo::RefPtr ctx = Cairo::Context::create(surface); - Gdk::Cairo::set_source_pixbuf(ctx, bmp, 0.0, 0.0); - ctx->paint(); - - return ret; -} - -void container_linux::get_media_features(litehtml::media_features& media) const -{ - litehtml::position client; - get_client_rect(client); - media.type = litehtml::media_type_screen; - media.width = client.width; - media.height = client.height; - media.device_width = Gdk::screen_width(); - media.device_height = Gdk::screen_height(); - media.color = 8; - media.monochrome = 0; - media.color_index = 256; - media.resolution = 96; -} - -void container_linux::get_language(litehtml::string& language, litehtml::string& culture) const -{ - language = "en"; - culture = ""; -} - -void container_linux::link(const std::shared_ptr &ptr, const litehtml::element::ptr& el) -{ - -} diff --git a/containers/test/Bitmap.cpp b/containers/test/Bitmap.cpp index 862d4844e..fc85b5bc7 100644 --- a/containers/test/Bitmap.cpp +++ b/containers/test/Bitmap.cpp @@ -1,24 +1,30 @@ #include "Bitmap.h" #include "lodepng.h" -using namespace std; +#include "canvas_ity.hpp" +using namespace canvas_ity; -web_color Bitmap::get_pixel(int x, int y) const +Bitmap::Bitmap(canvas& canvas) : Bitmap(canvas.width(), canvas.height()) +{ + canvas.get_image_data((byte*)data.data(), width, height, width * 4, 0, 0); +} + +color Bitmap::get_pixel(int x, int y) const { if (x < 0 || x >= width || y < 0 || y >= height) - return web_color::black; + return black; else return data[x + y * width]; } -void Bitmap::set_pixel(int x, int y, web_color color) +void Bitmap::set_pixel(int x, int y, color color) { if (x < 0 || x >= width || y < 0 || y >= height) return; - if (color.alpha == 0) return; + if (color.a == 0) return; data[x + y * width] = color; } // endpoint is not drawn, like in GDI -void Bitmap::draw_line(int x0, int y0, int x1, int y1, web_color color) +void Bitmap::draw_line(int x0, int y0, int x1, int y1, color color) { if (x0 != x1 && y0 != y1) return; // only horz and vert lines supported @@ -36,29 +42,22 @@ void Bitmap::draw_line(int x0, int y0, int x1, int y1, web_color color) } } -void Bitmap::draw_rect(int x, int y, int width, int height, web_color color) +void Bitmap::draw_rect(int x, int y, int _width, int _height, color color) { - draw_line(x, y, x + width, y, color); // top - draw_line(x, y + height - 1, x + width, y + height - 1, color); // bottom - draw_line(x, y, x, y + height, color); // left - draw_line(x + width - 1, y, x + width - 1, y + height, color); // right + draw_line(x, y, x + _width, y, color); // top + draw_line(x, y + _height - 1, x + _width, y + _height - 1, color); // bottom + draw_line(x, y, x, y + _height, color); // left + draw_line(x + _width - 1, y, x + _width - 1, y + _height, color); // right } -void Bitmap::fill_rect(position rect, web_color color) +void Bitmap::fill_rect(rect rect, color color) { for (int y = rect.top(); y < rect.bottom(); y++) for (int x = rect.left(); x < rect.right(); x++) set_pixel(x, y, color); } -void Bitmap::draw_bitmap(int x0, int y0, const Bitmap& bmp) -{ - for (int y = 0; y < bmp.height; y++) - for (int x = 0; x < bmp.width; x++) - set_pixel(x0 + x, y0 + y, bmp.get_pixel(x, y)); -} - -void Bitmap::replace_color(web_color original, web_color replacement) +void Bitmap::replace_color(color original, color replacement) { for (auto& pixel : data) { @@ -68,7 +67,7 @@ void Bitmap::replace_color(web_color original, web_color replacement) } // find minimal rectangle containing pixels different from bgcolor -position Bitmap::find_picture(web_color bgcolor) +rect Bitmap::find_picture(color bgcolor) { auto horz_line_empty = [&](int y) { for (int x = 0; x < width; x++) @@ -81,33 +80,21 @@ position Bitmap::find_picture(web_color bgcolor) return true; }; - position pos; + rect rect; int y; for (y = 0; y < height && horz_line_empty(y); y++); - if (y == height) return pos; // no picture - pos.y = y; + if (y == height) return rect; // no picture + rect.y = y; for (y = height - 1; y >= 0 && horz_line_empty(y); y--); - pos.height = y + 1 - pos.y; + rect.height = y + 1 - rect.y; int x; for (x = 0; x < width && vert_line_empty(x); x++); - pos.x = x; + rect.x = x; for (x = width - 1; x >= 0 && vert_line_empty(x); x--); - pos.width = x + 1 - pos.x; + rect.width = x + 1 - rect.x; - return pos; -} - -void Bitmap::resize(int new_width, int new_height) -{ - vector new_data(new_width * new_height, web_color::white); - for (int y = 0; y < min(new_height, height); y++) - for (int x = 0; x < min(new_width, width); x++) - new_data[x + y * new_width] = data[x + y * width]; - - width = new_width; - height = new_height; - data = new_data; + return rect; } void Bitmap::load(string filename) @@ -127,3 +114,20 @@ void Bitmap::save(string filename) { lodepng::encode(filename, (byte*)data.data(), width, height); } + +// This function can be used to compare gradient rendering between different browsers. +byte max_color_diff(const Bitmap& a, const Bitmap& b) +{ + if (a.width != b.width || a.height != b.height) + return 255; + + int diff = 0; + for (int y = 0; y < a.height; y++) + for (int x = 0; x < a.width; x++) + { + color A = a.get_pixel(x, y); + color B = b.get_pixel(x, y); + diff = max({diff, abs(A.r - B.r), abs(A.g - B.g), abs(A.b - B.b), abs(A.a - B.a)}); + } + return (byte)diff; +} \ No newline at end of file diff --git a/containers/test/Bitmap.h b/containers/test/Bitmap.h index bb66d190b..99b3987d9 100644 --- a/containers/test/Bitmap.h +++ b/containers/test/Bitmap.h @@ -1,15 +1,35 @@ +#pragma once #include using namespace litehtml; +using rect = position; +namespace canvas_ity { class canvas; } +using namespace canvas_ity; + +// color != web_color because sizeof(web_color) != 4 +struct color +{ + byte r, g, b, a; + + color() : r(0), g(0), b(0), a(0) {} + color(byte r, byte g, byte b, byte a) : r(r), g(g), b(b), a(a) {} + color(web_color c) : r(c.red), g(c.green), b(c.blue), a(c.alpha) {} + + bool operator==(color c) const { return r == c.r && g == c.g && b == c.b && a == c.a; } + bool operator!=(color c) const { return !(*this == c); } +}; +const color white(255,255,255,255); +const color black(0,0,0,255); +const color transparent(0,0,0,0); class Bitmap { public: int width = 0; int height = 0; - std::vector data; + vector data; Bitmap() {} - Bitmap(int width, int height, web_color color = web_color::white) : width(width), height(height) + Bitmap(int width, int height, color color = white) : width(width), height(height) { data.resize(width * height, color); } @@ -17,20 +37,22 @@ class Bitmap { load(filename); } + Bitmap(canvas& canvas); bool operator==(const Bitmap& bmp) const { return width == bmp.width && height == bmp.height && data == bmp.data; } bool operator!=(const Bitmap& bmp) const { return !(*this == bmp); } + operator bool() const { return width != 0 && height != 0; } - web_color get_pixel(int x, int y) const; - void set_pixel(int x, int y, web_color color); - void draw_line(int x0, int y0, int x1, int y1, web_color color); - void draw_rect(int x, int y, int width, int height, web_color color); - void fill_rect(position rect, web_color color); - void draw_bitmap(int x, int y, const Bitmap& bmp); - void replace_color(web_color original, web_color replacement); + color get_pixel(int x, int y) const; + void set_pixel(int x, int y, color color); + void draw_line(int x0, int y0, int x1, int y1, color color); + void draw_rect(int x, int y, int width, int height, color color); + void fill_rect(rect rect, color color); + void replace_color(color original, color replacement); - position find_picture(web_color bgcolor = web_color::white); - void resize(int new_width, int new_height); + rect find_picture(color bgcolor = white); void load(string filename); void save(string filename); -}; \ No newline at end of file +}; + +byte max_color_diff(const Bitmap& a, const Bitmap& b); \ No newline at end of file diff --git a/containers/test/Font.cpp b/containers/test/Font.cpp index 82ae36c68..b2b3d9771 100644 --- a/containers/test/Font.cpp +++ b/containers/test/Font.cpp @@ -1,53 +1,78 @@ #define _CRT_SECURE_NO_WARNINGS #include "Font.h" +#include "canvas_ity.hpp" +using namespace canvas_ity; string readfile(string filename); -using namespace std; +void draw_image(canvas& cvs, int x, int y, const Bitmap& bmp); +bool set_font(canvas& cvs, const string& raw_font_data, int pixel_size); +void set_color(canvas& cvs, brush_type type, color c); -string Font::font_dir = "../containers/test/fonts/"; // ctest is run from litehtml/build -Font::size_name Font::installed_fonts[] = +RasterFont::size_name RasterFont::installed_fonts[] = { - { 12, "terminus-ascii-bold-12px.yaff" }, - { 14, "terminus-ascii-bold-14px.yaff" }, - { 16, "terminus-ascii-bold-16px.yaff" }, - { 18, "terminus-ascii-bold-18px.yaff" }, - { 20, "terminus-ascii-bold-20px.yaff" }, - { 22, "terminus-ascii-bold-22px.yaff" }, - { 24, "terminus-ascii-bold-24px.yaff" }, - { 28, "terminus-ascii-bold-28px.yaff" }, - { 32, "terminus-ascii-bold-32px.yaff" }, - { 0 } + { 12, "terminus-12px.yaff" }, + { 14, "terminus-14px.yaff" }, + { 16, "terminus-16px.yaff" }, + { 18, "terminus-18px.yaff" }, + { 20, "terminus-20px.yaff" }, + { 22, "terminus-22px.yaff" }, + { 24, "terminus-24px.yaff" }, + { 28, "terminus-28px.yaff" }, + { 32, "terminus-32px.yaff" }, + { 0, "" } }; -Font::Font(int size) +string get_font_dir() +{ + string font_cpp = __FILE__; + auto i = font_cpp.find_last_of("\\/"); + return font_cpp.substr(0, i) + "/fonts/"; +} + +RasterFont::RasterFont(int size, int weight) { // find most suitable font - int min_diff = 1000; - int n = 0; - for (int i = 0; installed_fonts[i].size; i++) + string name; + if (size == 16 && weight == 700) + name = "terminus-16px-bold.yaff"; + else { - int diff = abs(installed_fonts[i].size - size); - if (diff < min_diff) + int min_diff = 1000; + int n = 0; + for (int i = 0; installed_fonts[i].size; i++) { - min_diff = diff; - n = i; + int diff = abs(installed_fonts[i].size - size); + if (diff < min_diff) + { + min_diff = diff; + n = i; + } } + name = installed_fonts[n].name; } - - load(font_dir + installed_fonts[n].name); + + load(get_font_dir() + name); +} + +RasterFont* RasterFont::create(string name, int size, int weight) +{ + if (lowcase(name) == "terminus") + return new RasterFont(size, weight); + + return nullptr; } -Bitmap Font::get_glyph(int ch, web_color color) +Bitmap RasterFont::get_glyph(int ch, color color) { - if (ch < 0 || ch >= 128 || glyphs[ch].width == 0) + if (glyphs[ch].width == 0) { - Bitmap bmp(width, height, web_color::transparent); + Bitmap bmp(width, height, transparent); bmp.draw_rect(1, 1, width - 2, height - 2, color); return bmp; } - else if (color != web_color::black) + else if (color != black) { Bitmap bmp = glyphs[ch]; - bmp.replace_color(web_color::black, color); + bmp.replace_color(black, color); return bmp; } else @@ -57,27 +82,25 @@ Bitmap Font::get_glyph(int ch, web_color color) } // load .yaff font file in an ad hoc manner (can't parse arbitrary yaff files) -void Font::load(string filename) +void RasterFont::load(string filename) { string text = readfile(filename); - - string_vector lines; - split_string(text, lines, "\n"); + string_vector lines = split_string(text, "\n", "", ""); int i; // parse header - for (i = 0; i < lines.size(); i++) + for (i = 0; i < (int)lines.size(); i++) { string line = lines[i]; trim(line); - if (line == "" || line[0] == '#') continue; // skip empty lines and comments + if (line.empty() || line[0] == '#') continue; // skip empty lines and comments auto sep = line.find(':'); - if (sep == -1) return; // line without ':' - error + if (sep == string::npos) return; // line without ':' - error auto key = line.substr(0, sep); trim(key); auto val = line.substr(sep + 1); trim(val); - if (val == "") break; // end of header + if (val.empty()) break; // end of header if (key == "cell-size") sscanf(val.c_str(), "%d %d", &width, &height); else if (key == "ascent") ascent = atoi(val.c_str()); @@ -89,38 +112,107 @@ void Font::load(string filename) // only u+NNNN: label is recognized, all others are skipped auto parse_key = [&]() { int ch = -1; - for (; i < lines.size(); i++) + for (; i < (int)lines.size(); i++) { string line = lines[i]; trim(line); - if (line == "") continue; - if (line.find(':') == -1) break; // start of glyph data + if (line.empty()) continue; + if (line.find(':') == string::npos) break; // start of glyph data if (line.substr(0, 2) == "u+") - sscanf(line.c_str(), "u+%X:", &ch); + sscanf(line.c_str(), "u+%X:", (unsigned int*) &ch); } return ch; }; auto parse_glyph = [&](int ch) { - Bitmap& glyph = glyphs[ch] = Bitmap(width, height, web_color::transparent); - for (int y = 0; i < lines.size() && y < height; i++, y++) + int glyph_width = (int)trim(lines[i]).size(); + Bitmap& glyph = glyphs[ch] = Bitmap(glyph_width, height, transparent); + for (int y = 0; i < (int)lines.size() && y < height; i++, y++) { - string line = lines[i]; - trim(line); - for (int x = 0; x < min((int)line.size(), width); x++) + string line = trim(lines[i]); + for (int x = 0; x < min((int)line.size(), glyph_width); x++) { if (line[x] == '@') - glyph.set_pixel(x, y, web_color::black); + glyph.set_pixel(x, y, black); } } }; - while (i < lines.size()) + while (i < (int)lines.size()) { int ch = parse_key(); - if (ch < 0 || ch >= 128) break; + if (ch < 0) break; parse_glyph(ch); } - x_height = glyphs['x'].find_picture(web_color::transparent).height; + x_height = glyphs[(int)'x'].find_picture(transparent).height; +} + +int RasterFont::text_width(string text) +{ + utf8_to_utf32 utf32(text); + int width_ = 0; + for (const char32_t* p = utf32; *p; p++) + width_ += get_glyph(*p, black).width; + return width_; +} + +void RasterFont::draw_text(canvas& cvs, string text, color color, int x, int y) +{ + utf8_to_utf32 utf32(text); + for (const char32_t* p = utf32; *p; p++) + { + Bitmap glyph = get_glyph(*p, color); + draw_image(cvs, x, y, glyph); + x += glyph.width; + } +} + +// keys must be in lowcase +string_map OutlineFont::installed_fonts = +{ + { "ahem", "ahem.ttf" } +}; + +OutlineFont* OutlineFont::create(string name, int size) +{ + lcase(name); + + if (installed_fonts.count(name)) + return new OutlineFont(name, size); + + return nullptr; +} + +OutlineFont::OutlineFont(string name, int size) : name(name), size(size) +{ + string filename = at(installed_fonts, name); + data = readfile(get_font_dir() + filename); + + canvas cvs; + set_font(cvs, data, size); + cvs.get_font_metrics(ascent, descent, height, x_height); +} + +int OutlineFont::text_width(string text) +{ + canvas cvs; + set_font(cvs, data, size); + return (int)ceil(cvs.measure_text(text.c_str())); +} + +void OutlineFont::draw_text(canvas& cvs, string text, color color, int x, int y) +{ + set_font(cvs, data, size); + cvs.text_baseline = top; + set_color(cvs, fill_style, color); + cvs.fill_text(text.c_str(), (float)x, (float)y); +} + +Font* Font::create(string name, int size, int weight) +{ + if (RasterFont* font = RasterFont::create(name, size, weight)) + return font; + + return OutlineFont::create(name, size); } diff --git a/containers/test/Font.h b/containers/test/Font.h index 4f3c01297..fe665176c 100644 --- a/containers/test/Font.h +++ b/containers/test/Font.h @@ -1,19 +1,41 @@ #include "Bitmap.h" -class Font +class Font : public font_metrics { public: - int width = 0; - int height = 0; - int ascent = 0; - int descent = 0; - int x_height = 0; - Bitmap glyphs[128]; - - static string font_dir; + static Font* create(string face, int size, int weight); + virtual int text_width(string text) = 0; + virtual void draw_text(canvas& canvas, string text, color color, int x, int y) = 0; +}; + +class RasterFont : public Font +{ + int width = 0; // for absent glyphs, see get_glyph + std::map glyphs; +public: + RasterFont(int size, int weight); + static struct size_name { int size; string name; } installed_fonts[]; - - Font(int size); - Bitmap get_glyph(int ch, web_color color); + static RasterFont* create(string face, int size, int weight); + + Bitmap get_glyph(int ch, color color); void load(string filename); -}; \ No newline at end of file + + int text_width(string text) override; + void draw_text(canvas& canvas, string text, color color, int x, int y) override; +}; + +class OutlineFont : public Font +{ + string name; + string data; // raw contents of ttf file + int size = 0; // pixels per em +public: + OutlineFont(string name, int size); + + static string_map installed_fonts; + static OutlineFont* create(string name, int size); + + int text_width(string text) override; + void draw_text(canvas& canvas, string text, color color, int x, int y) override; +}; diff --git a/containers/test/canvas_ity.hpp b/containers/test/canvas_ity.hpp new file mode 100644 index 000000000..7241ff080 --- /dev/null +++ b/containers/test/canvas_ity.hpp @@ -0,0 +1,3627 @@ +// canvas_ity v1.00 -- ISC license +// Copyright (c) 2022 Andrew Kensler +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +// ======== ABOUT ======== +// +// This is a tiny, single-header C++ library for rasterizing immediate-mode +// 2D vector graphics, closely modeled on the basic W3C (not WHATWG) HTML5 2D +// canvas specification (https://www.w3.org/TR/2015/REC-2dcontext-20151119/). +// +// The priorities for this library are high-quality rendering, ease of use, +// and compact size. Speed is important too, but secondary to the other +// priorities. Notably, this library takes an opinionated approach and +// does not provide options for trading off quality for speed. +// +// Despite its small size, it supports nearly everything listed in the W3C +// HTML5 2D canvas specification, except for hit regions and getting certain +// properties. The main differences lie in the surface-level API to make this +// easier for C++ use, while the underlying implementation is carefully based +// on the specification. In particular, stroke, fill, gradient, pattern, +// image, and font styles are specified slightly differently (avoiding strings +// and auxiliary classes). Nonetheless, the goal is that this library could +// produce a conforming HTML5 2D canvas implementation if wrapped in a thin +// layer of JavaScript bindings. See the accompanying C++ automated test +// suite and its HTML5 port for a mapping between the APIs and a comparison +// of this library's rendering output against browser canvas implementations. + +// ======== FEATURES ======== +// +// High-quality rendering: +// +// - Trapezoidal area antialiasing provides very smooth antialiasing, even +// when lines are nearly horizontal or vertical. +// - Gamma-correct blending, interpolation, and resampling are used +// throughout. It linearizes all colors and premultiplies alpha on +// input and converts back to unpremultiplied sRGB on output. This +// reduces muddiness on many gradients (e.g., red to green), makes line +// thicknesses more perceptually uniform, and avoids dark fringes when +// interpolating opacity. +// - Bicubic convolution resampling is used whenever it needs to resample a +// pattern or image. This smoothly interpolates with less blockiness when +// magnifying, and antialiases well when minifying. It can simultaneously +// magnify and minify along different axes. +// - Ordered dithering is used on output. This reduces banding on subtle +// gradients while still being compression-friendly. +// - High curvature is handled carefully in line joins. Thick lines are +// drawn correctly as though tracing with a wide pen nib, even where +// the lines curve sharply. (Simpler curve offsetting approaches tend +// to show bite-like artifacts in these cases.) +// +// Ease of use: +// +// - Provided as a single-header library with no dependencies beside the +// standard C++ library. There is nothing to link, and it even includes +// built-in binary parsing for TrueType font (TTF) files. It is pure CPU +// code and does not require a GPU or GPU context. +// - Has extensive Doxygen-style documentation comments for the public API. +// - Compiles cleanly at moderately high warning levels on most compilers. +// - Shares no internal pointers, nor holds any external pointers. Newcomers +// to C++ can have fun drawing with this library without worrying so much +// about resource lifetimes or mutability. +// - Uses no static or global variables. Threads may safely work with +// different canvas instances concurrently without locking. +// - Allocates no dynamic memory after reaching the high-water mark. Except +// for the pixel buffer, flat std::vector instances embedded in the canvas +// instance handle all dynamic memory. This reduces fragmentation and +// makes it easy to change the code to reserve memory up front or even +// to use statically allocated vectors. +// - Works with exceptions and RTTI disabled. +// - Intentionally uses a plain C++03 style to make it as widely portable +// as possible, easier to understand, and (with indexing preferred over +// pointer arithmetic) easier to port natively to other languages. The +// accompanying test suite may also help with porting. +// +// Compact size: +// +// - The source code for the entire library consists of a bit over 2300 lines +// (not counting comments or blanks), each no longer than 78 columns. +// Alternately measured, it has fewer than 1300 semicolons. +// - The object code for the library can be less than 36 KiB on x86-64 with +// appropriate compiler settings for size. +// - Due to the library's small size, the accompanying automated test suite +// achieves 100% line coverage of the library in gcov and llvm-cov. + +// ======== LIMITATIONS ======== +// +// - Trapezoidal antialiasing overestimates coverage where paths self- +// intersect within a single pixel. Where inner joins are visible, this +// can lead to a "grittier" appearance due to the extra windings used. +// - Clipping uses an antialiased sparse pixel mask rather than geometrically +// intersecting paths. Therefore, it is not subpixel-accurate. +// - Text rendering is extremely basic and mainly for convenience. It only +// supports left-to-right text, and does not do any hinting, kerning, +// ligatures, text shaping, or text layout. If you require any of those, +// consider using another library to provide those and feed the results +// to this library as either placed glyphs or raw paths. +// - TRUETYPE FONT PARSING IS NOT SECURE! It does some basic validity +// checking, but should only be used with known-good or sanitized fonts. +// - Parameter checking does not test for non-finite floating-point values. +// - Rendering is single-threaded, not explicitly vectorized, and not GPU- +// accelerated. It also copies data to avoid ownership issues. If you +// need the speed, you are better off using a more fully-featured library. +// - The library does no input or output on its own. Instead, you must +// provide it with buffers to copy into or out of. + +// ======== USAGE ======== +// +// This is a single-header library. You may freely include it in any of +// your source files to declare the canvas_ity namespace and its members. +// However, to get the implementation, you must +// #define CANVAS_ITY_IMPLEMENTATION +// in exactly one C++ file before including this header. +// +// Then, construct an instance of the canvas_ity::canvas class with the pixel +// dimensions that you want and draw into it using any of the various drawing +// functions. You can then use the get_image_data() function to retrieve the +// currently drawn image at any time. +// +// See each of the public member function and data member (i.e., method +// and field) declarations for the full API documentation. Also see the +// accompanying C++ automated test suite for examples of the usage of each +// public member, and the test suite's HTML5 port for how these map to the +// HTML5 canvas API. + +#ifndef CANVAS_ITY_HPP +#define CANVAS_ITY_HPP + +#include +#include +#include + +namespace canvas_ity +{ + +// Public API enums +enum composite_operation { + source_in = 1, source_copy, source_out, destination_in, + destination_atop = 7, + // confusing name, 'lighter' should be called 'plus' + // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_plus + // RED_out = ALPHA_src * RED_src + ALPHA_dst * RED_dst + // ALPHA_out = ALPHA_src + ALPHA_dst + // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation + // "Where both shapes overlap, the color is determined by adding color values." + lighter = 10, + destination_over, destination_out, + source_atop, source_over, exclusive_or }; +enum cap_style { butt, square, circle }; +enum join_style { miter, bevel, rounded }; +enum brush_type { fill_style, stroke_style }; +enum repetition_style { repeat, repeat_x, repeat_y, no_repeat }; +enum align_style { leftward, rightward, center, start = 0, ending }; +enum baseline_style { + alphabetic, top, middle, bottom, hanging, ideographic = 3 }; + +// Implementation details +struct xy { float x, y; xy(); xy( float, float ); }; +struct rgba { float r, g, b, a; rgba(); rgba( float, float, float, float ); }; +struct affine_matrix { float a, b, c, d, e, f; }; +struct paint_brush { enum types { color, pattern, linear, radial, css_radial, conic } type; + std::vector< rgba > colors; std::vector< float > stops; std::vector< std::optional > hints; + xy start, end; float start_radius, end_radius; xy css_radius; float angle; + int width, height; repetition_style repetition; }; +struct font_face { std::vector< unsigned char > data; + int cmap, glyf, head, hhea, hmtx, loca, maxp, os_2; + float scale; }; +struct subpath_data { size_t count; bool closed; }; +struct bezier_path { std::vector< xy > points; + std::vector< subpath_data > subpaths; }; +struct line_path { std::vector< xy > points; + std::vector< subpath_data > subpaths; }; +struct pixel_run { unsigned short x, y; float delta; }; +typedef std::vector< pixel_run > pixel_runs; + +class canvas +{ +public: + + // ======== LIFECYCLE ======== + + /// @brief Construct a new canvas. + /// + /// It will begin with all pixels set to transparent black. Initially, + /// the visible coordinates will run from (0, 0) in the upper-left to + /// (width, height) in the lower-right and with pixel centers offset + /// (0.5, 0.5) from the integer grid, though all this may be changed + /// by transforms. The sizes must be between 1 and 32768, inclusive. + /// + /// @param width horizontal size of the new canvas in pixels + /// @param height vertical size of the new canvas in pixels + /// + canvas( + int width, + int height ); + + canvas( + int width, + int height, + rgba color); + + canvas() : canvas(0, 0) {} + + /// @brief Destroy the canvas and release all associated memory. + /// + ~canvas(); + + // ======== TRANSFORMS ======== + + /// @brief Scale the current transform. + /// + /// Scaling may be non-uniform if the x and y scaling factors are + /// different. When a scale factor is less than one, things will be + /// shrunk in that direction. When a scale factor is greater than + /// one, they will grow bigger. Negative scaling factors will flip or + /// mirror it in that direction. The scaling factors must be non-zero. + /// If either is zero, most drawing operations will do nothing. + /// + /// @param x horizontal scaling factor + /// @param y vertical scaling factor + /// + void scale( + float x, + float y ); + + /// @brief Rotate the current transform. + /// + /// The rotation is applied clockwise in a direction around the origin. + /// + /// Tip: to rotate around another point, first translate that point to + /// the origin, then do the rotation, and then translate back. + /// + /// @param angle clockwise angle in radians + /// + void rotate( + float angle ); + + /// @brief Translate the current transform. + /// + /// By default, positive x values shift that many pixels to the right, + /// while negative y values shift left, positive y values shift up, and + /// negative y values shift down. + /// + /// @param x amount to shift horizontally + /// @param y amount to shift vertically + /// + void translate( + float x, + float y ); + + /// @brief Add an arbitrary transform to the current transform. + /// + /// This takes six values for the upper two rows of a homogenous 3x3 + /// matrix (i.e., {{a, c, e}, {b, d, f}, {0.0, 0.0, 1.0}}) describing an + /// arbitrary affine transform and appends it to the current transform. + /// The values can represent any affine transform such as scaling, + /// rotation, translation, or skew, or any composition of affine + /// transforms. The matrix must be invertible. If it is not, most + /// drawing operations will do nothing. + /// + /// @param a horizontal scaling factor (m11) + /// @param b vertical skewing (m12) + /// @param c horizontal skewing (m21) + /// @param d vertical scaling factor (m22) + /// @param e horizontal translation (m31) + /// @param f vertical translation (m32) + /// + void transform( + float a, + float b, + float c, + float d, + float e, + float f ); + + /// @brief Replace the current transform. + /// + /// This takes six values for the upper two rows of a homogenous 3x3 + /// matrix (i.e., {{a, c, e}, {b, d, f}, {0.0, 0.0, 1.0}}) describing + /// an arbitrary affine transform and replaces the current transform + /// with it. The values can represent any affine transform such as + /// scaling, rotation, translation, or skew, or any composition of + /// affine transforms. The matrix must be invertible. If it is not, + /// most drawing operations will do nothing. + /// + /// Tip: to reset the current transform back to the default, use + /// 1.0, 0.0, 0.0, 1.0, 0.0, 0.0. + /// + /// @param a horizontal scaling factor (m11) + /// @param b vertical skewing (m12) + /// @param c horizontal skewing (m21) + /// @param d vertical scaling factor (m22) + /// @param e horizontal translation (m31) + /// @param f vertical translation (m32) + /// + void set_transform( + float a, + float b, + float c, + float d, + float e, + float f ); + + // ======== COMPOSITING ======== + + /// @brief Set the degree of opacity applied to all drawing operations. + /// + /// If an operation already uses a transparent color, this can make it + /// yet more transparent. It must be in the range from 0.0 for fully + /// transparent to 1.0 for fully opaque, inclusive. If it is not, this + /// does nothing. Defaults to 1.0 (opaque). + /// + /// @param alpha degree of opacity applied to all drawing operations + /// + void set_global_alpha( + float alpha ); + + /// @brief Compositing operation for blending new drawing and old pixels. + /// + /// The source_copy, source_in, source_out, destination_atop, and + /// destination_in operations may clear parts of the canvas outside the + /// new drawing but within the clip region. Defaults to source_over. + /// + /// source_atop: Show new over old where old is opaque. + /// source_copy: Replace old with new. + /// source_in: Replace old with new where old was opaque. + /// source_out: Replace old with new where old was transparent. + /// source_over: Show new over old. + /// destination_atop: Show old over new where new is opaque. + /// destination_in: Clear old where new is transparent. + /// destination_out: Clear old where new is opaque. + /// destination_over: Show new under old. + /// exclusive_or: Show new and old but clear where both are opaque. + /// lighter: Sum the new with the old. + /// + composite_operation global_composite_operation; + + // ======== SHADOWS ======== + + /// @brief Set the color and opacity of the shadow. + /// + /// Most drawing operations can optionally draw a blurred drop shadow + /// before doing the main drawing. The shadow is modulated by the opacity + /// of the drawing and will be blended into the existing pixels subject to + /// the compositing settings and clipping region. Shadows will only be + /// drawn if the shadow color has any opacity and the shadow is either + /// offset or blurred. The color and opacity values will be clamped to + /// the 0.0 to 1.0 range, inclusive. Defaults to 0.0, 0.0, 0.0, 0.0 + /// (transparent black). + /// + /// @param red sRGB red component of the shadow color + /// @param green sRGB green component of the shadow color + /// @param blue sRGB blue component of the shadow color + /// @param alpha opacity of the shadow (not premultiplied) + /// + void set_shadow_color( + float red, + float green, + float blue, + float alpha ); + + /// @brief Horizontal offset of the shadow in pixels. + /// + /// Negative shifts left, positive shifts right. This is not affected + /// by the current transform. Defaults to 0.0 (no offset). + /// + float shadow_offset_x; + + /// @brief Vertical offset of the shadow in pixels. + /// + /// Negative shifts up, positive shifts down. This is not affected by + /// the current transform. Defaults to 0.0 (no offset). + /// + float shadow_offset_y; + + /// @brief Set the level of Gaussian blurring on the shadow. + /// + /// Zero produces no blur, while larger values will blur the shadow + /// more strongly. This is not affected by the current transform. + /// Must be non-negative. If it is not, this does nothing. Defaults to + /// 0.0 (no blur). + /// + /// @param level the level of Gaussian blurring on the shadow + /// + void set_shadow_blur( + float level ); + + // ======== LINE STYLES ======== + + /// @brief Set the width of the lines when stroking. + /// + /// Initially this is measured in pixels, though the current transform + /// at the time of drawing can affect this. Must be positive. If it + /// is not, this does nothing. Defaults to 1.0. + /// + /// @param width width of the lines when stroking + /// + void set_line_width( + float width ); + + /// @brief Cap style for the ends of open subpaths and dash segments. + /// + /// The actual shape may be affected by the current transform at the time + /// of drawing. Only affects stroking. Defaults to butt. + /// + /// butt: Use a flat cap flush to the end of the line. + /// square: Use a half-square cap that extends past the end of the line. + /// circle: Use a semicircular cap. + /// + cap_style line_cap; + + /// @brief Join style for connecting lines within the paths. + /// + /// The actual shape may be affected by the current transform at the time + /// of drawing. Only affects stroking. Defaults to miter. + /// + /// miter: Continue the ends until they intersect, if within miter limit. + /// bevel: Connect the ends with a flat triangle. + /// round: Join the ends with a circular arc. + /// + join_style line_join; + + /// @brief Set the limit on maximum pointiness allowed for miter joins. + /// + /// If the distance from the point where the lines intersect to the + /// point where the outside edges of the join intersect exceeds this + /// ratio relative to the line width, then a bevel join will be used + /// instead, and the miter will be lopped off. Larger values allow + /// pointier miters. Only affects stroking and only when the line join + /// style is miter. Must be positive. If it is not, this does nothing. + /// Defaults to 10.0. + /// + /// @param limit the limit on maximum pointiness allowed for miter joins + /// + void set_miter_limit( + float limit ); + + /// @brief Offset where each subpath starts the dash pattern. + /// + /// Changing this shifts the location of the dashes along the path and + /// animating it will produce a marching ants effect. Only affects + /// stroking and only when a dash pattern is set. May be negative or + /// exceed the length of the dash pattern, in which case it will wrap. + /// Defaults to 0.0. + /// + float line_dash_offset; + + /// @brief Set or clear the line dash pattern. + /// + /// Takes an array with entries alternately giving the lengths of dash + /// and gap segments. All must be non-negative; if any are not, this + /// does nothing. These will be used to draw with dashed lines when + /// stroking, with each subpath starting at the length along the dash + /// pattern indicated by the line dash offset. Initially these lengths + /// are measured in pixels, though the current transform at the time of + /// drawing can affect this. The count must be non-negative. If it is + /// odd, the array will be appended to itself to make an even count. If + /// it is zero, or the pointer is null, the dash pattern will be cleared + /// and strokes will be drawn as solid lines. The array is copied and + /// it is safe to change or destroy it after this call. Defaults to + /// solid lines. + /// + /// @param segments pointer to array for dash pattern + /// @param count number of entries in the array + /// + void set_line_dash( + float const *segments, + int count ); + + // ======== FILL AND STROKE STYLES ======== + + /// @brief Set filling or stroking to use a constant color and opacity. + /// + /// The color and opacity values will be clamped to the 0.0 to 1.0 range, + /// inclusive. Filling and stroking defaults a constant color with 0.0, + /// 0.0, 0.0, 1.0 (opaque black). + /// + /// @param type whether to set the fill_style or stroke_style + /// @param red sRGB red component of the painting color + /// @param green sRGB green component of the painting color + /// @param blue sRGB blue component of the painting color + /// @param alpha opacity to paint with (not premultiplied) + /// + void set_color( + brush_type type, + float red, + float green, + float blue, + float alpha ); + + void set_color( + brush_type type, + rgba c) + { + set_color(type, c.r, c.g, c.b, c.a); + } + + /// @brief Set filling or stroking to use a linear gradient. + /// + /// Positions the start and end points of the gradient and clears all + /// color stops to reset the gradient to transparent black. Color stops + /// can then be added again. When drawing, pixels will be painted with + /// the color of the gradient at the nearest point on the line segment + /// between the start and end points. This is affected by the current + /// transform at the time of drawing. + /// + /// @param type whether to set the fill_style or stroke_style + /// @param start_x horizontal coordinate of the start of the gradient + /// @param start_y vertical coordinate of the start of the gradient + /// @param end_x horizontal coordinate of the end of the gradient + /// @param end_y vertical coordinate of the end of the gradient + /// + void set_linear_gradient( + brush_type type, + float start_x, + float start_y, + float end_x, + float end_y ); + + /// @brief Set filling or stroking to use a radial gradient. + /// + /// Positions the start and end circles of the gradient and clears all + /// color stops to reset the gradient to transparent black. Color stops + /// can then be added again. When drawing, pixels will be painted as + /// though the starting circle moved and changed size linearly to match + /// the ending circle, while sweeping through the colors of the gradient. + /// Pixels not touched by the moving circle will be left transparent + /// black. The radial gradient is affected by the current transform + /// at the time of drawing. The radii must be non-negative. + /// + /// @param type whether to set the fill_style or stroke_style + /// @param start_x horizontal starting coordinate of the circle + /// @param start_y vertical starting coordinate of the circle + /// @param start_radius starting radius of the circle + /// @param end_x horizontal ending coordinate of the circle + /// @param end_y vertical ending coordinate of the circle + /// @param end_radius ending radius of the circle + /// + void set_radial_gradient( + brush_type type, + float start_x, + float start_y, + float start_radius, + float end_x, + float end_y, + float end_radius ); + + void set_css_radial_gradient( + brush_type type, + float x, + float y, + float radius_x, + float radius_y); + + void set_conic_gradient( + brush_type type, + float x, + float y, + float angle); + + /// @brief Add a color stop to a linear or radial gradient. + /// + /// Each color stop has an offset which defines its position from 0.0 at + /// the start of the gradient to 1.0 at the end. Colors and opacity are + /// linearly interpolated along the gradient between adjacent pairs of + /// stops without premultiplying the alpha. If more than one stop is + /// added for a given offset, the first one added is considered closest + /// to 0.0 and the last is closest to 1.0. If no stop is at offset 0.0 + /// or 1.0, the stops with the closest offsets will be extended. If no + /// stops are added, the gradient will be fully transparent black. Set a + /// new linear or radial gradient to clear all the stops and redefine the + /// gradient colors. Color stops may be added to a gradient at any time. + /// The color and opacity values will be clamped to the 0.0 to 1.0 range, + /// inclusive. The offset must be in the 0.0 to 1.0 range, inclusive. + /// If it is not, or if chosen style type is not currently set to a + /// gradient, this does nothing. + /// + /// @param type whether to add to the fill_style or stroke_style + /// @param offset position of the color stop along the gradient + /// @param red sRGB red component of the color stop + /// @param green sRGB green component of the color stop + /// @param blue sRGB blue component of the color stop + /// @param alpha opacity of the color stop (not premultiplied) + /// + void add_color_stop( + brush_type type, + float offset, + float red, + float green, + float blue, + float alpha, + std::optional hint = {} ); + + /// @brief Set filling or stroking to draw with an image pattern. + /// + /// Initially, pixels in the pattern correspond exactly to pixels on the + /// canvas, with the pattern starting in the upper left. The pattern + /// is affected by the current transform at the time of drawing, and + /// the pattern will be resampled as needed (with the filtering always + /// wrapping regardless of the repetition setting). The pattern can be + /// repeated either horizontally, vertically, both, or neither, relative + /// to the source image. If the pattern is not repeated, then beyond it + /// will be considered transparent black. The pattern image, which should + /// be in top to bottom rows of contiguous pixels from left to right, + /// is copied and it is safe to change or destroy it after this call. + /// The width and height must both be positive. If either are not, or + /// the image pointer is null, this does nothing. + /// + /// Tip: to use a small piece of a larger image, reduce the width and + /// height, and offset the image pointer while keeping the stride. + /// + /// @param type whether to set the fill_style or stroke_style + /// @param image pointer to unpremultiplied sRGB RGBA8 image data + /// @param width width of the pattern image in pixels + /// @param height height of the pattern image in pixels + /// @param stride number of bytes between the start of each image row + /// @param repetition repeat, repeat_x, repeat_y, or no_repeat + /// + void set_pattern( + brush_type type, + unsigned char const *image, + int width, + int height, + int stride, + repetition_style repetition ); + + // ======== BUILDING PATHS ======== + + /// @brief Reset the current path. + /// + /// The current path and all subpaths will be cleared after this, and a + /// new path can be built. + /// + void begin_path(); + + /// @brief Create a new subpath. + /// + /// The given point will become the first point of the new subpath and + /// is subject to the current transform at the time this is called. + /// + /// @param x horizontal coordinate of the new first point + /// @param y vertical coordinate of the new first point + /// + void move_to( + float x, + float y ); + + /// @brief Close the current subpath. + /// + /// Adds a straight line from the end of the current subpath back to its + /// first point and marks the subpath as closed so that this line will + /// join with the beginning of the path at this point. A new, empty + /// subpath will be started beginning with the same first point. If the + /// current path is empty, this does nothing. + /// + void close_path(); + + /// @brief Extend the current subpath with a straight line. + /// + /// The line will go from the current end point (if the current path is + /// not empty) to the given point, which will become the new end point. + /// Its position is affected by the current transform at the time that + /// this is called. If the current path was empty, this is equivalent + /// to just a move. + /// + /// @param x horizontal coordinate of the new end point + /// @param y vertical coordinate of the new end point + /// + void line_to( + float x, + float y ); + + /// @brief Extend the current subpath with a quadratic Bezier curve. + /// + /// The curve will go from the current end point (or the control point + /// if the current path is empty) to the given point, which will become + /// the new end point. The curve will be tangent toward the control + /// point at both ends. The current transform at the time that this + /// is called will affect both points passed in. + /// + /// Tip: to make multiple curves join smoothly, ensure that each new end + /// point is on a line between the adjacent control points. + /// + /// @param control_x horizontal coordinate of the control point + /// @param control_y vertical coordinate of the control point + /// @param x horizontal coordinate of the new end point + /// @param y vertical coordinate of the new end point + /// + void quadratic_curve_to( + float control_x, + float control_y, + float x, + float y ); + + /// @brief Extend the current subpath with a cubic Bezier curve. + /// + /// The curve will go from the current end point (or the first control + /// point if the current path is empty) to the given point, which will + /// become the new end point. The curve will be tangent toward the first + /// control point at the beginning and tangent toward the second control + /// point at the end. The current transform at the time that this is + /// called will affect all points passed in. + /// + /// Tip: to make multiple curves join smoothly, ensure that each new end + /// point is on a line between the adjacent control points. + /// + /// @param control_1_x horizontal coordinate of the first control point + /// @param control_1_y vertical coordinate of the first control point + /// @param control_2_x horizontal coordinate of the second control point + /// @param control_2_y vertical coordinate of the second control point + /// @param x horizontal coordinate of the new end point + /// @param y vertical coordinate of the new end point + /// + void bezier_curve_to( + float control_1_x, + float control_1_y, + float control_2_x, + float control_2_y, + float x, + float y ); + + /// @brief Extend the current subpath with an arc tangent to two lines. + /// + /// The arc is from the circle with the given radius tangent to both + /// the line from the current end point to the vertex, and to the line + /// from the vertex to the given point. A straight line will be added + /// from the current end point to the first tangent point (unless the + /// current path is empty), then the shortest arc from the first to the + /// second tangent points will be added. The second tangent point will + /// become the new end point. If the radius is large, these tangent + /// points may fall outside the line segments. The current transform + /// at the time that this is called will affect the passed in points + /// and the arc. If the current path was empty, this is equivalent to + /// a move. If the arc would be degenerate, it is equivalent to a line + /// to the vertex point. The radius must be non-negative. If it is not, + /// or if the current transform is not invertible, this does nothing. + /// + /// Tip: to draw a polygon with rounded corners, call this once for each + /// vertex and pass the midpoint of the adjacent edge as the second + /// point; this works especially well for rounded boxes. + /// + /// @param vertex_x horizontal coordinate where the tangent lines meet + /// @param vertex_y vertical coordinate where the tangent lines meet + /// @param x a horizontal coordinate on the second tangent line + /// @param y a vertical coordinate on the second tangent line + /// @param radius radius of the circle containing the arc + /// + void arc_to( + float vertex_x, + float vertex_y, + float x, + float y, + float radius ); + + /// @brief Extend the current subpath with an arc between two angles. + /// + /// The arc is from the circle centered at the given point and with the + /// given radius. A straight line will be added from the current end + /// point to the starting point of the arc (unless the current path is + /// empty), then the arc along the circle from the starting angle to the + /// ending angle in the given direction will be added. If they are more + /// than two pi radians apart in the given direction, the arc will stop + /// after one full circle. The point at the ending angle will become + /// the new end point of the path. Initially, the angles are clockwise + /// relative to the x-axis. The current transform at the time that + /// this is called will affect the passed in point, angles, and arc. + /// The radius must be non-negative. + /// + /// @param x horizontal coordinate of the circle center + /// @param y vertical coordinate of the circle center + /// @param radius radius of the circle containing the arc + /// @param start_angle radians clockwise from x-axis to begin + /// @param end_angle radians clockwise from x-axis to end + /// @param counter_clockwise true if the arc turns counter-clockwise + /// + void arc( + float x, + float y, + float radius, + float start_angle, + float end_angle, + bool counter_clockwise = false ); + + /// @brief Add a closed subpath in the shape of a rectangle. + /// + /// The rectangle has one corner at the given point and then goes in the + /// direction along the width before going in the direction of the height + /// towards the opposite corner. The current transform at the time that + /// this is called will affect the given point and rectangle. The width + /// and/or the height may be negative or zero, and this can affect the + /// winding direction. + /// + /// @param x horizontal coordinate of a rectangle corner + /// @param y vertical coordinate of a rectangle corner + /// @param width width of the rectangle + /// @param height height of the rectangle + /// + void rectangle( + float x, + float y, + float width, + float height ); + + void polygon(std::vector points); + + // ======== DRAWING PATHS ======== + + /// @brief Draw the interior of the current path using the fill style. + /// + /// Interior pixels are determined by the non-zero winding rule, with + /// all open subpaths implicitly closed by a straight line beforehand. + /// If shadows have been enabled by setting the shadow color with any + /// opacity and either offsetting or blurring the shadows, then the + /// shadows of the filled areas will be drawn first, followed by the + /// filled areas themselves. Both will be blended into the canvas + /// according to the global alpha, the global compositing operation, + /// and the clip region. If the fill style is a gradient or a pattern, + /// it will be affected by the current transform. The current path is + /// left unchanged by filling; begin a new path to clear it. If the + /// current transform is not invertible, this does nothing. + /// + void fill(); + + /// @brief Draw the edges of the current path using the stroke style. + /// + /// Edges of the path will be expanded into strokes according to the + /// current dash pattern, dash offset, line width, line join style + /// (and possibly miter limit), line cap, and transform. If shadows + /// have been enabled by setting the shadow color with any opacity and + /// either offsetting or blurring the shadows, then the shadow will be + /// drawn for the stroked lines first, then the stroked lines themselves. + /// Both will be blended into the canvas according to the global alpha, + /// the global compositing operation, and the clip region. If the stroke + /// style is a gradient or a pattern, it will be affected by the current + /// transform. The current path is left unchanged by stroking; begin a + /// new path to clear it. If the current transform is not invertible, + /// this does nothing. + /// + /// Tip: to draw with a calligraphy-like angled brush effect, add a + /// non-uniform scale transform just before stroking. + /// + void stroke(); + + /// @brief Restrict the clip region by the current path. + /// + /// Intersects the current clip region with the interior of the current + /// path (the region that would be filled), and replaces the current + /// clip region with this intersection. Subsequent calls to clip can + /// only reduce this further. When filling or stroking, only pixels + /// within the current clip region will change. The current path is + /// left unchanged by updating the clip region; begin a new path to + /// clear it. Defaults to the entire canvas. + /// + /// Tip: to be able to reset the current clip region, save the canvas + /// state first before clipping then restore the state to reset it. + /// + void clip(); + + /// @brief Tests whether a point is in or on the current path. + /// + /// Interior areas are determined by the non-zero winding rule, with + /// all open subpaths treated as implicitly closed by a straight line + /// beforehand. Points exactly on the boundary are also considered + /// inside. The point to test is interpreted without being affected by + /// the current transform, nor is the clip region considered. The current + /// path is left unchanged by this test. + /// + /// @param x horizontal coordinate of the point to test + /// @param y vertical coordinate of the point to test + /// @return true if the point is in or on the current path + /// + bool is_point_in_path( + float x, + float y ); + + // ======== DRAWING RECTANGLES ======== + + /// @brief Clear a rectangular area back to transparent black. + /// + /// The clip region may limit the area cleared. The current path is not + /// affected by this clearing. The current transform at the time that + /// this is called will affect the given point and rectangle. The width + /// and/or the height may be negative or zero. If either is zero, or the + /// current transform is not invertible, this does nothing. + /// + /// @param x horizontal coordinate of a rectangle corner + /// @param y vertical coordinate of a rectangle corner + /// @param width width of the rectangle + /// @param height height of the rectangle + /// + void clear_rectangle( + float x, + float y, + float width, + float height ); + + /// @brief Fill a rectangular area. + /// + /// This behaves as though the current path were reset to a single + /// rectangle and then filled as usual. However, the current path is + /// not actually changed. The current transform at the time that this is + /// called will affect the given point and rectangle. The width and/or + /// the height may be negative but not zero. If either is zero, or the + /// current transform is not invertible, this does nothing. + /// + /// @param x horizontal coordinate of a rectangle corner + /// @param y vertical coordinate of a rectangle corner + /// @param width width of the rectangle + /// @param height height of the rectangle + /// + void fill_rectangle( + float x, + float y, + float width, + float height ); + + /// @brief Stroke a rectangular area. + /// + /// This behaves as though the current path were reset to a single + /// rectangle and then stroked as usual. However, the current path is + /// not actually changed. The current transform at the time that this + /// is called will affect the given point and rectangle. The width + /// and/or the height may be negative or zero. If both are zero, or + /// the current transform is not invertible, this does nothing. If only + /// one is zero, this behaves as though it strokes a single horizontal or + /// vertical line. + /// + /// @param x horizontal coordinate of a rectangle corner + /// @param y vertical coordinate of a rectangle corner + /// @param width width of the rectangle + /// @param height height of the rectangle + /// + void stroke_rectangle( + float x, + float y, + float width, + float height ); + + // ======== DRAWING TEXT ======== + + /// @brief Horizontal position of the text relative to the anchor point. + /// + /// When drawing text, the positioning of the text relative to the anchor + /// point includes the side bearings of the first and last glyphs. + /// Defaults to leftward. + /// + /// leftward: Draw the text's left edge at the anchor point. + /// rightward: Draw the text's right edge at the anchor point. + /// center: Draw the text's horizontal center at the anchor point. + /// start: This is a synonym for leftward. + /// ending: This is a synonym for rightward. + /// + align_style text_align; + + /// @brief Vertical position of the text relative to the anchor point. + /// + /// Defaults to alphabetic. + /// + /// alphabetic: Draw with the alphabetic baseline at the anchor point. + /// top: Draw the top of the em box at the anchor point. + /// middle: Draw the exact middle of the em box at the anchor point. + /// bottom: Draw the bottom of the em box at the anchor point. + /// hanging: Draw 60% of an em over the baseline at the anchor point. + /// ideographic: This is a synonym for bottom. + /// + baseline_style text_baseline; + + /// @brief Set the font to use for text drawing. + /// + /// The font must be a TrueType font (TTF) file which has been loaded or + /// mapped into memory. Following some basic validation, the relevant + /// sections of the font file contents are copied, and it is safe to + /// change or destroy after this call. As an optimization, calling this + /// with either a null pointer or zero for the number of bytes will allow + /// for changing the size of the previous font without recopying from + /// the file. Note that the font parsing is not meant to be secure; + /// only use this with trusted TTF files! + /// + /// @param font pointer to the contents of a TrueType font (TTF) file + /// @param bytes number of bytes in the font file + /// @param size size in pixels per em to draw at + /// @return true if the font was set successfully + /// + bool set_font( + unsigned char const *font, + int bytes, + float size ); + + void get_font_metrics(int& ascent, int& descent, int& height, int& x_height); + + /// @brief Draw a line of text by filling its outline. + /// + /// This behaves as though the current path were reset to the outline + /// of the given text in the current font and size, positioned relative + /// to the given anchor point according to the current alignment and + /// baseline, and then filled as usual. However, the current path is + /// not actually changed. The current transform at the time that this + /// is called will affect the given anchor point and the text outline. + /// However, the comparison to the maximum width in pixels and the + /// condensing if needed is done before applying the current transform. + /// The maximum width (if given) must be positive. If it is not, or + /// the text pointer is null, or the font has not been set yet, or the + /// last setting of it was unsuccessful, or the current transform is not + /// invertible, this does nothing. + /// + /// @param text null-terminated UTF-8 string of text to fill + /// @param x horizontal coordinate of the anchor point + /// @param y vertical coordinate of the anchor point + /// @param maximum_width horizontal width to condense wider text to + /// + void fill_text( + char const *text, + float x, + float y, + float maximum_width = 1.0e30f ); + + /// @brief Draw a line of text by stroking its outline. + /// + /// This behaves as though the current path were reset to the outline + /// of the given text in the current font and size, positioned relative + /// to the given anchor point according to the current alignment and + /// baseline, and then stroked as usual. However, the current path is + /// not actually changed. The current transform at the time that this + /// is called will affect the given anchor point and the text outline. + /// However, the comparison to the maximum width in pixels and the + /// condensing if needed is done before applying the current transform. + /// The maximum width (if given) must be positive. If it is not, or + /// the text pointer is null, or the font has not been set yet, or the + /// last setting of it was unsuccessful, or the current transform is not + /// invertible, this does nothing. + /// + /// @param text null-terminated UTF-8 string of text to stroke + /// @param x horizontal coordinate of the anchor point + /// @param y vertical coordinate of the anchor point + /// @param maximum_width horizontal width to condense wider text to + /// + void stroke_text( + char const *text, + float x, + float y, + float maximum_width = 1.0e30f ); + + /// @brief Measure the width in pixels of a line of text. + /// + /// The measured width is the advance width, which includes the side + /// bearings of the first and last glyphs. However, text as drawn may + /// go outside this (e.g., due to glyphs that spill beyond their nominal + /// widths or stroked text with wide lines). Measurements ignore the + /// current transform. If the text pointer is null, or the font has + /// not been set yet, or the last setting of it was unsuccessful, this + /// returns zero. + /// + /// @param text null-terminated UTF-8 string of text to measure + /// @return width of the text in pixels + /// + float measure_text( + char const *text ); + + // ======== DRAWING IMAGES ======== + + /// @brief Draw an image onto the canvas. + /// + /// The position of the rectangle that the image is drawn to is affected + /// by the current transform at the time of drawing, and the image will + /// be resampled as needed (with the filtering always clamping to the + /// edges of the image). The drawing is also affected by the shadow, + /// global alpha, global compositing operation settings, and by the + /// clip region. The current path is not affected by drawing an image. + /// The image data, which should be in top to bottom rows of contiguous + /// pixels from left to right, is not retained and it is safe to change + /// or destroy it after this call. The width and height must both be + /// positive and the width and/or the height to scale to may be negative + /// but not zero. Otherwise, or if the image pointer is null, or the + /// current transform is not invertible, this does nothing. + /// + /// Tip: to use a small piece of a larger image, reduce the width and + /// height, and offset the image pointer while keeping the stride. + /// + /// @param image pointer to unpremultiplied sRGB RGBA8 image data + /// @param width width of the image in pixels + /// @param height height of the image in pixels + /// @param stride number of bytes between the start of each image row + /// @param x horizontal coordinate to draw the corner at + /// @param y vertical coordinate to draw the corner at + /// @param to_width width to scale the image to + /// @param to_height height to scale the image to + /// + void draw_image( + unsigned char const *image, + int width, + int height, + int stride, + float x, + float y, + float to_width, + float to_height ); + + // ======== PIXEL MANIPULATION ======== + + /// @brief Fetch a rectangle of pixels from the canvas to an image. + /// + /// This call is akin to a direct pixel-for-pixel copy from the canvas + /// buffer without resampling. The position and size of the canvas + /// rectangle is not affected by the current transform. The image data + /// is copied into, from top to bottom in rows of contiguous pixels from + /// left to right, and it is safe to change or destroy it after this call. + /// The requested rectangle may safely extend outside the bounds of the + /// canvas; these pixels will be set to transparent black. The width + /// and height must be positive. If not, or if the image pointer is + /// null, this does nothing. + /// + /// Tip: to copy into a section of a larger image, reduce the width and + /// height, and offset the image pointer while keeping the stride. + /// Tip: use this to get the rendered image from the canvas after drawing. + /// + /// @param image pointer to unpremultiplied sRGB RGBA8 image data + /// @param width width of the image in pixels + /// @param height height of the image in pixels + /// @param stride number of bytes between the start of each image row + /// @param x horizontal coordinate of upper-left pixel to fetch + /// @param y vertical coordinate of upper-left pixel to fetch + /// + void get_image_data( + unsigned char *image, + int width, + int height, + int stride, + int x, + int y ); + + /// @brief Replace a rectangle of pixels on the canvas with an image. + /// + /// This call is akin to a direct pixel-for-pixel copy into the canvas + /// buffer without resampling. The position and size of the canvas + /// rectangle is not affected by the current transform. Nor is the + /// result affected by the current settings for the global alpha, global + /// compositing operation, shadows, or the clip region. The image data, + /// which should be in top to bottom rows of contiguous pixels from left + /// to right, is copied from and it is safe to change or destroy it after + /// this call. The width and height must be positive. If not, or if the + /// image pointer is null, this does nothing. + /// + /// Tip: to copy from a section of a larger image, reduce the width and + /// height, and offset the image pointer while keeping the stride. + /// Tip: use this to prepopulate the canvas before drawing. + /// + /// @param image pointer to unpremultiplied sRGB RGBA8 image data + /// @param width width of the image in pixels + /// @param height height of the image in pixels + /// @param stride number of bytes between the start of each image row + /// @param x horizontal coordinate of upper-left pixel to replace + /// @param y vertical coordinate of upper-left pixel to replace + /// + void put_image_data( + unsigned char const *image, + int width, + int height, + int stride, + int x, + int y ); + + int width() { return size_x; } + int height() { return size_y; } + + // ======== CANVAS STATE ======== + + /// @brief Save the current state as though to a stack. + /// + /// The full state of the canvas is saved, except for the pixels in the + /// canvas buffer, and the current path. + /// + /// Tip: to be able to reset the current clip region, save the canvas + /// state first before clipping then restore the state to reset it. + /// + void save(); + + /// @brief Restore a previously saved state as though from a stack. + /// + /// This does not affect the pixels in the canvas buffer or the current + /// path. If the stack of canvas states is empty, this does nothing. + /// + void restore(); + +private: + int size_x; + int size_y; + affine_matrix forward; + affine_matrix inverse; + float global_alpha; + rgba shadow_color; + float shadow_blur; + std::vector< float > shadow; + float line_width; + float miter_limit; + std::vector< float > line_dash; + paint_brush fill_brush; + paint_brush stroke_brush; + paint_brush image_brush; + bezier_path path; + line_path lines; + line_path scratch; + pixel_runs runs; + pixel_runs mask; + font_face face; + rgba *bitmap; + canvas *saves; + canvas( canvas const & ); + canvas &operator=( canvas const & ); + void add_tessellation( xy, xy, xy, xy, float, int ); + void add_bezier( xy, xy, xy, xy, float ); + void path_to_lines( bool ); + void add_glyph( int, float ); + int character_to_glyph( char const *, int & ); + void text_to_lines( char const *, xy, float, bool ); + void dash_lines(); + void add_half_stroke( size_t, size_t, bool ); + void stroke_lines(); + void add_runs( xy, xy ); + void lines_to_runs( xy, int ); + rgba paint_pixel( xy, paint_brush const & ); + void render_shadow( paint_brush const & ); + void render_main( paint_brush const & ); +}; + +} + +#endif // CANVAS_ITY_HPP + +// ======== IMPLEMENTATION ======== +// +// The general internal data flow (albeit not control flow!) for rendering +// a shape onto the canvas is as follows: +// +// 1. Construct a set of polybeziers representing the current path via the +// public path building API (move_to, line_to, bezier_curve_to, etc.). +// 2. Adaptively tessellate the polybeziers into polylines (path_to_lines). +// 3. Maybe break the polylines into shorter polylines according to the dash +// pattern (dash_lines). +// 4. Maybe stroke expand the polylines into new polylines that can be filled +// to show the lines with width, joins, and caps (stroke_lines). +// 5. Scan-convert the polylines into a sparse representation of fractional +// pixel coverage (lines_to_runs). +// 6. Maybe paint the covered pixel span alphas "offscreen", blur, color, +// and blend them onto the canvas where not clipped (render_shadow). +// 7. Paint the covered pixel spans and blend them onto the canvas where not +// clipped (render_main). + +#ifdef CANVAS_ITY_IMPLEMENTATION +#define LINEARIZE_RGB 2 + +#include +#include +#include + +namespace canvas_ity +{ + +// 2D vector math operations +const float pi = 3.14159265f; +xy::xy() : x( 0.0f ), y( 0.0f ) {} +xy::xy( float new_x, float new_y ) : x( new_x ), y( new_y ) {} +static xy &operator+=( xy &left, xy right ) { + left.x += right.x; left.y += right.y; return left; } +static xy &operator-=( xy &left, xy right ) { + left.x -= right.x; left.y -= right.y; return left; } +static xy &operator*=( xy &left, float right ) { + left.x *= right; left.y *= right; return left; } +static xy const operator+( xy left, xy right ) { + return left += right; } +static xy const operator-( xy left, xy right ) { + return left -= right; } +static xy const operator*( float left, xy right ) { + return right *= left; } +static xy const operator*( affine_matrix const &left, xy right ) { + return xy( left.a * right.x + left.c * right.y + left.e, + left.b * right.x + left.d * right.y + left.f ); } +static float dot( xy left, xy right ) { + return left.x * right.x + left.y * right.y; } +static float length( xy that ) { + return sqrtf( dot( that, that ) ); } +static float direction( xy that ) { + return atan2f( that.y, that.x ); } +static xy const normalized( xy that ) { + return 1.0f / std::max( 1.0e-6f, length( that ) ) * that; } +static xy const perpendicular( xy that ) { + return xy( -that.y, that.x ); } +static xy const lerp( xy from, xy to, float ratio ) { + return from + ratio * ( to - from ); } +// ensure 0 <= angle < 360 +float normalize_angle(float angle) { + return fmodf(angle, 360) + (angle < 0 ? 360 : 0); +} + +// RGBA color operations +rgba::rgba() : r( 0.0f ), g( 0.0f ), b( 0.0f ), a( 0.0f ) {} +rgba::rgba( float new_r, float new_g, float new_b, float new_a ) + : r( new_r ), g( new_g ), b( new_b ), a( new_a ) {} +static rgba &operator+=( rgba &left, rgba right ) { + left.r += right.r; left.g += right.g; left.b += right.b; + left.a += right.a; return left; } +//static rgba &operator-=( rgba &left, rgba right ) { +// left.r -= right.r; left.g -= right.g; left.b -= right.b; +// left.a -= right.a; return left; } +static rgba &operator*=( rgba &left, float right ) { + left.r *= right; left.g *= right; left.b *= right; + left.a *= right; return left; } +static rgba const operator+( rgba left, rgba right ) { + return left += right; } +//static rgba const operator-( rgba left, rgba right ) { +// return left -= right; } +static rgba const operator*( float left, rgba right ) { + return right *= left; } +#if (CANVAS_ITY_IMPLEMENTATION+0) & LINEARIZE_RGB +static float linearized( float value ) { + return value < 0.04045f ? value / 12.92f : + powf( ( value + 0.055f ) / 1.055f, 2.4f ); } +static float delinearized( float value ) { + return value < 0.0031308f ? 12.92f * value : + 1.055f * powf( value, 1.0f / 2.4f ) - 0.055f; } +#else +static float linearized(float value) { return value; } +static float delinearized(float value) { return value; } +#endif +static rgba const linearized( rgba that ) { + return rgba( linearized( that.r ), linearized( that.g ), + linearized( that.b ), that.a ); } +static rgba const premultiplied( rgba that ) { + return rgba( that.r * that.a, that.g * that.a, + that.b * that.a, that.a ); } +static rgba const delinearized( rgba that ) { + return rgba( delinearized( that.r ), delinearized( that.g ), + delinearized( that.b ), that.a ); } +static rgba const unpremultiplied( rgba that ) { + static float const threshold = 1.0f / 8160.0f; + return that.a < threshold ? rgba( 0.0f, 0.0f, 0.0f, 0.0f ) : + rgba( 1.0f / that.a * that.r, 1.0f / that.a * that.g, + 1.0f / that.a * that.b, that.a ); } +static rgba const clamped( rgba that ) { + return rgba( std::min( std::max( that.r, 0.0f ), 1.0f ), + std::min( std::max( that.g, 0.0f ), 1.0f ), + std::min( std::max( that.b, 0.0f ), 1.0f ), + std::min( std::max( that.a, 0.0f ), 1.0f ) ); } + +// Helpers for TTF file parsing +static int unsigned_8( std::vector< unsigned char > &data, int index ) { + return data[ static_cast< size_t >( index ) ]; } +static int signed_8( std::vector< unsigned char > &data, int index ) { + size_t place = static_cast< size_t >( index ); + return static_cast< signed char >( data[ place ] ); } +static int unsigned_16( std::vector< unsigned char > &data, int index ) { + size_t place = static_cast< size_t >( index ); + return data[ place ] << 8 | data[ place + 1 ]; } +static int signed_16( std::vector< unsigned char > &data, int index ) { + size_t place = static_cast< size_t >( index ); + return static_cast< short >( data[ place ] << 8 | data[ place + 1 ] ); } +static int signed_32( std::vector< unsigned char > &data, int index ) { + size_t place = static_cast< size_t >( index ); + return ( data[ place + 0 ] << 24 | data[ place + 1 ] << 16 | + data[ place + 2 ] << 8 | data[ place + 3 ] << 0 ); } + +// Tessellate (at low-level) a cubic Bezier curve and add it to the polyline +// data. This recursively splits the curve until two criteria are met +// (subject to a hard recursion depth limit). First, the control points +// must not be farther from the line between the endpoints than the tolerance. +// By the Bezier convex hull property, this ensures that the distance between +// the true curve and the polyline approximation will be no more than the +// tolerance. Secondly, it takes the cosine of an angular turn limit; the +// curve will be split until it turns less than this amount. This is used +// for stroking, with the angular limit chosen such that the sagita of an arc +// with that angle and a half-stroke radius will be equal to the tolerance. +// This keeps expanded strokes approximately within tolerance. Note that +// in the base case, it adds the control points as well as the end points. +// This way, stroke expansion infers the correct tangents from the ends of +// the polylines. +// +void canvas::add_tessellation( + xy point_1, + xy control_1, + xy control_2, + xy point_2, + float angular, + int limit ) +{ + static float const tolerance = 0.125f; + float flatness = tolerance * tolerance; + xy edge_1 = control_1 - point_1; + xy edge_2 = control_2 - control_1; + xy edge_3 = point_2 - control_2; + xy segment = point_2 - point_1; + float squared_1 = dot( edge_1, edge_1 ); + float squared_2 = dot( edge_2, edge_2 ); + float squared_3 = dot( edge_3, edge_3 ); + static float const epsilon = 1.0e-4f; + float length_squared = std::max( epsilon, dot( segment, segment ) ); + float projection_1 = dot( edge_1, segment ) / length_squared; + float projection_2 = dot( edge_3, segment ) / length_squared; + float clamped_1 = std::min( std::max( projection_1, 0.0f ), 1.0f ); + float clamped_2 = std::min( std::max( projection_2, 0.0f ), 1.0f ); + xy to_line_1 = point_1 + clamped_1 * segment - control_1; + xy to_line_2 = point_2 - clamped_2 * segment - control_2; + float cosine = 1.0f; + if ( angular > -1.0f ) + { + if ( squared_1 * squared_3 != 0.0f ) + cosine = dot( edge_1, edge_3 ) / sqrtf( squared_1 * squared_3 ); + else if ( squared_1 * squared_2 != 0.0f ) + cosine = dot( edge_1, edge_2 ) / sqrtf( squared_1 * squared_2 ); + else if ( squared_2 * squared_3 != 0.0f ) + cosine = dot( edge_2, edge_3 ) / sqrtf( squared_2 * squared_3 ); + } + if ( ( dot( to_line_1, to_line_1 ) <= flatness && + dot( to_line_2, to_line_2 ) <= flatness && + cosine >= angular ) || + !limit ) + { + if ( angular > -1.0f && squared_1 != 0.0f ) + lines.points.push_back( control_1 ); + if ( angular > -1.0f && squared_2 != 0.0f ) + lines.points.push_back( control_2 ); + if ( angular == -1.0f || squared_3 != 0.0f ) + lines.points.push_back( point_2 ); + return; + } + xy left_1 = lerp( point_1, control_1, 0.5f ); + xy middle = lerp( control_1, control_2, 0.5f ); + xy right_2 = lerp( control_2, point_2, 0.5f ); + xy left_2 = lerp( left_1, middle, 0.5f ); + xy right_1 = lerp( middle, right_2, 0.5f ); + xy split = lerp( left_2, right_1, 0.5f ); + add_tessellation( point_1, left_1, left_2, split, angular, limit - 1 ); + add_tessellation( split, right_1, right_2, point_2, angular, limit - 1 ); +} + +// Tessellate (at high-level) a cubic Bezier curve and add it to the polyline +// data. This solves both for the extreme in curvature and for the horizontal +// and vertical extrema. It then splits the curve into segments at these +// points and passes them off to the lower-level recursive tessellation. +// This preconditioning means the polyline exactly includes any cusps or +// ends of tight loops without the flatness test needing to locate it via +// bisection, and the angular limit test can use simple dot products without +// fear of curves turning more than 90 degrees. +// +void canvas::add_bezier( + xy point_1, + xy control_1, + xy control_2, + xy point_2, + float angular ) +{ + xy edge_1 = control_1 - point_1; + xy edge_2 = control_2 - control_1; + xy edge_3 = point_2 - control_2; + if ( dot( edge_1, edge_1 ) == 0.0f && + dot( edge_3, edge_3 ) == 0.0f ) + { + lines.points.push_back( point_2 ); + return; + } + float at[ 7 ] = { 0.0f, 1.0f }; + int cuts = 2; + xy extrema_a = -9.0f * edge_2 + 3.0f * ( point_2 - point_1 ); + xy extrema_b = 6.0f * ( point_1 + control_2 ) - 12.0f * control_1; + xy extrema_c = 3.0f * edge_1; + static float const epsilon = 1.0e-4f; + if ( fabsf( extrema_a.x ) > epsilon ) + { + float discriminant = + extrema_b.x * extrema_b.x - 4.0f * extrema_a.x * extrema_c.x; + if ( discriminant >= 0.0f ) + { + float sign = extrema_b.x > 0.0f ? 1.0f : -1.0f; + float term = -extrema_b.x - sign * sqrtf( discriminant ); + float extremum_1 = term / ( 2.0f * extrema_a.x ); + at[ cuts++ ] = extremum_1; + at[ cuts++ ] = extrema_c.x / ( extrema_a.x * extremum_1 ); + } + } + else if ( fabsf( extrema_b.x ) > epsilon ) + at[ cuts++ ] = -extrema_c.x / extrema_b.x; + if ( fabsf( extrema_a.y ) > epsilon ) + { + float discriminant = + extrema_b.y * extrema_b.y - 4.0f * extrema_a.y * extrema_c.y; + if ( discriminant >= 0.0f ) + { + float sign = extrema_b.y > 0.0f ? 1.0f : -1.0f; + float term = -extrema_b.y - sign * sqrtf( discriminant ); + float extremum_1 = term / ( 2.0f * extrema_a.y ); + at[ cuts++ ] = extremum_1; + at[ cuts++ ] = extrema_c.y / ( extrema_a.y * extremum_1 ); + } + } + else if ( fabsf( extrema_b.y ) > epsilon ) + at[ cuts++ ] = -extrema_c.y / extrema_b.y; + float determinant_1 = dot( perpendicular( edge_1 ), edge_2 ); + float determinant_2 = dot( perpendicular( edge_1 ), edge_3 ); + float determinant_3 = dot( perpendicular( edge_2 ), edge_3 ); + float curve_a = determinant_1 - determinant_2 + determinant_3; + float curve_b = -2.0f * determinant_1 + determinant_2; + if ( fabsf( curve_a ) > epsilon && + fabsf( curve_b ) > epsilon ) + at[ cuts++ ] = -0.5f * curve_b / curve_a; + for ( int index = 1; index < cuts; ++index ) + { + float value = at[ index ]; + int sorted = index - 1; + for ( ; 0 <= sorted && value < at[ sorted ]; --sorted ) + at[ sorted + 1 ] = at[ sorted ]; + at[ sorted + 1 ] = value; + } + xy split_point_1 = point_1; + for ( int index = 0; index < cuts - 1; ++index ) + { + if ( !( 0.0f <= at[ index ] && at[ index + 1 ] <= 1.0f && + at[ index ] != at[ index + 1 ] ) ) + continue; + float ratio = at[ index ] / at[ index + 1 ]; + xy partial_1 = lerp( point_1, control_1, at[ index + 1 ] ); + xy partial_2 = lerp( control_1, control_2, at[ index + 1 ] ); + xy partial_3 = lerp( control_2, point_2, at[ index + 1 ] ); + xy partial_4 = lerp( partial_1, partial_2, at[ index + 1 ] ); + xy partial_5 = lerp( partial_2, partial_3, at[ index + 1 ] ); + xy partial_6 = lerp( partial_1, partial_4, ratio ); + xy split_point_2 = lerp( partial_4, partial_5, at[ index + 1 ] ); + xy split_control_2 = lerp( partial_4, split_point_2, ratio ); + xy split_control_1 = lerp( partial_6, split_control_2, ratio ); + add_tessellation( split_point_1, split_control_1, + split_control_2, split_point_2, + angular, 20 ); + split_point_1 = split_point_2; + } +} + +// Convert the current path to a set of polylines. This walks over the +// complete set of subpaths in the current path (stored as sets of cubic +// Beziers) and converts each Bezier curve segment to a polyline while +// preserving information about where subpaths begin and end and whether +// they are closed or open. This replaces the previous polyline data. +// +void canvas::path_to_lines( + bool stroking ) +{ + static float const tolerance = 0.125f; + float ratio = tolerance / std::max( 0.5f * line_width, tolerance ); + float angular = stroking ? ( ratio - 2.0f ) * ratio * 2.0f + 1.0f : -1.0f; + lines.points.clear(); + lines.subpaths.clear(); + size_t index = 0; + size_t ending = 0; + for ( size_t subpath = 0; subpath < path.subpaths.size(); ++subpath ) + { + ending += path.subpaths[ subpath ].count; + size_t first = lines.points.size(); + xy point_1 = path.points[ index++ ]; + lines.points.push_back( point_1 ); + for ( ; index < ending; index += 3 ) + { + xy control_1 = path.points[ index + 0 ]; + xy control_2 = path.points[ index + 1 ]; + xy point_2 = path.points[ index + 2 ]; + add_bezier( point_1, control_1, control_2, point_2, angular ); + point_1 = point_2; + } + subpath_data entry = { + lines.points.size() - first, + path.subpaths[ subpath ].closed }; + lines.subpaths.push_back( entry ); + } +} + +// Add a text glyph directly to the polylines. Given a glyph index, this +// parses the data for that glyph directly from the TTF glyph data table and +// immediately tessellates it to a set of a polyline subpaths which it adds +// to any subpaths already present. It uses the current transform matrix to +// transform from the glyph's vertices in font units to the proper size and +// position on the canvas. +// +void canvas::add_glyph( + int glyph, + float angular ) +{ + int loc_format = unsigned_16( face.data, face.head + 50 ); + int offset = face.glyf + ( loc_format ? + signed_32( face.data, face.loca + glyph * 4 ) : + unsigned_16( face.data, face.loca + glyph * 2 ) * 2 ); + int next = face.glyf + ( loc_format ? + signed_32( face.data, face.loca + glyph * 4 + 4 ) : + unsigned_16( face.data, face.loca + glyph * 2 + 2 ) * 2 ); + if ( offset == next ) + return; + int contours = signed_16( face.data, offset ); + if ( contours < 0 ) + { + offset += 10; + for ( ; ; ) + { + int flags = unsigned_16( face.data, offset ); + int component = unsigned_16( face.data, offset + 2 ); + if ( !( flags & 2 ) ) + return; // Matching points are not supported + float e = static_cast< float >( flags & 1 ? + signed_16( face.data, offset + 4 ) : + signed_8( face.data, offset + 4 ) ); + float f = static_cast< float >( flags & 1 ? + signed_16( face.data, offset + 6 ) : + signed_8( face.data, offset + 5 ) ); + offset += flags & 1 ? 8 : 6; + float a = flags & 200 ? static_cast< float >( + signed_16( face.data, offset ) ) / 16384.0f : 1.0f; + float b = flags & 128 ? static_cast< float >( + signed_16( face.data, offset + 2 ) ) / 16384.0f : 0.0f; + float c = flags & 128 ? static_cast< float >( + signed_16( face.data, offset + 4 ) ) / 16384.0f : 0.0f; + float d = flags & 8 ? a : + flags & 64 ? static_cast< float >( + signed_16( face.data, offset + 2 ) ) / 16384.0f : + flags & 128 ? static_cast< float >( + signed_16( face.data, offset + 6 ) ) / 16384.0f : + 1.0f; + offset += flags & 8 ? 2 : flags & 64 ? 4 : flags & 128 ? 8 : 0; + affine_matrix saved_forward = forward; + affine_matrix saved_inverse = inverse; + transform( a, b, c, d, e, f ); + add_glyph( component, angular ); + forward = saved_forward; + inverse = saved_inverse; + if ( !( flags & 32 ) ) + return; + } + } + int hmetrics = unsigned_16( face.data, face.hhea + 34 ); + int left_side_bearing = glyph < hmetrics ? + signed_16( face.data, face.hmtx + glyph * 4 + 2 ) : + signed_16( face.data, face.hmtx + hmetrics * 2 + glyph * 2 ); + int x_min = signed_16( face.data, offset + 2 ); + int points = unsigned_16( face.data, offset + 8 + contours * 2 ) + 1; + int instructions = unsigned_16( face.data, offset + 10 + contours * 2 ); + int flags_array = offset + 12 + contours * 2 + instructions; + int flags_size = 0; + int x_size = 0; + for ( int index = 0; index < points; ) + { + int flags = unsigned_8( face.data, flags_array + flags_size++ ); + int repeated = flags & 8 ? + unsigned_8( face.data, flags_array + flags_size++ ) + 1 : 1; + x_size += repeated * ( flags & 2 ? 1 : flags & 16 ? 0 : 2 ); + index += repeated; + } + int x_array = flags_array + flags_size; + int y_array = x_array + x_size; + int x = left_side_bearing - x_min; + int y = 0; + int flags = 0; + int repeated = 0; + int index = 0; + for ( int contour = 0; contour < contours; ++contour ) + { + int beginning = index; + int ending = unsigned_16( face.data, offset + 10 + contour * 2 ); + xy begin_point = xy( 0.0f, 0.0f ); + bool begin_on = false; + xy end_point = xy( 0.0f, 0.0f ); + bool end_on = false; + size_t first = lines.points.size(); + for ( ; index <= ending; ++index ) + { + if ( repeated ) + --repeated; + else + { + flags = unsigned_8( face.data, flags_array++ ); + if ( flags & 8 ) + repeated = unsigned_8( face.data, flags_array++ ); + } + if ( flags & 2 ) + x += ( unsigned_8( face.data, x_array ) * + ( flags & 16 ? 1 : -1 ) ); + else if ( !( flags & 16 ) ) + x += signed_16( face.data, x_array ); + if ( flags & 4 ) + y += ( unsigned_8( face.data, y_array ) * + ( flags & 32 ? 1 : -1 ) ); + else if ( !( flags & 32 ) ) + y += signed_16( face.data, y_array ); + x_array += flags & 2 ? 1 : flags & 16 ? 0 : 2; + y_array += flags & 4 ? 1 : flags & 32 ? 0 : 2; + xy point = forward * xy( static_cast< float >( x ), + static_cast< float >( y ) ); + int on_curve = flags & 1; + if ( index == beginning ) + { + begin_point = point; + begin_on = on_curve; + if ( on_curve ) + lines.points.push_back( point ); + } + else + { + xy point_2 = on_curve ? point : + lerp( end_point, point, 0.5f ); + if ( lines.points.size() == first || + ( end_on && on_curve ) ) + lines.points.push_back( point_2 ); + else if ( !end_on || on_curve ) + { + xy point_1 = lines.points.back(); + xy control_1 = lerp( point_1, end_point, 2.0f / 3.0f ); + xy control_2 = lerp( point_2, end_point, 2.0f / 3.0f ); + add_bezier( point_1, control_1, control_2, point_2, + angular ); + } + } + end_point = point; + end_on = on_curve; + } + if ( begin_on ^ end_on ) + { + xy point_1 = lines.points.back(); + xy point_2 = lines.points[ first ]; + xy control = end_on ? begin_point : end_point; + xy control_1 = lerp( point_1, control, 2.0f / 3.0f ); + xy control_2 = lerp( point_2, control, 2.0f / 3.0f ); + add_bezier( point_1, control_1, control_2, point_2, angular ); + } + else if ( !begin_on && !end_on ) + { + xy point_1 = lines.points.back(); + xy split = lerp( begin_point, end_point, 0.5f ); + xy point_2 = lines.points[ first ]; + xy left_1 = lerp( point_1, end_point, 2.0f / 3.0f ); + xy left_2 = lerp( split, end_point, 2.0f / 3.0f ); + xy right_1 = lerp( split, begin_point, 2.0f / 3.0f ); + xy right_2 = lerp( point_2, begin_point, 2.0f / 3.0f ); + add_bezier( point_1, left_1, left_2, split, angular ); + add_bezier( split, right_1, right_2, point_2, angular ); + } + lines.points.push_back( lines.points[ first ] ); + subpath_data entry = { lines.points.size() - first, true }; + lines.subpaths.push_back( entry ); + } +} + +// Decode the next codepoint from a null-terminated UTF-8 string to its glyph +// index within the font. The index to the next codepoint in the string +// is advanced accordingly. It checks for valid UTF-8 encoding, but not +// for valid unicode codepoints. Where it finds an invalid encoding, it +// decodes it as the Unicode replacement character (U+FFFD) and advances to +// the invalid byte, per Unicode recommendation. It also replaces low-ASCII +// whitespace characters with regular spaces. After decoding the codepoint, +// it looks up the corresponding glyph index from the current font's character +// map table, returning a glyph index of 0 for the .notdef character (i.e., +// "tofu") if the font lacks a glyph for that codepoint. +// +int canvas::character_to_glyph( + char const *text, + int &index ) +{ + int bytes = ( ( text[ index ] & 0x80 ) == 0x00 ? 1 : + ( text[ index ] & 0xe0 ) == 0xc0 ? 2 : + ( text[ index ] & 0xf0 ) == 0xe0 ? 3 : + ( text[ index ] & 0xf8 ) == 0xf0 ? 4 : + 0 ); + int const masks[] = { 0x0, 0x7f, 0x1f, 0x0f, 0x07 }; + int codepoint = bytes ? text[ index ] & masks[ bytes ] : 0xfffd; + ++index; + while ( --bytes > 0 ) + if ( ( text[ index ] & 0xc0 ) == 0x80 ) + codepoint = codepoint << 6 | ( text[ index++ ] & 0x3f ); + else + { + codepoint = 0xfffd; + break; + } + if ( codepoint == '\t' || codepoint == '\v' || codepoint == '\f' || + codepoint == '\r' || codepoint == '\n' ) + codepoint = ' '; + int tables = unsigned_16( face.data, face.cmap + 2 ); + int format_12 = 0; + int format_4 = 0; + int format_0 = 0; + for ( int table = 0; table < tables; ++table ) + { + int platform = unsigned_16( face.data, face.cmap + table * 8 + 4 ); + int encoding = unsigned_16( face.data, face.cmap + table * 8 + 6 ); + int offset = signed_32( face.data, face.cmap + table * 8 + 8 ); + int format = unsigned_16( face.data, face.cmap + offset ); + if ( platform == 3 && encoding == 10 && format == 12 ) + format_12 = face.cmap + offset; + else if ( platform == 3 && encoding == 1 && format == 4 ) + format_4 = face.cmap + offset; + else if ( format == 0 ) + format_0 = face.cmap + offset; + } + if ( format_12 ) + { + int groups = signed_32( face.data, format_12 + 12 ); + for ( int group = 0; group < groups; ++group ) + { + int start = signed_32( face.data, format_12 + 16 + group * 12 ); + int end = signed_32( face.data, format_12 + 20 + group * 12 ); + int glyph = signed_32( face.data, format_12 + 24 + group * 12 ); + if ( start <= codepoint && codepoint <= end ) + return codepoint - start + glyph; + } + } + else if ( format_4 ) + { + int segments = unsigned_16( face.data, format_4 + 6 ); + int end_array = format_4 + 14; + int start_array = end_array + 2 + segments; + int delta_array = start_array + segments; + int range_array = delta_array + segments; + for ( int segment = 0; segment < segments; segment += 2 ) + { + int start = unsigned_16( face.data, start_array + segment ); + int end = unsigned_16( face.data, end_array + segment ); + int delta = signed_16( face.data, delta_array + segment ); + int range = unsigned_16( face.data, range_array + segment ); + if ( start <= codepoint && codepoint <= end ) + return range ? + unsigned_16( face.data, range_array + segment + + ( codepoint - start ) * 2 + range ) : + ( codepoint + delta ) & 0xffff; + } + } + else if ( format_0 && 0 <= codepoint && codepoint < 256 ) + return unsigned_8( face.data, format_0 + 6 + codepoint ); + return 0; +} + +void canvas::get_font_metrics(int& ascent, int& descent, int& height, int& x_height) +{ + if (face.data.empty()) return; + + // https://www.w3.org/TR/css-inline/#ascent-descent + // "It is recommended that implementations that use OpenType or TrueType fonts use the metrics sTypoAscender and sTypoDescender from the font’s OS/2 table" + float sTypoAscender = (float)signed_16(face.data, face.os_2 + 68); + float sTypoDescender = (float)signed_16(face.data, face.os_2 + 70); + // Some fonts, eg. Inconsolata, don't have the sxHeight field (it is defined in the second version of OS/2 table). + // If it is absent (yMax - yMin) for 'x' from glyf table can be used. + int os2ver = unsigned_16(face.data, face.os_2); + float sxHeight = os2ver >= 2 ? (float)signed_16(face.data, face.os_2 + 86) : 0; + + ascent = (int)ceil(sTypoAscender * face.scale); + descent = (int)ceil(-sTypoDescender * face.scale); + + // https://www.w3.org/TR/css-inline/#font-line-gap + // https://www.w3.org/TR/css-inline/#inline-height + // In several fonts I examined, including Inconsolata and csstest-ascii.ttf from w3.org, + // both OS/2 sTypoLineGap and hhea lineGap are either 0 or very small (I used https://fontdrop.info/ viewer). + // cairo container sets height to ascent + descent, litehtml uses this value as a normal line height and for baseline calculations. + height = (int)ceil((sTypoAscender - sTypoDescender) * face.scale); + + x_height = (int)ceil(sxHeight * face.scale); +} + +// Convert a text string to a set of polylines. This works out the placement +// of the text string relative to the anchor position. Then it walks through +// the string, sizing and placing each character by temporarily changing the +// current transform matrix to map from font units to canvas pixel coordinates +// before adding the glyph to the polylines. This replaces the previous +// polyline data. +// +void canvas::text_to_lines( + char const *text, + xy position, + float maximum_width, + bool stroking ) +{ + static float const tolerance = 0.125f; + float ratio = tolerance / std::max( 0.5f * line_width, tolerance ); + float angular = stroking ? ( ratio - 2.0f ) * ratio * 2.0f + 1.0f : -1.0f; + lines.points.clear(); + lines.subpaths.clear(); + if ( face.data.empty() || !text || maximum_width <= 0.0f ) + return; + float width = maximum_width == 1.0e30f && text_align == leftward ? 0.0f : + measure_text( text ); + float reduction = maximum_width / std::max( maximum_width, width ); + if ( text_align == rightward ) + position.x -= width * reduction; + else if ( text_align == center ) + position.x -= 0.5f * width * reduction; + xy scaling = face.scale * xy( reduction, 1.0f ); + float units_per_em = static_cast< float >( + unsigned_16( face.data, face.head + 18 ) ); + float ascender = static_cast< float >( + signed_16( face.data, face.os_2 + 68 ) ); + float descender = static_cast< float >( + signed_16( face.data, face.os_2 + 70 ) ); + if ( text_baseline == top ) + position.y += ascender * face.scale; + else if ( text_baseline == middle ) + position.y += ( ascender - descender ) * 0.5f * face.scale; + else if ( text_baseline == bottom ) + position.y += descender * face.scale; + else if ( text_baseline == hanging ) + position.y += 0.6f * face.scale * units_per_em; + affine_matrix saved_forward = forward; + affine_matrix saved_inverse = inverse; + int hmetrics = unsigned_16( face.data, face.hhea + 34 ); + int place = 0; + for ( int index = 0; text[ index ]; ) + { + int glyph = character_to_glyph( text, index ); + forward = saved_forward; + transform( scaling.x, 0.0f, 0.0f, -scaling.y, + position.x + static_cast< float >( place ) * scaling.x, + position.y ); + add_glyph( glyph, angular ); + int entry = std::min( glyph, hmetrics - 1 ); + place += unsigned_16( face.data, face.hmtx + entry * 4 ); + } + forward = saved_forward; + inverse = saved_inverse; +} + +// Break the polylines into smaller pieces according to the dash settings. +// This walks along the polyline subpaths and dash pattern together, emitting +// new points via lerping where dash segments begin and end. Each dash +// segment becomes a new open subpath in the polyline. Some care is to +// taken to handle two special cases of closed subpaths. First, those that +// are completely within the first dash segment should be emitted as-is and +// remain closed. Secondly, those that start and end within a dash should +// have the two dashes merged together so that the lines join. This replaces +// the previous polyline data. +// +void canvas::dash_lines() +{ + if ( line_dash.empty() ) + return; + lines.points.swap( scratch.points ); + lines.points.clear(); + lines.subpaths.swap( scratch.subpaths ); + lines.subpaths.clear(); + float total = std::accumulate( line_dash.begin(), line_dash.end(), 0.0f ); + float offset = fmodf( line_dash_offset, total ); + if ( offset < 0.0f ) + offset += total; + size_t start = 0; + while ( offset >= line_dash[ start ] ) + { + offset -= line_dash[ start ]; + start = start + 1 < line_dash.size() ? start + 1 : 0; + } + size_t ending = 0; + for ( size_t subpath = 0; subpath < scratch.subpaths.size(); ++subpath ) + { + size_t index = ending; + ending += scratch.subpaths[ subpath ].count; + size_t first = lines.points.size(); + size_t segment = start; + bool emit = ~start & 1; + size_t merge_point = lines.points.size(); + size_t merge_subpath = lines.subpaths.size(); + bool merge_emit = emit; + float next = line_dash[ start ] - offset; + for ( ; index + 1 < ending; ++index ) + { + xy from = scratch.points[ index ]; + xy to = scratch.points[ index + 1 ]; + if ( emit ) + lines.points.push_back( from ); + float line = length( inverse * to - inverse * from ); + while ( next < line ) + { + lines.points.push_back( lerp( from, to, next / line ) ); + if ( emit ) + { + subpath_data entry = { + lines.points.size() - first, false }; + lines.subpaths.push_back( entry ); + first = lines.points.size(); + } + segment = segment + 1 < line_dash.size() ? segment + 1 : 0; + emit = !emit; + next += line_dash[ segment ]; + } + next -= line; + } + if ( emit ) + { + lines.points.push_back( scratch.points[ index ] ); + subpath_data entry = { lines.points.size() - first, false }; + lines.subpaths.push_back( entry ); + if ( scratch.subpaths[ subpath ].closed && merge_emit ) + { + if ( lines.subpaths.size() == merge_subpath + 1 ) + lines.subpaths.back().closed = true; + else + { + size_t count = lines.subpaths.back().count; + std::rotate( ( lines.points.begin() + + static_cast< ptrdiff_t >( merge_point ) ), + ( lines.points.end() - + static_cast< ptrdiff_t >( count ) ), + lines.points.end() ); + lines.subpaths[ merge_subpath ].count += count; + lines.subpaths.pop_back(); + } + } + } + } +} + +// Trace along a series of points from a subpath in the scratch polylines +// and add new points to the main polylines with the stroke expansion on +// one side. Calling this again with the ends reversed adds the other +// half of the stroke. If the original subpath was closed, each pass +// adds a complete closed loop, with one adding the inside and one adding +// the outside. The two will wind in opposite directions. If the original +// subpath was open, each pass ends with one of the line caps and the two +// passes together form a single closed loop. In either case, this handles +// adding line joins, including inner joins. Care is taken to fill tight +// inside turns correctly by adding additional windings. See Figure 10 of +// "Converting Stroked Primitives to Filled Primitives" by Diego Nehab, for +// the inspiration for these extra windings. +// +void canvas::add_half_stroke( + size_t beginning, + size_t ending, + bool closed ) +{ + float half = line_width * 0.5f; + float ratio = miter_limit * miter_limit * half * half; + xy in_direction = xy( 0.0f, 0.0f ); + float in_length = 0.0f; + xy point = inverse * scratch.points[ beginning ]; + size_t finish = beginning; + size_t index = beginning; + do + { + xy next = inverse * scratch.points[ index ]; + xy out_direction = normalized( next - point ); + float out_length = length( next - point ); + static float const epsilon = 1.0e-4f; + if ( in_length != 0.0f && out_length >= epsilon ) + { + if ( closed && finish == beginning ) + finish = index; + xy side_in = point + half * perpendicular( in_direction ); + xy side_out = point + half * perpendicular( out_direction ); + float turn = dot( perpendicular( in_direction ), out_direction ); + if ( fabsf( turn ) < epsilon ) + turn = 0.0f; + xy offset = turn == 0.0f ? xy( 0.0f, 0.0f ) : + half / turn * ( out_direction - in_direction ); + bool tight = ( dot( offset, in_direction ) < -in_length && + dot( offset, out_direction ) > out_length ); + if ( turn > 0.0f && tight ) + { + std::swap( side_in, side_out ); + std::swap( in_direction, out_direction ); + lines.points.push_back( forward * side_out ); + lines.points.push_back( forward * point ); + lines.points.push_back( forward * side_in ); + } + if ( ( turn > 0.0f && !tight ) || + ( turn != 0.0f && line_join == miter && + dot( offset, offset ) <= ratio ) ) + lines.points.push_back( forward * ( point + offset ) ); + else if ( line_join == rounded ) + { + float cosine = dot( in_direction, out_direction ); + float angle = acosf( + std::min( std::max( cosine, -1.0f ), 1.0f ) ); + float alpha = 4.0f / 3.0f * tanf( 0.25f * angle ); + lines.points.push_back( forward * side_in ); + add_bezier( + forward * side_in, + forward * ( side_in + alpha * half * in_direction ), + forward * ( side_out - alpha * half * out_direction ), + forward * side_out, + -1.0f ); + } + else + { + lines.points.push_back( forward * side_in ); + lines.points.push_back( forward * side_out ); + } + if ( turn > 0.0f && tight ) + { + lines.points.push_back( forward * side_out ); + lines.points.push_back( forward * point ); + lines.points.push_back( forward * side_in ); + std::swap( in_direction, out_direction ); + } + } + if ( out_length >= epsilon ) + { + in_direction = out_direction; + in_length = out_length; + point = next; + } + index = ( index == ending ? beginning : + ending > beginning ? index + 1 : + index - 1 ); + } while ( index != finish ); + if ( closed || in_length == 0.0f ) + return; + xy ahead = half * in_direction; + xy side = perpendicular( ahead ); + if ( line_cap == butt ) + { + lines.points.push_back( forward * ( point + side ) ); + lines.points.push_back( forward * ( point - side ) ); + } + else if ( line_cap == square ) + { + lines.points.push_back( forward * ( point + ahead + side ) ); + lines.points.push_back( forward * ( point + ahead - side ) ); + } + else if ( line_cap == circle ) + { + static float const alpha = 0.55228475f; // 4/3*tan(pi/8) + lines.points.push_back( forward * ( point + side ) ); + add_bezier( forward * ( point + side ), + forward * ( point + side + alpha * ahead ), + forward * ( point + ahead + alpha * side ), + forward * ( point + ahead ), + -1.0f ); + add_bezier( forward * ( point + ahead ), + forward * ( point + ahead - alpha * side ), + forward * ( point - side + alpha * ahead ), + forward * ( point - side ), + -1.0f ); + } +} + +// Perform stroke expansion on the polylines. After first breaking them up +// according to the dash pattern (if any), it then moves the polyline data to +// the scratch space. Each subpath is then traced both forwards and backwards +// to add the points for a half stroke, which together create the points for +// one (if the original subpath was open) or two (if it was closed) new closed +// subpaths which, when filled, will draw the stroked lines. While the lower +// level code this calls only adds the points of the half strokes, this +// adds subpath information for the strokes. This replaces the previous +// polyline data. +// +void canvas::stroke_lines() +{ + if ( forward.a * forward.d - forward.b * forward.c == 0.0f ) + return; + dash_lines(); + lines.points.swap( scratch.points ); + lines.points.clear(); + lines.subpaths.swap( scratch.subpaths ); + lines.subpaths.clear(); + size_t ending = 0; + for ( size_t subpath = 0; subpath < scratch.subpaths.size(); ++subpath ) + { + size_t beginning = ending; + ending += scratch.subpaths[ subpath ].count; + if ( ending - beginning < 2 ) + continue; + size_t first = lines.points.size(); + add_half_stroke( beginning, ending - 1, + scratch.subpaths[ subpath ].closed ); + if ( scratch.subpaths[ subpath ].closed ) + { + subpath_data entry = { lines.points.size() - first, true }; + lines.subpaths.push_back( entry ); + first = lines.points.size(); + } + add_half_stroke( ending - 1, beginning, + scratch.subpaths[ subpath ].closed ); + subpath_data entry = { lines.points.size() - first, true }; + lines.subpaths.push_back( entry ); + } +} + +// Scan-convert a single polyline segment. This walks along the pixels that +// the segment touches in left-to-right order, using signed trapezoidal area +// to accumulate a list of changes in signed coverage at each visible pixel +// when processing them from left to right. Each run of horizontal pixels +// ends with one final update to the right of the last pixel to bring the +// total up to date. Note that this does not clip to the screen boundary. +// +void canvas::add_runs( + xy from, + xy to ) +{ + static float const epsilon = 2.0e-5f; + if ( fabsf( to.y - from.y ) < epsilon) + return; + float sign = to.y > from.y ? 1.0f : -1.0f; + if ( from.x > to.x ) + std::swap( from, to ); + xy now = from; + xy pixel = xy( floorf( now.x ), floorf( now.y ) ); + xy corner = pixel + xy( 1.0f, to.y > from.y ? 1.0f : 0.0f ); + xy slope = xy( ( to.x - from.x ) / ( to.y - from.y ), + ( to.y - from.y ) / ( to.x - from.x ) ); + xy next_x = ( to.x - from.x < epsilon ) ? to : + xy( corner.x, now.y + ( corner.x - now.x ) * slope.y ); + xy next_y = xy( now.x + ( corner.y - now.y ) * slope.x, corner.y ); + if ( ( from.y < to.y && to.y < next_y.y ) || + ( from.y > to.y && to.y > next_y.y ) ) + next_y = to; + float y_step = to.y > from.y ? 1.0f : -1.0f; + do + { + float carry = 0.0f; + while ( next_x.x < next_y.x ) + { + float strip = std::min( std::max( ( next_x.y - now.y ) * y_step, + 0.0f ), 1.0f ); + float mid = ( next_x.x + now.x ) * 0.5f; + float area = ( mid - pixel.x ) * strip; + pixel_run piece = { static_cast< unsigned short >( pixel.x ), + static_cast< unsigned short >( pixel.y ), + ( carry + strip - area ) * sign }; + runs.push_back( piece ); + carry = area; + now = next_x; + next_x.x += 1.0f; + next_x.y = ( next_x.x - from.x ) * slope.y + from.y; + pixel.x += 1.0f; + } + float strip = std::min( std::max( ( next_y.y - now.y ) * y_step, + 0.0f ), 1.0f ); + float mid = ( next_y.x + now.x ) * 0.5f; + float area = ( mid - pixel.x ) * strip; + pixel_run piece_1 = { static_cast< unsigned short >( pixel.x ), + static_cast< unsigned short >( pixel.y ), + ( carry + strip - area ) * sign }; + pixel_run piece_2 = { static_cast< unsigned short >( pixel.x + 1.0f ), + static_cast< unsigned short >( pixel.y ), + area * sign }; + runs.push_back( piece_1 ); + runs.push_back( piece_2 ); + now = next_y; + next_y.y += y_step; + next_y.x = ( next_y.y - from.y ) * slope.x + from.x; + pixel.y += y_step; + if ( ( from.y < to.y && to.y < next_y.y ) || + ( from.y > to.y && to.y > next_y.y ) ) + next_y = to; + } while ( now.y != to.y ); +} + +static bool operator<( + pixel_run left, + pixel_run right ) +{ + return ( left.y < right.y ? true : + left.y > right.y ? false : + left.x < right.x ? true : + left.x > right.x ? false : + fabsf( left.delta ) < fabsf( right.delta ) ); +} + +// Scan-convert the polylines to prepare them for antialiased rendering. +// For each of the polyline loops, it first clips them to the screen. +// See "Reentrant Polygon Clipping" by Sutherland and Hodgman for details. +// Then it walks the polyline loop and scan-converts each line segment to +// produce a list of changes in signed pixel coverage when processed in +// left-to-right, top-to-bottom order. The list of changes is then sorted +// into that order, and multiple changes to the same pixel are coalesced +// by summation. The result is a sparse, run-length encoded description +// of the coverage of each pixel to be drawn. +// +void canvas::lines_to_runs( + xy offset, + int padding ) +{ + runs.clear(); + float width = static_cast< float >( size_x + padding ); + float height = static_cast< float >( size_y + padding ); + size_t ending = 0; + for ( size_t subpath = 0; subpath < lines.subpaths.size(); ++subpath ) + { + size_t beginning = ending; + ending += lines.subpaths[ subpath ].count; + scratch.points.clear(); + for ( size_t index = beginning; index < ending; ++index ) + scratch.points.push_back( offset + lines.points[ index ] ); + for ( int edge = 0; edge < 4; ++edge ) + { + xy normal = xy( edge == 0 ? 1.0f : edge == 2 ? -1.0f : 0.0f, + edge == 1 ? 1.0f : edge == 3 ? -1.0f : 0.0f ); + float place = edge == 2 ? width : edge == 3 ? height : 0.0f; + size_t first = scratch.points.size(); + for ( size_t index = 0; index < first; ++index ) + { + xy from = scratch.points[ ( index ? index : first ) - 1 ]; + xy to = scratch.points[ index ]; + float from_side = dot( from, normal ) + place; + float to_side = dot( to, normal ) + place; + if ( from_side * to_side < 0.0f ) + scratch.points.push_back( lerp( from, to, + from_side / ( from_side - to_side ) ) ); + if ( to_side >= 0.0f ) + scratch.points.push_back( to ); + } + scratch.points.erase( + scratch.points.begin(), + scratch.points.begin() + static_cast< ptrdiff_t >( first ) ); + } + size_t last = scratch.points.size(); + for ( size_t index = 0; index < last; ++index ) + { + xy from = scratch.points[ ( index ? index : last ) - 1 ]; + xy to = scratch.points[ index ]; + add_runs( xy( std::min( std::max( from.x, 0.0f ), width ), + std::min( std::max( from.y, 0.0f ), height ) ), + xy( std::min( std::max( to.x, 0.0f ), width ), + std::min( std::max( to.y, 0.0f ), height ) ) ); + } + } + if ( runs.empty() ) + return; + std::sort( runs.begin(), runs.end() ); + size_t to = 0; + for ( size_t from = 1; from < runs.size(); ++from ) + if ( runs[ from ].x == runs[ to ].x && + runs[ from ].y == runs[ to ].y ) + runs[ to ].delta += runs[ from ].delta; + else if ( runs[ from ].delta != 0.0f ) + runs[ ++to ] = runs[ from ]; + runs.erase( runs.begin() + static_cast< ptrdiff_t >( to ) + 1, + runs.end() ); +} + +// Paint a pixel according to its point location and a paint style to produce +// a premultiplied, linearized RGBA color. This handles all supported paint +// styles: solid colors, linear gradients, radial gradients, and patterns. +// For gradients and patterns, it takes into account the current transform. +// Patterns are resampled using a separable bicubic convolution filter, +// with edges handled according to the wrap mode. See "Cubic Convolution +// Interpolation for Digital Image Processing" by Keys. This filter is best +// known for magnification, but also works well for antialiased minification, +// since it's actually a Catmull-Rom spline approximation of Lanczos-2. +// +rgba canvas::paint_pixel( + xy point, + paint_brush const &brush ) +{ + if ( brush.colors.empty() ) + return rgba( 0.0f, 0.0f, 0.0f, 0.0f ); + if ( brush.type == paint_brush::color ) + return brush.colors.front(); + point = inverse * point; + if ( brush.type == paint_brush::pattern ) + { + float width = static_cast< float >( brush.width ); + float height = static_cast< float >( brush.height ); + if ( ( ( brush.repetition & 2 ) && + ( point.x < 0.0f || width <= point.x ) ) || + ( ( brush.repetition & 1 ) && + ( point.y < 0.0f || height <= point.y ) ) ) + return rgba( 0.0f, 0.0f, 0.0f, 0.0f ); + float scale_x = fabsf( inverse.a ) + fabsf( inverse.c ); + float scale_y = fabsf( inverse.b ) + fabsf( inverse.d ); + scale_x = std::max( 1.0f, std::min( scale_x, width * 0.25f ) ); + scale_y = std::max( 1.0f, std::min( scale_y, height * 0.25f ) ); + float reciprocal_x = 1.0f / scale_x; + float reciprocal_y = 1.0f / scale_y; + point -= xy( 0.5f, 0.5f ); + int left = static_cast< int >( ceilf( point.x - scale_x * 2.0f ) ); + int top = static_cast< int >( ceilf( point.y - scale_y * 2.0f ) ); + int right = static_cast< int >( ceilf( point.x + scale_x * 2.0f ) ); + int bottom = static_cast< int >( ceilf( point.y + scale_y * 2.0f ) ); + rgba total_color = rgba( 0.0f, 0.0f, 0.0f, 0.0f ); + float total_weight = 0.0f; + for ( int pattern_y = top; pattern_y < bottom; ++pattern_y ) + { + float y = fabsf( reciprocal_y * + ( static_cast< float >( pattern_y ) - point.y ) ); + float weight_y = ( y < 1.0f ? + ( 1.5f * y - 2.5f ) * y * y + 1.0f : + ( ( -0.5f * y + 2.5f ) * y - 4.0f ) * y + 2.0f ); + int wrapped_y = pattern_y % brush.height; + if ( wrapped_y < 0 ) + wrapped_y += brush.height; + if ( &brush == &image_brush ) + wrapped_y = std::min( std::max( pattern_y, 0 ), + brush.height - 1 ); + for ( int pattern_x = left; pattern_x < right; ++pattern_x ) + { + float x = fabsf( reciprocal_x * + ( static_cast< float >( pattern_x ) - point.x ) ); + float weight_x = ( x < 1.0f ? + ( 1.5f * x - 2.5f ) * x * x + 1.0f : + ( ( -0.5f * x + 2.5f ) * x - 4.0f ) * x + 2.0f ); + int wrapped_x = pattern_x % brush.width; + if ( wrapped_x < 0 ) + wrapped_x += brush.width; + if ( &brush == &image_brush ) + wrapped_x = std::min( std::max( pattern_x, 0 ), + brush.width - 1 ); + float weight = weight_x * weight_y; + size_t index = static_cast< size_t >( + wrapped_y * brush.width + wrapped_x ); + total_color += weight * brush.colors[ index ]; + total_weight += weight; + } + } + return ( 1.0f / total_weight ) * total_color; + } + float offset = 0; + xy relative = point - brush.start; + xy line = brush.end - brush.start; + float gradient = dot( relative, line ); + float span = dot( line, line ); + if ( brush.type == paint_brush::linear ) + { + if ( span == 0.0f ) + return rgba( 0.0f, 0.0f, 0.0f, 0.0f ); + offset = gradient / span; + } + else if (brush.type == paint_brush::radial) + { + float initial = brush.start_radius; + float change = brush.end_radius - initial; + float a = span - change * change; + float b = -2.0f * ( gradient + initial * change ); + float c = dot( relative, relative ) - initial * initial; + float discriminant = b * b - 4.0f * a * c; + if ( discriminant < 0.0f || + ( span == 0.0f && change == 0.0f ) ) + return rgba( 0.0f, 0.0f, 0.0f, 0.0f ); + float root = sqrtf( discriminant ); + float reciprocal = 1.0f / ( 2.0f * a ); + float offset_1 = ( -b - root ) * reciprocal; + float offset_2 = ( -b + root ) * reciprocal; + float radius_1 = initial + change * offset_1; + float radius_2 = initial + change * offset_2; + if ( radius_2 >= 0.0f ) + offset = offset_2; + else if ( radius_1 >= 0.0f ) + offset = offset_1; + else + return rgba( 0.0f, 0.0f, 0.0f, 0.0f ); + } + else if (brush.type == paint_brush::css_radial) + { + if (brush.css_radius.x == 0 || brush.css_radius.y == 0) + offset = 1; + else + { + xy rel = {relative.x / brush.css_radius.x, relative.y / brush.css_radius.y}; + offset = length(rel); + } + } + else if (brush.type == paint_brush::conic) + { + float angle = 90 + direction(relative) * 180 / pi; + offset = normalize_angle(angle - brush.angle) / 360; + } + size_t index = static_cast< size_t >( + std::upper_bound( brush.stops.begin(), brush.stops.end(), offset ) - + brush.stops.begin() ); + if ( index == 0 ) + return premultiplied( brush.colors.front() ); + if ( index == brush.stops.size() ) + return premultiplied( brush.colors.back() ); + struct { rgba color; float stop; std::optional hint; } + A = { brush.colors[index-1], brush.stops[index-1], brush.hints[index-1] }, + B = { brush.colors[index], brush.stops[index], {} }; + // https://drafts.csswg.org/css-images-4/#coloring-gradient-line + // 1. Determine the location of the transition hint as a percentage of the distance between the two color stops, denoted as a number between 0 and 1 + float H = !A.hint ? .5f : (*A.hint - A.stop) / (B.stop - A.stop); + // 2. For any given point between the two color stops, determine the point’s location as a percentage of the distance between the two color stops, in the same way as the previous step. + float P = (offset - A.stop) / (B.stop - A.stop); + // 3. Let C, the color weighting at that point, be equal to P^logH(.5). + float C = pow(P, log(.5f) / log(H)); + // 4. The color at that point is then a linear blend between the colors of the two color stops, blending (1 - C) of the first stop and C of the second stop. + return premultiplied((1 - C) * A.color + C * B.color); +} + +// Render the shadow of the polylines into the pixel buffer if needed. After +// computing the border as the maximum distance that one pixel can affect +// another via the blur, it scan-converts the lines to runs with the shadow +// offset and that extra amount of border padding. Then it bounds the scan +// converted shape, pads this with border, clips that to the extended canvas +// size, and rasterizes to fill a working area with the alpha. Next, a fast +// approximation of a Gaussian blur is done using three passes of box blurs +// each in the rows and columns. Note that these box blurs have a small extra +// weight on the tails to allow for fractional widths. See "Theoretical +// Foundations of Gaussian Convolution by Extended Box Filtering" by Gwosdek +// et al. for details. Finally, it colors the blurred alpha image with +// the shadow color and blends this into the pixel buffer according to the +// compositing settings and clip mask. Note that it does not bother clearing +// outside the area of the alpha image when the compositing settings require +// clearing; that will be done on the subsequent main rendering pass. +// +void canvas::render_shadow( + paint_brush const &brush ) +{ + if ( shadow_color.a == 0.0f || ( shadow_blur == 0.0f && + shadow_offset_x == 0.0f && + shadow_offset_y == 0.0f ) ) + return; + float sigma_squared = 0.25f * shadow_blur * shadow_blur; + size_t radius = static_cast< size_t >( + 0.5f * sqrtf( 4.0f * sigma_squared + 1.0f ) - 0.5f ); + int border = 3 * ( static_cast< int >( radius ) + 1 ); + xy offset = xy( static_cast< float >( border ) + shadow_offset_x, + static_cast< float >( border ) + shadow_offset_y ); + lines_to_runs( offset, 2 * border ); + int left = size_x + 2 * border; + int right = 0; + int top = size_y + 2 * border; + int bottom = 0; + for ( size_t index = 0; index < runs.size(); ++index ) + { + left = std::min( left, static_cast< int >( runs[ index ].x ) ); + right = std::max( right, static_cast< int >( runs[ index ].x ) ); + top = std::min( top, static_cast< int >( runs[ index ].y ) ); + bottom = std::max( bottom, static_cast< int >( runs[ index ].y ) ); + } + left = std::max( left - border, 0 ); + right = std::min( right + border, size_x + 2 * border ) + 1; + top = std::max( top - border, 0 ); + bottom = std::min( bottom + border, size_y + 2 * border ); + size_t width = static_cast< size_t >( std::max( right - left, 0 ) ); + size_t height = static_cast< size_t >( std::max( bottom - top, 0 ) ); + size_t working = width * height; + shadow.clear(); + shadow.resize( working + std::max( width, height ) ); + static float const threshold = 1.0f / 8160.0f; + { + int x = -1; + int y = -1; + float sum = 0.0f; + for ( size_t index = 0; index < runs.size(); ++index ) + { + pixel_run next = runs[ index ]; + float coverage = std::min( fabsf( sum ), 1.0f ); + int to = next.y == y ? next.x : x + 1; + if ( coverage >= threshold ) + for ( ; x < to; ++x ) + shadow[ static_cast< size_t >( y - top ) * width + + static_cast< size_t >( x - left ) ] = coverage * + paint_pixel( xy( static_cast< float >( x ) + 0.5f, + static_cast< float >( y ) + 0.5f ) - + offset, brush ).a; + if ( next.y != y ) + sum = 0.0f; + x = next.x; + y = next.y; + sum += next.delta; + } + } + float alpha = static_cast< float >( 2 * radius + 1 ) * + ( static_cast< float >( radius * ( radius + 1 ) ) - sigma_squared ) / + ( 2.0f * sigma_squared - + static_cast< float >( 6 * ( radius + 1 ) * ( radius + 1 ) ) ); + float divisor = 2.0f * ( alpha + static_cast< float >( radius ) ) + 1.0f; + float weight_1 = alpha / divisor; + float weight_2 = ( 1.0f - alpha ) / divisor; + for ( size_t y = 0; y < height; ++y ) + for ( int pass = 0; pass < 3; ++pass ) + { + for ( size_t x = 0; x < width; ++x ) + shadow[ working + x ] = shadow[ y * width + x ]; + float running = weight_1 * shadow[ working + radius + 1 ]; + for ( size_t x = 0; x <= radius; ++x ) + running += ( weight_1 + weight_2 ) * shadow[ working + x ]; + shadow[ y * width ] = running; + for ( size_t x = 1; x < width; ++x ) + { + if ( x >= radius + 1 ) + running -= weight_2 * shadow[ working + x - radius - 1 ]; + if ( x >= radius + 2 ) + running -= weight_1 * shadow[ working + x - radius - 2 ]; + if ( x + radius < width ) + running += weight_2 * shadow[ working + x + radius ]; + if ( x + radius + 1 < width ) + running += weight_1 * shadow[ working + x + radius + 1 ]; + shadow[ y * width + x ] = running; + } + } + for ( size_t x = 0; x < width; ++x ) + for ( int pass = 0; pass < 3; ++pass ) + { + for ( size_t y = 0; y < height; ++y ) + shadow[ working + y ] = shadow[ y * width + x ]; + float running = weight_1 * shadow[ working + radius + 1 ]; + for ( size_t y = 0; y <= radius; ++y ) + running += ( weight_1 + weight_2 ) * shadow[ working + y ]; + shadow[ x ] = running; + for ( size_t y = 1; y < height; ++y ) + { + if ( y >= radius + 1 ) + running -= weight_2 * shadow[ working + y - radius - 1 ]; + if ( y >= radius + 2 ) + running -= weight_1 * shadow[ working + y - radius - 2 ]; + if ( y + radius < height ) + running += weight_2 * shadow[ working + y + radius ]; + if ( y + radius + 1 < height ) + running += weight_1 * shadow[ working + y + radius + 1 ]; + shadow[ y * width + x ] = running; + } + } + int operation = global_composite_operation; + int x = -1; + int y = -1; + float sum = 0.0f; + for ( size_t index = 0; index < mask.size(); ++index ) + { + pixel_run next = mask[ index ]; + float visibility = std::min( fabsf( sum ), 1.0f ); + int to = std::min( next.y == y ? next.x : x + 1, right - border ); + if ( visibility >= threshold && + top <= y + border && y + border < bottom ) + for ( ; x < to; ++x ) + { + rgba &back = bitmap[ y * size_x + x ]; + rgba fore = global_alpha * + shadow[ + static_cast< size_t >( y + border - top ) * width + + static_cast< size_t >( x + border - left ) ] * + shadow_color; + float mix_fore = operation & 1 ? back.a : 0.0f; + if ( operation & 2 ) + mix_fore = 1.0f - mix_fore; + float mix_back = operation & 4 ? fore.a : 0.0f; + if ( operation & 8 ) + mix_back = 1.0f - mix_back; + rgba blend = mix_fore * fore + mix_back * back; + blend.a = std::min( blend.a, 1.0f ); + back = visibility * blend + ( 1.0f - visibility ) * back; + } + if ( next.y != y ) + sum = 0.0f; + x = std::max( static_cast< int >( next.x ), left - border ); + y = next.y; + sum += next.delta; + } +} + +// Render the polylines into the pixel buffer. It scan-converts the lines +// to runs which represent changes to the signed fractional coverage when +// read from left-to-right, top-to-bottom. It scans through these to +// determine spans of pixels that need to be drawn, paints those pixels +// according to the brush, and then blends them into the buffer according +// to the current compositing settings. This is slightly more complicated +// because it interleaves this with a simultaneous scan through a similar +// set of runs representing the current clip mask to determine which pixels +// it can composite into. Note that shadows are always drawn first. +// +void canvas::render_main( + paint_brush const &brush ) +{ + if ( forward.a * forward.d - forward.b * forward.c == 0.0f ) + return; + render_shadow( brush ); + lines_to_runs( xy( 0.0f, 0.0f ), 0 ); + int operation = global_composite_operation; + int x = -1; + int y = -1; + float path_sum = 0.0f; + float clip_sum = 0.0f; + size_t path_index = 0; + size_t clip_index = 0; + while ( clip_index < mask.size() ) + { + bool which = ( path_index < runs.size() && + runs[ path_index ] < mask[ clip_index ] ); + pixel_run next = which ? runs[ path_index ] : mask[ clip_index ]; + float coverage = std::min( fabsf( path_sum ), 1.0f ); + float visibility = std::min( fabsf( clip_sum ), 1.0f ); + int to = next.y == y ? next.x : x + 1; + static float const threshold = 1.0f / 8160.0f; + if ( ( coverage >= threshold || ~operation & 8 ) && + visibility >= threshold ) + for ( ; x < to; ++x ) + { + rgba &back = bitmap[ y * size_x + x ]; + rgba fore = coverage * global_alpha * + paint_pixel( xy( static_cast< float >( x ) + 0.5f, + static_cast< float >( y ) + 0.5f ), + brush ); + float mix_fore = operation & 1 ? back.a : 0.0f; + if ( operation & 2 ) + mix_fore = 1.0f - mix_fore; + float mix_back = operation & 4 ? fore.a : 0.0f; + if ( operation & 8 ) + mix_back = 1.0f - mix_back; + rgba blend = mix_fore * fore + mix_back * back; + blend.a = std::min( blend.a, 1.0f ); + back = visibility * blend + ( 1.0f - visibility ) * back; + } + x = next.x; + if ( next.y != y ) + { + y = next.y; + path_sum = 0.0f; + clip_sum = 0.0f; + } + if ( which ) + path_sum += runs[ path_index++ ].delta; + else + clip_sum += mask[ clip_index++ ].delta; + } +} + +canvas::canvas( + int width, + int height ) + : global_composite_operation( source_over ), + shadow_offset_x( 0.0f ), + shadow_offset_y( 0.0f ), + line_cap( butt ), + line_join( miter ), + line_dash_offset( 0.0f ), + text_align( start ), + text_baseline( alphabetic ), + size_x( width ), + size_y( height ), + global_alpha( 1.0f ), + shadow_blur( 0.0f ), + line_width( 1.0f ), + miter_limit( 10.0f ), + fill_brush(), + stroke_brush(), + image_brush(), + face(), + bitmap( new rgba[ width * height ] ), + saves( 0 ) +{ + affine_matrix identity = { 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f }; + forward = identity; + inverse = identity; + set_color( fill_style, 0.0f, 0.0f, 0.0f, 1.0f ); + set_color( stroke_style, 0.0f, 0.0f, 0.0f, 1.0f ); + for ( unsigned short y = 0; y < size_y; ++y ) + { + pixel_run piece_1 = { 0, y, 1.0f }; + pixel_run piece_2 = { static_cast< unsigned short >( size_x ), y, + -1.0f }; + mask.push_back( piece_1 ); + mask.push_back( piece_2 ); + } +} + +canvas::canvas(int width, int height, rgba c) : canvas(width, height) +{ + save(); + set_color(fill_style, c.r, c.g, c.b, c.a); + fill_rectangle(0, 0, (float)width, (float)height); + restore(); +} + +canvas::~canvas() +{ + delete[] bitmap; + while ( canvas *head = saves ) + { + saves = head->saves; + head->saves = 0; + delete head; + } +} + +void canvas::scale( + float x, + float y ) +{ + transform( x, 0.0f, 0.0f, y, 0.0f, 0.0f ); +} + +void canvas::rotate( + float angle ) +{ + float cosine = cosf( angle ); + float sine = sinf( angle ); + transform( cosine, sine, -sine, cosine, 0.0f, 0.0f ); +} + +void canvas::translate( + float x, + float y ) +{ + transform( 1.0f, 0.0f, 0.0f, 1.0f, x, y ); +} + +void canvas::transform( + float a, + float b, + float c, + float d, + float e, + float f ) +{ + set_transform( forward.a * a + forward.c * b, + forward.b * a + forward.d * b, + forward.a * c + forward.c * d, + forward.b * c + forward.d * d, + forward.a * e + forward.c * f + forward.e, + forward.b * e + forward.d * f + forward.f ); +} + +void canvas::set_transform( + float a, + float b, + float c, + float d, + float e, + float f ) +{ + float determinant = a * d - b * c; + float scaling = determinant != 0.0f ? 1.0f / determinant : 0.0f; + affine_matrix new_forward = { a, b, c, d, e, f }; + affine_matrix new_inverse = { + scaling * d, scaling * -b, scaling * -c, scaling * a, + scaling * ( c * f - d * e ), scaling * ( b * e - a * f ) }; + forward = new_forward; + inverse = new_inverse; +} + +void canvas::set_global_alpha( + float alpha ) +{ + if ( 0.0f <= alpha && alpha <= 1.0f ) + global_alpha = alpha; +} + +void canvas::set_shadow_color( + float red, + float green, + float blue, + float alpha ) +{ + shadow_color = premultiplied( linearized( clamped( + rgba( red, green, blue, alpha ) ) ) ); +} + +void canvas::set_shadow_blur( + float level ) +{ + if ( 0.0f <= level ) + shadow_blur = level; +} + +void canvas::set_line_width( + float width ) +{ + if ( 0.0f < width ) + line_width = width; +} + +void canvas::set_miter_limit( + float limit ) +{ + if ( 0.0f < limit ) + miter_limit = limit; +} + +void canvas::set_line_dash( + float const *segments, + int count ) +{ + for ( int index = 0; index < count; ++index ) + if ( segments && segments[ index ] < 0.0f ) + return; + line_dash.clear(); + if ( !segments ) + return; + for ( int index = 0; index < count; ++index ) + line_dash.push_back( segments[ index ] ); + if ( count & 1 ) + for ( int index = 0; index < count; ++index ) + line_dash.push_back( segments[ index ] ); +} + +void canvas::set_color( + brush_type type, + float red, + float green, + float blue, + float alpha ) +{ + paint_brush &brush = type == fill_style ? fill_brush : stroke_brush; + brush.type = paint_brush::color; + brush.colors.clear(); + brush.colors.push_back( premultiplied( linearized( clamped( + rgba( red, green, blue, alpha ) ) ) ) ); +} + +void canvas::set_linear_gradient( + brush_type type, + float start_x, + float start_y, + float end_x, + float end_y ) +{ + paint_brush &brush = type == fill_style ? fill_brush : stroke_brush; + brush.type = paint_brush::linear; + brush.colors.clear(); + brush.stops.clear(); + brush.hints.clear(); + brush.start = xy( start_x, start_y ); + brush.end = xy( end_x, end_y ); +} + +void canvas::set_radial_gradient( + brush_type type, + float start_x, + float start_y, + float start_radius, + float end_x, + float end_y, + float end_radius ) +{ + if ( start_radius < 0.0f || end_radius < 0.0f ) + return; + paint_brush &brush = type == fill_style ? fill_brush : stroke_brush; + brush.type = paint_brush::radial; + brush.colors.clear(); + brush.stops.clear(); + brush.hints.clear(); + brush.start = xy( start_x, start_y ); + brush.end = xy( end_x, end_y ); + brush.start_radius = start_radius; + brush.end_radius = end_radius; +} + +void canvas::set_css_radial_gradient( + brush_type type, + float x, + float y, + float radius_x, + float radius_y) +{ + if (radius_x < 0.0f || radius_y < 0.0f) + return; + paint_brush& brush = type == fill_style ? fill_brush : stroke_brush; + brush.type = paint_brush::css_radial; + brush.colors.clear(); + brush.stops.clear(); + brush.hints.clear(); + brush.start = {x, y}; + brush.css_radius = {radius_x, radius_y}; +} + +void canvas::set_conic_gradient( + brush_type type, + float x, + float y, + float angle) +{ + paint_brush& brush = type == fill_style ? fill_brush : stroke_brush; + brush.type = paint_brush::conic; + brush.colors = {}; + brush.stops = {}; + brush.hints = {}; + brush.start = {x, y}; + brush.angle = angle; +} + +void canvas::add_color_stop( + brush_type type, + float offset, + float red, + float green, + float blue, + float alpha, + std::optional hint ) +{ + paint_brush &brush = type == fill_style ? fill_brush : stroke_brush; + if ( ( brush.type != paint_brush::linear && + brush.type != paint_brush::radial && + brush.type != paint_brush::css_radial && + brush.type != paint_brush::conic ) || + offset < 0.0f || 1.0f < offset ) + return; + ptrdiff_t index = std::upper_bound( + brush.stops.begin(), brush.stops.end(), offset ) - + brush.stops.begin(); + rgba color = linearized( clamped( rgba( red, green, blue, alpha ) ) ); + brush.colors.insert( brush.colors.begin() + index, color ); + brush.stops.insert( brush.stops.begin() + index, offset ); + brush.hints.insert( brush.hints.begin() + index, hint ); +} + +void canvas::set_pattern( + brush_type type, + unsigned char const *image, + int width, + int height, + int stride, + repetition_style repetition ) +{ + if ( !image || width <= 0 || height <= 0 ) + return; + paint_brush &brush = type == fill_style ? fill_brush : stroke_brush; + brush.type = paint_brush::pattern; + brush.colors.clear(); + for ( int y = 0; y < height; ++y ) + for ( int x = 0; x < width; ++x ) + { + int index = y * stride + x * 4; + rgba color = rgba( + image[ index + 0 ] / 255.0f, image[ index + 1 ] / 255.0f, + image[ index + 2 ] / 255.0f, image[ index + 3 ] / 255.0f ); + brush.colors.push_back( premultiplied( linearized( color ) ) ); + } + brush.width = width; + brush.height = height; + brush.repetition = repetition; +} + +void canvas::begin_path() +{ + path.points.clear(); + path.subpaths.clear(); +} + +void canvas::move_to( + float x, + float y ) +{ + if ( !path.subpaths.empty() && path.subpaths.back().count == 1 ) + { + path.points.back() = forward * xy( x, y ); + return; + } + subpath_data subpath = { 1, false }; + path.points.push_back( forward * xy( x, y ) ); + path.subpaths.push_back( subpath ); +} + +void canvas::close_path() +{ + if ( path.subpaths.empty() ) + return; + xy first = path.points[ path.points.size() - path.subpaths.back().count ]; + affine_matrix saved_forward = forward; + affine_matrix identity = { 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f }; + forward = identity; + line_to( first.x, first.y ); + path.subpaths.back().closed = true; + move_to( first.x, first.y ); + forward = saved_forward; +} + +void canvas::line_to( + float x, + float y ) +{ + if ( path.subpaths.empty() ) + { + move_to( x, y ); + return; + } + xy point_1 = path.points.back(); + xy point_2 = forward * xy( x, y ); + if ( dot( point_2 - point_1, point_2 - point_1 ) == 0.0f ) + return; + path.points.push_back( point_1 ); + path.points.push_back( point_2 ); + path.points.push_back( point_2 ); + path.subpaths.back().count += 3; +} + +void canvas::quadratic_curve_to( + float control_x, + float control_y, + float x, + float y ) +{ + if ( path.subpaths.empty() ) + move_to( control_x, control_y ); + xy point_1 = path.points.back(); + xy control = forward * xy( control_x, control_y ); + xy point_2 = forward * xy( x, y ); + xy control_1 = lerp( point_1, control, 2.0f / 3.0f ); + xy control_2 = lerp( point_2, control, 2.0f / 3.0f ); + path.points.push_back( control_1 ); + path.points.push_back( control_2 ); + path.points.push_back( point_2 ); + path.subpaths.back().count += 3; +} + +void canvas::bezier_curve_to( + float control_1_x, + float control_1_y, + float control_2_x, + float control_2_y, + float x, + float y ) +{ + if ( path.subpaths.empty() ) + move_to( control_1_x, control_1_y ); + xy control_1 = forward * xy( control_1_x, control_1_y ); + xy control_2 = forward * xy( control_2_x, control_2_y ); + xy point_2 = forward * xy( x, y ); + path.points.push_back( control_1 ); + path.points.push_back( control_2 ); + path.points.push_back( point_2 ); + path.subpaths.back().count += 3; +} + +void canvas::arc_to( + float vertex_x, + float vertex_y, + float x, + float y, + float radius ) +{ + if ( radius < 0.0f || + forward.a * forward.d - forward.b * forward.c == 0.0f ) + return; + if ( path.subpaths.empty() ) + move_to( vertex_x, vertex_y ); + xy point_1 = inverse * path.points.back(); + xy vertex = xy( vertex_x, vertex_y ); + xy point_2 = xy( x, y ); + xy edge_1 = normalized( point_1 - vertex ); + xy edge_2 = normalized( point_2 - vertex ); + float sine = fabsf( dot( perpendicular( edge_1 ), edge_2 ) ); + static float const epsilon = 1.0e-4f; + if ( sine < epsilon ) + { + line_to( vertex_x, vertex_y ); + return; + } + xy offset = radius / sine * ( edge_1 + edge_2 ); + xy center = vertex + offset; + float angle_1 = direction( dot( offset, edge_1 ) * edge_1 - offset ); + float angle_2 = direction( dot( offset, edge_2 ) * edge_2 - offset ); + bool reverse = static_cast< int >( + floorf( ( angle_2 - angle_1 ) / pi ) ) & 1; + arc( center.x, center.y, radius, angle_1, angle_2, reverse ); +} + +void canvas::arc( + float x, + float y, + float radius, + float start_angle, + float end_angle, + bool counter_clockwise ) +{ + if ( radius < 0.0f ) + return; + static float const tau = 2 * pi; + float winding = counter_clockwise ? -1.0f : 1.0f; + float from = fmodf( start_angle, tau ); + float span = fmodf( end_angle, tau ) - from; + if ( ( end_angle - start_angle ) * winding >= tau ) + span = tau * winding; + else if ( span * winding < 0.0f ) + span += tau * winding; + xy centered_1 = radius * xy( cosf( from ), sinf( from ) ); + line_to( x + centered_1.x, y + centered_1.y ); + if ( span == 0.0f ) + return; + int steps = static_cast< int >( + std::max( 1.0f, roundf( 16.0f / tau * span * winding ) ) ); + float segment = span / static_cast< float >( steps ); + float alpha = 4.0f / 3.0f * tanf( 0.25f * segment ); + for ( int step = 0; step < steps; ++step ) + { + float angle = from + static_cast< float >( step + 1 ) * segment; + xy centered_2 = radius * xy( cosf( angle ), sinf( angle ) ); + xy point_1 = xy( x, y ) + centered_1; + xy point_2 = xy( x, y ) + centered_2; + xy control_1 = point_1 + alpha * perpendicular( centered_1 ); + xy control_2 = point_2 - alpha * perpendicular( centered_2 ); + bezier_curve_to( control_1.x, control_1.y, + control_2.x, control_2.y, + point_2.x, point_2.y ); + centered_1 = centered_2; + } +} + +void canvas::rectangle( + float x, + float y, + float width, + float height ) +{ + move_to( x, y ); + line_to( x + width, y ); + line_to( x + width, y + height ); + line_to( x, y + height ); + close_path(); +} + +void canvas::polygon(std::vector points) +{ + move_to(points[0].x, points[0].y); + for (auto pt : points) + line_to(pt.x, pt.y); + close_path(); +} + +void canvas::fill() +{ + path_to_lines( false ); + render_main( fill_brush ); +} + +void canvas::stroke() +{ + path_to_lines( true ); + stroke_lines(); + render_main( stroke_brush ); +} + +void canvas::clip() +{ + path_to_lines( false ); + lines_to_runs( xy( 0.0f, 0.0f ), 0 ); + size_t part = runs.size(); + runs.insert( runs.end(), mask.begin(), mask.end() ); + mask.clear(); + int y = -1; + float last = 0.0f; + float sum_1 = 0.0f; + float sum_2 = 0.0f; + size_t index_1 = 0; + size_t index_2 = part; + while ( index_1 < part && index_2 < runs.size() ) + { + bool which = runs[ index_1 ] < runs[ index_2 ]; + pixel_run next = which ? runs[ index_1 ] : runs[ index_2 ]; + if ( next.y != y ) + { + y = next.y; + last = 0.0f; + sum_1 = 0.0f; + sum_2 = 0.0f; + } + if ( which ) + sum_1 += runs[ index_1++ ].delta; + else + sum_2 += runs[ index_2++ ].delta; + float visibility = ( std::min( fabsf( sum_1 ), 1.0f ) * + std::min( fabsf( sum_2 ), 1.0f ) ); + if ( visibility == last ) + continue; + if ( !mask.empty() && + mask.back().x == next.x && mask.back().y == next.y ) + mask.back().delta += visibility - last; + else + { + pixel_run piece = { next.x, next.y, visibility - last }; + mask.push_back( piece ); + } + last = visibility; + } +} + +bool canvas::is_point_in_path( + float x, + float y ) +{ + path_to_lines( false ); + int winding = 0; + size_t subpath = 0; + size_t beginning = 0; + size_t ending = 0; + for ( size_t index = 0; index < lines.points.size(); ++index ) + { + while ( index >= ending ) + { + beginning = ending; + ending += lines.subpaths[ subpath++ ].count; + } + xy from = lines.points[ index ]; + xy to = lines.points[ index + 1 < ending ? index + 1 : beginning ]; + if ( ( from.y < y && y <= to.y ) || ( to.y < y && y <= from.y ) ) + { + float side = dot( perpendicular( to - from ), xy( x, y ) - from ); + if ( side == 0.0f ) + return true; + winding += side > 0.0f ? 1 : -1; + } + else if ( from.y == y && y == to.y && + ( ( from.x <= x && x <= to.x ) || + ( to.x <= x && x <= from.x ) ) ) + return true; + } + return winding; +} + +void canvas::clear_rectangle( + float x, + float y, + float width, + float height ) +{ + composite_operation saved_operation = global_composite_operation; + float saved_global_alpha = global_alpha; + float saved_alpha = shadow_color.a; + paint_brush::types saved_type = fill_brush.type; + global_composite_operation = destination_out; + global_alpha = 1.0f; + shadow_color.a = 0.0f; + fill_brush.type = paint_brush::color; + fill_rectangle( x, y, width, height ); + fill_brush.type = saved_type; + shadow_color.a = saved_alpha; + global_alpha = saved_global_alpha; + global_composite_operation = saved_operation; +} + +void canvas::fill_rectangle( + float x, + float y, + float width, + float height ) +{ + if ( width == 0.0f || height == 0.0f ) + return; + lines.points.clear(); + lines.subpaths.clear(); + lines.points.push_back( forward * xy( x, y ) ); + lines.points.push_back( forward * xy( x + width, y ) ); + lines.points.push_back( forward * xy( x + width, y + height ) ); + lines.points.push_back( forward * xy( x, y + height ) ); + subpath_data entry = { 4, true }; + lines.subpaths.push_back( entry ); + render_main( fill_brush ); +} + +void canvas::stroke_rectangle( + float x, + float y, + float width, + float height ) +{ + if ( width == 0.0f && height == 0.0f ) + return; + lines.points.clear(); + lines.subpaths.clear(); + if ( width == 0.0f || height == 0.0f ) + { + lines.points.push_back( forward * xy( x, y ) ); + lines.points.push_back( forward * xy( x + width, y + height ) ); + subpath_data entry = { 2, false }; + lines.subpaths.push_back( entry ); + } + else + { + lines.points.push_back( forward * xy( x, y ) ); + lines.points.push_back( forward * xy( x + width, y ) ); + lines.points.push_back( forward * xy( x + width, y + height ) ); + lines.points.push_back( forward * xy( x, y + height ) ); + lines.points.push_back( forward * xy( x, y ) ); + subpath_data entry = { 5, true }; + lines.subpaths.push_back( entry ); + } + stroke_lines(); + render_main( stroke_brush ); +} + +bool canvas::set_font( + unsigned char const *font, + int bytes, + float size ) +{ + if ( font && bytes ) + { + face.data.clear(); + face.cmap = 0; + face.glyf = 0; + face.head = 0; + face.hhea = 0; + face.hmtx = 0; + face.loca = 0; + face.maxp = 0; + face.os_2 = 0; + if ( bytes < 6 ) + return false; + int version = ( font[ 0 ] << 24 | font[ 1 ] << 16 | + font[ 2 ] << 8 | font[ 3 ] << 0 ); + int tables = font[ 4 ] << 8 | font[ 5 ]; + if ( ( version != 0x00010000 && version != 0x74727565 ) || + bytes < tables * 16 + 12 ) + return false; + face.data.insert( face.data.end(), font, font + tables * 16 + 12 ); + for ( int index = 0; index < tables; ++index ) + { + int tag = signed_32( face.data, index * 16 + 12 ); + int offset = signed_32( face.data, index * 16 + 20 ); + int span = signed_32( face.data, index * 16 + 24 ); + if ( bytes < offset + span ) + { + face.data.clear(); + return false; + } + int place = static_cast< int >( face.data.size() ); + if ( tag == 0x636d6170 ) + face.cmap = place; + else if ( tag == 0x676c7966 ) + face.glyf = place; + else if ( tag == 0x68656164 ) + face.head = place; + else if ( tag == 0x68686561 ) + face.hhea = place; + else if ( tag == 0x686d7478 ) + face.hmtx = place; + else if ( tag == 0x6c6f6361 ) + face.loca = place; + else if ( tag == 0x6d617870 ) + face.maxp = place; + else if ( tag == 0x4f532f32 ) + face.os_2 = place; + else + continue; + face.data.insert( + face.data.end(), font + offset, font + offset + span ); + } + if ( !face.cmap || !face.glyf || !face.head || !face.hhea || + !face.hmtx || !face.loca || !face.maxp || !face.os_2 ) + { + face.data.clear(); + return false; + } + } + if ( face.data.empty() ) + return false; + int units_per_em = unsigned_16( face.data, face.head + 18 ); + face.scale = size / static_cast< float >( units_per_em ); + return true; +} + +void canvas::fill_text( + char const *text, + float x, + float y, + float maximum_width ) +{ + text_to_lines( text, xy( x, y ), maximum_width, false ); + render_main( fill_brush ); +} + +void canvas::stroke_text( + char const *text, + float x, + float y, + float maximum_width ) +{ + text_to_lines( text, xy( x, y ), maximum_width, true ); + stroke_lines(); + render_main( stroke_brush ); +} + +float canvas::measure_text( + char const *text ) +{ + if ( face.data.empty() || !text ) + return 0.0f; + int hmetrics = unsigned_16( face.data, face.hhea + 34 ); + int width = 0; + for ( int index = 0; text[ index ]; ) + { + int glyph = character_to_glyph( text, index ); + int entry = std::min( glyph, hmetrics - 1 ); + width += unsigned_16( face.data, face.hmtx + entry * 4 ); + } + return static_cast< float >( width ) * face.scale; +} + +void canvas::draw_image( + unsigned char const *image, + int width, + int height, + int stride, + float x, + float y, + float to_width, + float to_height ) +{ + if ( !image || width <= 0 || height <= 0 || + to_width == 0.0f || to_height == 0.0f ) + return; + std::swap( fill_brush, image_brush ); + set_pattern( fill_style, image, width, height, stride, repeat ); + std::swap( fill_brush, image_brush ); + lines.points.clear(); + lines.subpaths.clear(); + lines.points.push_back( forward * xy( x, y ) ); + lines.points.push_back( forward * xy( x + to_width, y ) ); + lines.points.push_back( forward * xy( x + to_width, y + to_height ) ); + lines.points.push_back( forward * xy( x, y + to_height ) ); + subpath_data entry = { 4, true }; + lines.subpaths.push_back( entry ); + affine_matrix saved_forward = forward; + affine_matrix saved_inverse = inverse; + translate( x + std::min( 0.0f, to_width ), + y + std::min( 0.0f, to_height ) ); + scale( fabsf( to_width ) / static_cast< float >( width ), + fabsf( to_height ) / static_cast< float >( height ) ); + render_main( image_brush ); + forward = saved_forward; + inverse = saved_inverse; +} + +void canvas::get_image_data( + unsigned char *image, + int width, + int height, + int stride, + int x, + int y ) +{ + if ( !image ) + return; + static float const bayer[][ 4 ] = { + { 0.03125f, 0.53125f, 0.15625f, 0.65625f }, + { 0.78125f, 0.28125f, 0.90625f, 0.40625f }, + { 0.21875f, 0.71875f, 0.09375f, 0.59375f }, + { 0.96875f, 0.46875f, 0.84375f, 0.34375f } }; + for ( int image_y = 0; image_y < height; ++image_y ) + for ( int image_x = 0; image_x < width; ++image_x ) + { + int index = image_y * stride + image_x * 4; + int canvas_x = x + image_x; + int canvas_y = y + image_y; + rgba color = rgba( 0.0f, 0.0f, 0.0f, 0.0f ); + if ( 0 <= canvas_x && canvas_x < size_x && + 0 <= canvas_y && canvas_y < size_y ) + color = bitmap[ canvas_y * size_x + canvas_x ]; + float threshold = bayer[ canvas_y & 3 ][ canvas_x & 3 ]; + color = rgba( threshold, threshold, threshold, threshold ) + + 255.0f * delinearized( clamped( unpremultiplied( color ) ) ); + image[ index + 0 ] = static_cast< unsigned char >( color.r ); + image[ index + 1 ] = static_cast< unsigned char >( color.g ); + image[ index + 2 ] = static_cast< unsigned char >( color.b ); + image[ index + 3 ] = static_cast< unsigned char >( color.a ); + } +} + +void canvas::put_image_data( + unsigned char const *image, + int width, + int height, + int stride, + int x, + int y ) +{ + if ( !image ) + return; + for ( int image_y = 0; image_y < height; ++image_y ) + for ( int image_x = 0; image_x < width; ++image_x ) + { + int index = image_y * stride + image_x * 4; + int canvas_x = x + image_x; + int canvas_y = y + image_y; + if ( canvas_x < 0 || size_x <= canvas_x || + canvas_y < 0 || size_y <= canvas_y ) + continue; + rgba color = rgba( + image[ index + 0 ] / 255.0f, image[ index + 1 ] / 255.0f, + image[ index + 2 ] / 255.0f, image[ index + 3 ] / 255.0f ); + bitmap[ canvas_y * size_x + canvas_x ] = + premultiplied( linearized( color ) ); + } +} + +void canvas::save() +{ + canvas *state = new canvas( 0, 0 ); + state->global_composite_operation = global_composite_operation; + state->shadow_offset_x = shadow_offset_x; + state->shadow_offset_y = shadow_offset_y; + state->line_cap = line_cap; + state->line_join = line_join; + state->line_dash_offset = line_dash_offset; + state->text_align = text_align; + state->text_baseline = text_baseline; + state->forward = forward; + state->inverse = inverse; + state->global_alpha = global_alpha; + state->shadow_color = shadow_color; + state->shadow_blur = shadow_blur; + state->line_width = line_width; + state->miter_limit = miter_limit; + state->line_dash = line_dash; + state->fill_brush = fill_brush; + state->stroke_brush = stroke_brush; + state->mask = mask; + state->face = face; + state->saves = saves; + saves = state; +} + +void canvas::restore() +{ + if ( !saves ) + return; + canvas *state = saves; + global_composite_operation = state->global_composite_operation; + shadow_offset_x = state->shadow_offset_x; + shadow_offset_y = state->shadow_offset_y; + line_cap = state->line_cap; + line_join = state->line_join; + line_dash_offset = state->line_dash_offset; + text_align = state->text_align; + text_baseline = state->text_baseline; + forward = state->forward; + inverse = state->inverse; + global_alpha = state->global_alpha; + shadow_color = state->shadow_color; + shadow_blur = state->shadow_blur; + line_width = state->line_width; + miter_limit = state->miter_limit; + line_dash = state->line_dash; + fill_brush = state->fill_brush; + stroke_brush = state->stroke_brush; + mask = state->mask; + face = state->face; + saves = state->saves; + state->saves = 0; + delete state; +} + +} + +#endif // CANVAS_ITY_IMPLEMENTATION diff --git a/containers/test/fonts/ahem-README b/containers/test/fonts/ahem-README new file mode 100644 index 000000000..43cb7a155 --- /dev/null +++ b/containers/test/fonts/ahem-README @@ -0,0 +1,49 @@ +The Ahem font was developed by Todd Fahrner to help test writers +develop predictable tests. The font's em square is exactly square. +Its ascent and descent is exactly the size of the em square. This +means that the font's extent is exactly the same as its line-height, +meaning that it can be exactly aligned with padding, borders, margins, +and so forth. + +The font's alphabetic baseline is 0.2em above its bottom, and 0.8em +below its top. The font has an x-height of 0.8em. + +The font has four main glyphs: + + 'X' U+0058 A square exactly 1em in height and width. + + 'p' U+0070 A rectangle exactly 0.2em high, 1em wide, and aligned so + that its top is flush with the baseline. + + 'É' U+00C9 A rectangle exactly 0.8em high, 1em wide, and aligned so + that its bottom is flush with the baseline. + + ' ' U+0020 A transparent space exactly 1em high and wide. + +Most other US-ASCII characters in the font have the same glyph as X. + +Additionally, the following Unicode formatting characters are assigned +appropriate glyphs so that they may safely be used in tests as well: + + U+2002 - en-space + U+2003 - em-space + U+2004 - 3-per-em space + U+2005 - 4-per-em space + U+2006 - six-per-em space + U+2009 - thin space + U+200A - hair space + U+200B - zero-width space + U+200C - zero-width non-joiner + U+200D - zero-width joiner + U+3000 - ideographic space + U+FEFF - zero-width non-breaking space + +The Ahem font belongs to the public domain as described in COPYING. + +Acknowledgements: + v1 The font was originally created by Todd Fahrner in the late 90s. + v2 It was updated by Paul Nelson in the mid 2000s to add x-height + information to the OS/2 table and to add the space and non-breaking + space glyphs. + v3 It was further updated by Sergey Malkin to include the non-ASCII + space glyphs. diff --git a/containers/test/fonts/ahem.ttf b/containers/test/fonts/ahem.ttf new file mode 100644 index 000000000..a18a56c15 Binary files /dev/null and b/containers/test/fonts/ahem.ttf differ diff --git a/containers/test/fonts/readme.txt b/containers/test/fonts/readme.txt index a09328433..c0ad1e378 100644 --- a/containers/test/fonts/readme.txt +++ b/containers/test/fonts/readme.txt @@ -1,12 +1,17 @@ -Note: Fonts in this directory are not automatically picked up, you have to update Font::installed_fonts. +Note: Fonts in this directory are not automatically picked up, you have to update RasterFont::installed_fonts or OutlineFont::installed_fonts. +-------------------------------------------------------------------------------------------------------- Terminus Font: Sizes: 6x12, 8x14, 8x16, 10x18, 10x20, 11x22, 12x24, 14x28, 16x32. https://terminus-font.sourceforge.net https://sourceforge.net/projects/terminus-font/files/terminus-font-4.49/terminus-font-4.49.1.tar.gz +Original Terminus bold is used as a regular face in test_container. terminus-16px-bold.yaff is further boldened by hand. bdf -> yaff conversion: https://github.com/robhagemans/monobit pip install monobit monobit-convert ter-u14b.bdf to ter-u14b.yaff then manually removed non-ascii chars + +-------------------------------------------------------------------------------------------------------- +Ahem font: https://www.w3.org/Style/CSS/Test/Fonts \ No newline at end of file diff --git a/containers/test/fonts/terminus-ascii-bold-12px.yaff b/containers/test/fonts/terminus-12px.yaff similarity index 90% rename from containers/test/fonts/terminus-ascii-bold-12px.yaff rename to containers/test/fonts/terminus-12px.yaff index a2c91c52d..da71f0307 100644 --- a/containers/test/fonts/terminus-ascii-bold-12px.yaff +++ b/containers/test/fonts/terminus-12px.yaff @@ -1,1463 +1,1478 @@ -name: Terminus Bold 6x12 -spacing: character-cell -cell-size: 6 12 -family: Terminus -foundry: xos4 -copyright: Copyright (C) 2020 Dimitar Toshkov Zhekov -notice: Licensed under the SIL Open Font License, Version 1.1 -point-size: 12 -weight: bold -slant: roman -setwidth: normal -dpi: 72 72 -average-width: 6 -ascent: 10 -descent: 2 -shift-up: -2 -encoding: iso10646-1 -default-char: u+fffd -min-word-space: 6 -converter: monobit v0.32 -source-name: ter-u12b.bdf -source-format: BDF v2.1 -history: load --format=bdf - -u+0000: -char0: - ...... - ...... - @@.@@. - @...@. - ...... - @...@. - @...@. - ...... - @...@. - @@.@@. - ...... - ...... - -u+0020: -space: - ...... - ...... - ...... - ...... - ...... - ...... - ...... - ...... - ...... - ...... - ...... - ...... - -u+0021: -exclam: - ...... - ...... - ..@... - ..@... - ..@... - ..@... - ..@... - ...... - ..@... - ..@... - ...... - ...... - -u+0022: -quotedbl: - ...... - .@.@.. - .@.@.. - .@.@.. - ...... - ...... - ...... - ...... - ...... - ...... - ...... - ...... - -u+0023: -numbersign: - ...... - ...... - .@.@.. - .@.@.. - @@@@@. - .@.@.. - .@.@.. - @@@@@. - .@.@.. - .@.@.. - ...... - ...... - -u+0024: -dollar: - ...... - ...... - ..@... - .@@@.. - @.@.@. - @.@... - .@@@.. - ..@.@. - @.@.@. - .@@@.. - ..@... - ...... - -u+0025: -percent: - ...... - ...... - .@..@. - @.@.@. - .@.@.. - ...@.. - ..@... - ..@.@. - .@.@.@ - .@..@. - ...... - ...... - -u+0026: -ampersand: - ...... - ...... - ..@... - .@.@.. - .@.@.. - ..@... - .@@.@. - @..@.. - @..@.. - .@@.@. - ...... - ...... - -u+0027: -quotesingle: - ...... - ..@... - ..@... - ..@... - ...... - ...... - ...... - ...... - ...... - ...... - ...... - ...... - -u+0028: -parenleft: - ...... - ...... - ...@.. - ..@... - .@.... - .@.... - .@.... - .@.... - ..@... - ...@.. - ...... - ...... - -u+0029: -parenright: - ...... - ...... - .@.... - ..@... - ...@.. - ...@.. - ...@.. - ...@.. - ..@... - .@.... - ...... - ...... - -u+002a: -asterisk: - ...... - ...... - ...... - ...... - .@.@.. - ..@... - @@@@@. - ..@... - .@.@.. - ...... - ...... - ...... - -u+002b: -plus: - ...... - ...... - ...... - ...... - ..@... - ..@... - @@@@@. - ..@... - ..@... - ...... - ...... - ...... - -u+002c: -comma: - ...... - ...... - ...... - ...... - ...... - ...... - ...... - ...... - ..@... - ..@... - .@.... - ...... - -u+002d: -hyphen: - ...... - ...... - ...... - ...... - ...... - ...... - @@@@@. - ...... - ...... - ...... - ...... - ...... - -u+002e: -period: - ...... - ...... - ...... - ...... - ...... - ...... - ...... - ...... - ..@... - ..@... - ...... - ...... - -u+002f: -slash: - ...... - ...... - ....@. - ....@. - ...@.. - ...@.. - ..@... - ..@... - .@.... - .@.... - ...... - ...... - -u+0030: -zero: - ...... - ...... - .@@@.. - @...@. - @..@@. - @.@.@. - @@..@. - @...@. - @...@. - .@@@.. - ...... - ...... - -u+0031: -one: - ...... - ...... - ..@... - .@@... - ..@... - ..@... - ..@... - ..@... - ..@... - .@@@.. - ...... - ...... - -u+0032: -two: - ...... - ...... - .@@@.. - @...@. - @...@. - ....@. - ...@.. - ..@... - .@.... - @@@@@. - ...... - ...... - -u+0033: -three: - ...... - ...... - .@@@.. - @...@. - ....@. - ..@@.. - ....@. - ....@. - @...@. - .@@@.. - ...... - ...... - -u+0034: -four: - ...... - ...... - ....@. - ...@@. - ..@.@. - .@..@. - @...@. - @@@@@. - ....@. - ....@. - ...... - ...... - -u+0035: -five: - ...... - ...... - @@@@@. - @..... - @..... - @@@@.. - ....@. - ....@. - @...@. - .@@@.. - ...... - ...... - -u+0036: -six: - ...... - ...... - .@@@.. - @..... - @..... - @@@@.. - @...@. - @...@. - @...@. - .@@@.. - ...... - ...... - -u+0037: -seven: - ...... - ...... - @@@@@. - ....@. - ....@. - ...@.. - ...@.. - ..@... - ..@... - ..@... - ...... - ...... - -u+0038: -eight: - ...... - ...... - .@@@.. - @...@. - @...@. - .@@@.. - @...@. - @...@. - @...@. - .@@@.. - ...... - ...... - -u+0039: -nine: - ...... - ...... - .@@@.. - @...@. - @...@. - @...@. - .@@@@. - ....@. - ....@. - .@@@.. - ...... - ...... - -u+003a: -colon: - ...... - ...... - ...... - ...... - ..@... - ..@... - ...... - ...... - ..@... - ..@... - ...... - ...... - -u+003b: -semicolon: - ...... - ...... - ...... - ...... - ..@... - ..@... - ...... - ...... - ..@... - ..@... - .@.... - ...... - -u+003c: -less: - ...... - ...... - ...... - ....@. - ...@.. - ..@... - .@.... - ..@... - ...@.. - ....@. - ...... - ...... - -u+003d: -equal: - ...... - ...... - ...... - ...... - @@@@@. - ...... - ...... - @@@@@. - ...... - ...... - ...... - ...... - -u+003e: -greater: - ...... - ...... - ...... - .@.... - ..@... - ...@.. - ....@. - ...@.. - ..@... - .@.... - ...... - ...... - -u+003f: -question: - ...... - ...... - .@@@.. - @...@. - @...@. - ...@.. - ..@... - ...... - ..@... - ..@... - ...... - ...... - -u+0040: -at: - ...... - ...... - .@@@.. - @...@. - @..@@. - @.@.@. - @.@.@. - @..@@. - @..... - .@@@@. - ...... - ...... - -u+0041: -"A": - ...... - ...... - .@@@.. - @...@. - @...@. - @...@. - @@@@@. - @...@. - @...@. - @...@. - ...... - ...... - -u+0042: -"B": - ...... - ...... - @@@@.. - @...@. - @...@. - @@@@.. - @...@. - @...@. - @...@. - @@@@.. - ...... - ...... - -u+0043: -"C": - ...... - ...... - .@@@.. - @...@. - @..... - @..... - @..... - @..... - @...@. - .@@@.. - ...... - ...... - -u+0044: -"D": - ...... - ...... - @@@... - @..@.. - @...@. - @...@. - @...@. - @...@. - @..@.. - @@@... - ...... - ...... - -u+0045: -"E": - ...... - ...... - @@@@@. - @..... - @..... - @@@@.. - @..... - @..... - @..... - @@@@@. - ...... - ...... - -u+0046: -"F": - ...... - ...... - @@@@@. - @..... - @..... - @@@@.. - @..... - @..... - @..... - @..... - ...... - ...... - -u+0047: -"G": - ...... - ...... - .@@@.. - @...@. - @..... - @..... - @.@@@. - @...@. - @...@. - .@@@.. - ...... - ...... - -u+0048: -"H": - ...... - ...... - @...@. - @...@. - @...@. - @@@@@. - @...@. - @...@. - @...@. - @...@. - ...... - ...... - -u+0049: -"I": - ...... - ...... - .@@@.. - ..@... - ..@... - ..@... - ..@... - ..@... - ..@... - .@@@.. - ...... - ...... - -u+004a: -"J": - ...... - ...... - ..@@@. - ...@.. - ...@.. - ...@.. - ...@.. - @..@.. - @..@.. - .@@... - ...... - ...... - -u+004b: -"K": - ...... - ...... - @...@. - @..@.. - @.@... - @@.... - @@.... - @.@... - @..@.. - @...@. - ...... - ...... - -u+004c: -"L": - ...... - ...... - @..... - @..... - @..... - @..... - @..... - @..... - @..... - @@@@@. - ...... - ...... - -u+004d: -"M": - ...... - ...... - @...@. - @@.@@. - @.@.@. - @.@.@. - @...@. - @...@. - @...@. - @...@. - ...... - ...... - -u+004e: -"N": - ...... - ...... - @...@. - @...@. - @@..@. - @.@.@. - @..@@. - @...@. - @...@. - @...@. - ...... - ...... - -u+004f: -"O": - ...... - ...... - .@@@.. - @...@. - @...@. - @...@. - @...@. - @...@. - @...@. - .@@@.. - ...... - ...... - -u+0050: -"P": - ...... - ...... - @@@@.. - @...@. - @...@. - @...@. - @@@@.. - @..... - @..... - @..... - ...... - ...... - -u+0051: -"Q": - ...... - ...... - .@@@.. - @...@. - @...@. - @...@. - @...@. - @...@. - @.@.@. - .@@@.. - ....@. - ...... - -u+0052: -"R": - ...... - ...... - @@@@.. - @...@. - @...@. - @...@. - @@@@.. - @.@... - @..@.. - @...@. - ...... - ...... - -u+0053: -"S": - ...... - ...... - .@@@.. - @...@. - @..... - .@@@.. - ....@. - ....@. - @...@. - .@@@.. - ...... - ...... - -u+0054: -"T": - ...... - ...... - @@@@@. - ..@... - ..@... - ..@... - ..@... - ..@... - ..@... - ..@... - ...... - ...... - -u+0055: -"U": - ...... - ...... - @...@. - @...@. - @...@. - @...@. - @...@. - @...@. - @...@. - .@@@.. - ...... - ...... - -u+0056: -"V": - ...... - ...... - @...@. - @...@. - @...@. - .@.@.. - .@.@.. - .@.@.. - ..@... - ..@... - ...... - ...... - -u+0057: -"W": - ...... - ...... - @...@. - @...@. - @...@. - @...@. - @.@.@. - @.@.@. - @@.@@. - @...@. - ...... - ...... - -u+0058: -"X": - ...... - ...... - @...@. - @...@. - .@.@.. - ..@... - ..@... - .@.@.. - @...@. - @...@. - ...... - ...... - -u+0059: -"Y": - ...... - ...... - @...@. - @...@. - .@.@.. - .@.@.. - ..@... - ..@... - ..@... - ..@... - ...... - ...... - -u+005a: -"Z": - ...... - ...... - @@@@@. - ....@. - ...@.. - ..@... - .@.... - @..... - @..... - @@@@@. - ...... - ...... - -u+005b: -bracketleft: - ...... - ...... - .@@@.. - .@.... - .@.... - .@.... - .@.... - .@.... - .@.... - .@@@.. - ...... - ...... - -u+005c: -backslash: - ...... - ...... - .@.... - .@.... - ..@... - ..@... - ...@.. - ...@.. - ....@. - ....@. - ...... - ...... - -u+005d: -bracketright: - ...... - ...... - .@@@.. - ...@.. - ...@.. - ...@.. - ...@.. - ...@.. - ...@.. - .@@@.. - ...... - ...... - -u+005e: -asciicircum: - ...... - ..@... - .@.@.. - @...@. - ...... - ...... - ...... - ...... - ...... - ...... - ...... - ...... - -u+005f: -underscore: - ...... - ...... - ...... - ...... - ...... - ...... - ...... - ...... - ...... - ...... - @@@@@. - ...... - -u+0060: -grave: - .@.... - ..@... - ...... - ...... - ...... - ...... - ...... - ...... - ...... - ...... - ...... - ...... - -u+0061: -"a": - ...... - ...... - ...... - ...... - .@@@.. - ....@. - .@@@@. - @...@. - @...@. - .@@@@. - ...... - ...... - -u+0062: -"b": - ...... - ...... - @..... - @..... - @@@@.. - @...@. - @...@. - @...@. - @...@. - @@@@.. - ...... - ...... - -u+0063: -"c": - ...... - ...... - ...... - ...... - .@@@.. - @...@. - @..... - @..... - @...@. - .@@@.. - ...... - ...... - -u+0064: -"d": - ...... - ...... - ....@. - ....@. - .@@@@. - @...@. - @...@. - @...@. - @...@. - .@@@@. - ...... - ...... - -u+0065: -"e": - ...... - ...... - ...... - ...... - .@@@.. - @...@. - @@@@@. - @..... - @..... - .@@@@. - ...... - ...... - -u+0066: -"f": - ...... - ...... - ...@@. - ..@... - .@@@.. - ..@... - ..@... - ..@... - ..@... - ..@... - ...... - ...... - -u+0067: -"g": - ...... - ...... - ...... - ...... - .@@@@. - @...@. - @...@. - @...@. - @...@. - .@@@@. - ....@. - .@@@.. - -u+0068: -"h": - ...... - ...... - @..... - @..... - @@@@.. - @...@. - @...@. - @...@. - @...@. - @...@. - ...... - ...... - -u+0069: -"i": - ...... - ..@... - ..@... - ...... - .@@... - ..@... - ..@... - ..@... - ..@... - .@@@.. - ...... - ...... - -u+006a: -"j": - ...... - ....@. - ....@. - ...... - ...@@. - ....@. - ....@. - ....@. - ....@. - ....@. - .@..@. - ..@@.. - -u+006b: -"k": - ...... - ...... - .@.... - .@.... - .@..@. - .@.@.. - .@@... - .@@... - .@.@.. - .@..@. - ...... - ...... - -u+006c: -"l": - ...... - ...... - .@@... - ..@... - ..@... - ..@... - ..@... - ..@... - ..@... - .@@@.. - ...... - ...... - -u+006d: -"m": - ...... - ...... - ...... - ...... - @@@@.. - @.@.@. - @.@.@. - @.@.@. - @.@.@. - @.@.@. - ...... - ...... - -u+006e: -"n": - ...... - ...... - ...... - ...... - @@@@.. - @...@. - @...@. - @...@. - @...@. - @...@. - ...... - ...... - -u+006f: -"o": - ...... - ...... - ...... - ...... - .@@@.. - @...@. - @...@. - @...@. - @...@. - .@@@.. - ...... - ...... - -u+0070: -"p": - ...... - ...... - ...... - ...... - @@@@.. - @...@. - @...@. - @...@. - @...@. - @@@@.. - @..... - @..... - -u+0071: -"q": - ...... - ...... - ...... - ...... - .@@@@. - @...@. - @...@. - @...@. - @...@. - .@@@@. - ....@. - ....@. - -u+0072: -"r": - ...... - ...... - ...... - ...... - @.@@@. - @@.... - @..... - @..... - @..... - @..... - ...... - ...... - -u+0073: -"s": - ...... - ...... - ...... - ...... - .@@@@. - @..... - .@@@.. - ....@. - ....@. - @@@@.. - ...... - ...... - -u+0074: -"t": - ...... - ...... - ..@... - ..@... - .@@@.. - ..@... - ..@... - ..@... - ..@... - ...@@. - ...... - ...... - -u+0075: -"u": - ...... - ...... - ...... - ...... - @...@. - @...@. - @...@. - @...@. - @...@. - .@@@@. - ...... - ...... - -u+0076: -"v": - ...... - ...... - ...... - ...... - @...@. - @...@. - .@.@.. - .@.@.. - ..@... - ..@... - ...... - ...... - -u+0077: -"w": - ...... - ...... - ...... - ...... - @...@. - @...@. - @.@.@. - @.@.@. - @.@.@. - .@@@.. - ...... - ...... - -u+0078: -"x": - ...... - ...... - ...... - ...... - @...@. - .@.@.. - ..@... - ..@... - .@.@.. - @...@. - ...... - ...... - -u+0079: -"y": - ...... - ...... - ...... - ...... - @...@. - @...@. - @...@. - @...@. - @...@. - .@@@@. - ....@. - .@@@.. - -u+007a: -"z": - ...... - ...... - ...... - ...... - @@@@@. - ...@.. - ..@... - .@.... - @..... - @@@@@. - ...... - ...... - -u+007b: -braceleft: - ...... - ...... - ...@@. - ..@... - ..@... - .@.... - ..@... - ..@... - ..@... - ...@@. - ...... - ...... - -u+007c: -bar: - ...... - ...... - ..@... - ..@... - ..@... - ..@... - ..@... - ..@... - ..@... - ..@... - ...... - ...... - -u+007d: -braceright: - ...... - ...... - .@@... - ...@.. - ...@.. - ....@. - ...@.. - ...@.. - ...@.. - .@@... - ...... - ...... - -u+007e: -asciitilde: - ...... - .@..@. - @.@.@. - @..@.. - ...... - ...... - ...... - ...... - ...... - ...... - ...... - ...... +name: Terminus Bold 6x12 +spacing: character-cell +cell-size: 6 12 +family: Terminus +foundry: xos4 +copyright: Copyright (C) 2020 Dimitar Toshkov Zhekov +notice: Licensed under the SIL Open Font License, Version 1.1 +point-size: 12 +weight: bold +slant: roman +setwidth: normal +dpi: 72 72 +average-width: 6 +ascent: 10 +descent: 2 +shift-up: -2 +encoding: iso10646-1 +default-char: u+fffd +min-word-space: 6 +converter: monobit v0.32 +source-name: ter-u12b.bdf +source-format: BDF v2.1 +history: load --format=bdf + +u+0000: +char0: + ...... + ...... + @@.@@. + @...@. + ...... + @...@. + @...@. + ...... + @...@. + @@.@@. + ...... + ...... + +u+0020: +space: + ...... + ...... + ...... + ...... + ...... + ...... + ...... + ...... + ...... + ...... + ...... + ...... + +u+0021: +exclam: + ...... + ...... + ..@... + ..@... + ..@... + ..@... + ..@... + ...... + ..@... + ..@... + ...... + ...... + +u+0022: +quotedbl: + ...... + .@.@.. + .@.@.. + .@.@.. + ...... + ...... + ...... + ...... + ...... + ...... + ...... + ...... + +u+0023: +numbersign: + ...... + ...... + .@.@.. + .@.@.. + @@@@@. + .@.@.. + .@.@.. + @@@@@. + .@.@.. + .@.@.. + ...... + ...... + +u+0024: +dollar: + ...... + ...... + ..@... + .@@@.. + @.@.@. + @.@... + .@@@.. + ..@.@. + @.@.@. + .@@@.. + ..@... + ...... + +u+0025: +percent: + ...... + ...... + .@..@. + @.@.@. + .@.@.. + ...@.. + ..@... + ..@.@. + .@.@.@ + .@..@. + ...... + ...... + +u+0026: +ampersand: + ...... + ...... + ..@... + .@.@.. + .@.@.. + ..@... + .@@.@. + @..@.. + @..@.. + .@@.@. + ...... + ...... + +u+0027: +quotesingle: + ...... + ..@... + ..@... + ..@... + ...... + ...... + ...... + ...... + ...... + ...... + ...... + ...... + +u+0028: +parenleft: + ...... + ...... + ...@.. + ..@... + .@.... + .@.... + .@.... + .@.... + ..@... + ...@.. + ...... + ...... + +u+0029: +parenright: + ...... + ...... + .@.... + ..@... + ...@.. + ...@.. + ...@.. + ...@.. + ..@... + .@.... + ...... + ...... + +u+002a: +asterisk: + ...... + ...... + ...... + ...... + .@.@.. + ..@... + @@@@@. + ..@... + .@.@.. + ...... + ...... + ...... + +u+002b: +plus: + ...... + ...... + ...... + ...... + ..@... + ..@... + @@@@@. + ..@... + ..@... + ...... + ...... + ...... + +u+002c: +comma: + ...... + ...... + ...... + ...... + ...... + ...... + ...... + ...... + ..@... + ..@... + .@.... + ...... + +u+002d: +hyphen: + ...... + ...... + ...... + ...... + ...... + ...... + @@@@@. + ...... + ...... + ...... + ...... + ...... + +u+002e: +period: + ...... + ...... + ...... + ...... + ...... + ...... + ...... + ...... + ..@... + ..@... + ...... + ...... + +u+002f: +slash: + ...... + ...... + ....@. + ....@. + ...@.. + ...@.. + ..@... + ..@... + .@.... + .@.... + ...... + ...... + +u+0030: +zero: + ...... + ...... + .@@@.. + @...@. + @..@@. + @.@.@. + @@..@. + @...@. + @...@. + .@@@.. + ...... + ...... + +u+0031: +one: + ...... + ...... + ..@... + .@@... + ..@... + ..@... + ..@... + ..@... + ..@... + .@@@.. + ...... + ...... + +u+0032: +two: + ...... + ...... + .@@@.. + @...@. + @...@. + ....@. + ...@.. + ..@... + .@.... + @@@@@. + ...... + ...... + +u+0033: +three: + ...... + ...... + .@@@.. + @...@. + ....@. + ..@@.. + ....@. + ....@. + @...@. + .@@@.. + ...... + ...... + +u+0034: +four: + ...... + ...... + ....@. + ...@@. + ..@.@. + .@..@. + @...@. + @@@@@. + ....@. + ....@. + ...... + ...... + +u+0035: +five: + ...... + ...... + @@@@@. + @..... + @..... + @@@@.. + ....@. + ....@. + @...@. + .@@@.. + ...... + ...... + +u+0036: +six: + ...... + ...... + .@@@.. + @..... + @..... + @@@@.. + @...@. + @...@. + @...@. + .@@@.. + ...... + ...... + +u+0037: +seven: + ...... + ...... + @@@@@. + ....@. + ....@. + ...@.. + ...@.. + ..@... + ..@... + ..@... + ...... + ...... + +u+0038: +eight: + ...... + ...... + .@@@.. + @...@. + @...@. + .@@@.. + @...@. + @...@. + @...@. + .@@@.. + ...... + ...... + +u+0039: +nine: + ...... + ...... + .@@@.. + @...@. + @...@. + @...@. + .@@@@. + ....@. + ....@. + .@@@.. + ...... + ...... + +u+003a: +colon: + ...... + ...... + ...... + ...... + ..@... + ..@... + ...... + ...... + ..@... + ..@... + ...... + ...... + +u+003b: +semicolon: + ...... + ...... + ...... + ...... + ..@... + ..@... + ...... + ...... + ..@... + ..@... + .@.... + ...... + +u+003c: +less: + ...... + ...... + ...... + ....@. + ...@.. + ..@... + .@.... + ..@... + ...@.. + ....@. + ...... + ...... + +u+003d: +equal: + ...... + ...... + ...... + ...... + @@@@@. + ...... + ...... + @@@@@. + ...... + ...... + ...... + ...... + +u+003e: +greater: + ...... + ...... + ...... + .@.... + ..@... + ...@.. + ....@. + ...@.. + ..@... + .@.... + ...... + ...... + +u+003f: +question: + ...... + ...... + .@@@.. + @...@. + @...@. + ...@.. + ..@... + ...... + ..@... + ..@... + ...... + ...... + +u+0040: +at: + ...... + ...... + .@@@.. + @...@. + @..@@. + @.@.@. + @.@.@. + @..@@. + @..... + .@@@@. + ...... + ...... + +u+0041: +"A": + ...... + ...... + .@@@.. + @...@. + @...@. + @...@. + @@@@@. + @...@. + @...@. + @...@. + ...... + ...... + +u+0042: +"B": + ...... + ...... + @@@@.. + @...@. + @...@. + @@@@.. + @...@. + @...@. + @...@. + @@@@.. + ...... + ...... + +u+0043: +"C": + ...... + ...... + .@@@.. + @...@. + @..... + @..... + @..... + @..... + @...@. + .@@@.. + ...... + ...... + +u+0044: +"D": + ...... + ...... + @@@... + @..@.. + @...@. + @...@. + @...@. + @...@. + @..@.. + @@@... + ...... + ...... + +u+0045: +"E": + ...... + ...... + @@@@@. + @..... + @..... + @@@@.. + @..... + @..... + @..... + @@@@@. + ...... + ...... + +u+0046: +"F": + ...... + ...... + @@@@@. + @..... + @..... + @@@@.. + @..... + @..... + @..... + @..... + ...... + ...... + +u+0047: +"G": + ...... + ...... + .@@@.. + @...@. + @..... + @..... + @.@@@. + @...@. + @...@. + .@@@.. + ...... + ...... + +u+0048: +"H": + ...... + ...... + @...@. + @...@. + @...@. + @@@@@. + @...@. + @...@. + @...@. + @...@. + ...... + ...... + +u+0049: +"I": + ...... + ...... + .@@@.. + ..@... + ..@... + ..@... + ..@... + ..@... + ..@... + .@@@.. + ...... + ...... + +u+004a: +"J": + ...... + ...... + ..@@@. + ...@.. + ...@.. + ...@.. + ...@.. + @..@.. + @..@.. + .@@... + ...... + ...... + +u+004b: +"K": + ...... + ...... + @...@. + @..@.. + @.@... + @@.... + @@.... + @.@... + @..@.. + @...@. + ...... + ...... + +u+004c: +"L": + ...... + ...... + @..... + @..... + @..... + @..... + @..... + @..... + @..... + @@@@@. + ...... + ...... + +u+004d: +"M": + ...... + ...... + @...@. + @@.@@. + @.@.@. + @.@.@. + @...@. + @...@. + @...@. + @...@. + ...... + ...... + +u+004e: +"N": + ...... + ...... + @...@. + @...@. + @@..@. + @.@.@. + @..@@. + @...@. + @...@. + @...@. + ...... + ...... + +u+004f: +"O": + ...... + ...... + .@@@.. + @...@. + @...@. + @...@. + @...@. + @...@. + @...@. + .@@@.. + ...... + ...... + +u+0050: +"P": + ...... + ...... + @@@@.. + @...@. + @...@. + @...@. + @@@@.. + @..... + @..... + @..... + ...... + ...... + +u+0051: +"Q": + ...... + ...... + .@@@.. + @...@. + @...@. + @...@. + @...@. + @...@. + @.@.@. + .@@@.. + ....@. + ...... + +u+0052: +"R": + ...... + ...... + @@@@.. + @...@. + @...@. + @...@. + @@@@.. + @.@... + @..@.. + @...@. + ...... + ...... + +u+0053: +"S": + ...... + ...... + .@@@.. + @...@. + @..... + .@@@.. + ....@. + ....@. + @...@. + .@@@.. + ...... + ...... + +u+0054: +"T": + ...... + ...... + @@@@@. + ..@... + ..@... + ..@... + ..@... + ..@... + ..@... + ..@... + ...... + ...... + +u+0055: +"U": + ...... + ...... + @...@. + @...@. + @...@. + @...@. + @...@. + @...@. + @...@. + .@@@.. + ...... + ...... + +u+0056: +"V": + ...... + ...... + @...@. + @...@. + @...@. + .@.@.. + .@.@.. + .@.@.. + ..@... + ..@... + ...... + ...... + +u+0057: +"W": + ...... + ...... + @...@. + @...@. + @...@. + @...@. + @.@.@. + @.@.@. + @@.@@. + @...@. + ...... + ...... + +u+0058: +"X": + ...... + ...... + @...@. + @...@. + .@.@.. + ..@... + ..@... + .@.@.. + @...@. + @...@. + ...... + ...... + +u+0059: +"Y": + ...... + ...... + @...@. + @...@. + .@.@.. + .@.@.. + ..@... + ..@... + ..@... + ..@... + ...... + ...... + +u+005a: +"Z": + ...... + ...... + @@@@@. + ....@. + ...@.. + ..@... + .@.... + @..... + @..... + @@@@@. + ...... + ...... + +u+005b: +bracketleft: + ...... + ...... + .@@@.. + .@.... + .@.... + .@.... + .@.... + .@.... + .@.... + .@@@.. + ...... + ...... + +u+005c: +backslash: + ...... + ...... + .@.... + .@.... + ..@... + ..@... + ...@.. + ...@.. + ....@. + ....@. + ...... + ...... + +u+005d: +bracketright: + ...... + ...... + .@@@.. + ...@.. + ...@.. + ...@.. + ...@.. + ...@.. + ...@.. + .@@@.. + ...... + ...... + +u+005e: +asciicircum: + ...... + ..@... + .@.@.. + @...@. + ...... + ...... + ...... + ...... + ...... + ...... + ...... + ...... + +u+005f: +underscore: + ...... + ...... + ...... + ...... + ...... + ...... + ...... + ...... + ...... + ...... + @@@@@. + ...... + +u+0060: +grave: + .@.... + ..@... + ...... + ...... + ...... + ...... + ...... + ...... + ...... + ...... + ...... + ...... + +u+0061: +"a": + ...... + ...... + ...... + ...... + .@@@.. + ....@. + .@@@@. + @...@. + @...@. + .@@@@. + ...... + ...... + +u+0062: +"b": + ...... + ...... + @..... + @..... + @@@@.. + @...@. + @...@. + @...@. + @...@. + @@@@.. + ...... + ...... + +u+0063: +"c": + ...... + ...... + ...... + ...... + .@@@.. + @...@. + @..... + @..... + @...@. + .@@@.. + ...... + ...... + +u+0064: +"d": + ...... + ...... + ....@. + ....@. + .@@@@. + @...@. + @...@. + @...@. + @...@. + .@@@@. + ...... + ...... + +u+0065: +"e": + ...... + ...... + ...... + ...... + .@@@.. + @...@. + @@@@@. + @..... + @..... + .@@@@. + ...... + ...... + +u+0066: +"f": + ...... + ...... + ...@@. + ..@... + .@@@.. + ..@... + ..@... + ..@... + ..@... + ..@... + ...... + ...... + +u+0067: +"g": + ...... + ...... + ...... + ...... + .@@@@. + @...@. + @...@. + @...@. + @...@. + .@@@@. + ....@. + .@@@.. + +u+0068: +"h": + ...... + ...... + @..... + @..... + @@@@.. + @...@. + @...@. + @...@. + @...@. + @...@. + ...... + ...... + +u+0069: +"i": + ...... + ..@... + ..@... + ...... + .@@... + ..@... + ..@... + ..@... + ..@... + .@@@.. + ...... + ...... + +u+006a: +"j": + ...... + ....@. + ....@. + ...... + ...@@. + ....@. + ....@. + ....@. + ....@. + ....@. + .@..@. + ..@@.. + +u+006b: +"k": + ...... + ...... + .@.... + .@.... + .@..@. + .@.@.. + .@@... + .@@... + .@.@.. + .@..@. + ...... + ...... + +u+006c: +"l": + ...... + ...... + .@@... + ..@... + ..@... + ..@... + ..@... + ..@... + ..@... + .@@@.. + ...... + ...... + +u+006d: +"m": + ...... + ...... + ...... + ...... + @@@@.. + @.@.@. + @.@.@. + @.@.@. + @.@.@. + @.@.@. + ...... + ...... + +u+006e: +"n": + ...... + ...... + ...... + ...... + @@@@.. + @...@. + @...@. + @...@. + @...@. + @...@. + ...... + ...... + +u+006f: +"o": + ...... + ...... + ...... + ...... + .@@@.. + @...@. + @...@. + @...@. + @...@. + .@@@.. + ...... + ...... + +u+0070: +"p": + ...... + ...... + ...... + ...... + @@@@.. + @...@. + @...@. + @...@. + @...@. + @@@@.. + @..... + @..... + +u+0071: +"q": + ...... + ...... + ...... + ...... + .@@@@. + @...@. + @...@. + @...@. + @...@. + .@@@@. + ....@. + ....@. + +u+0072: +"r": + ...... + ...... + ...... + ...... + @.@@@. + @@.... + @..... + @..... + @..... + @..... + ...... + ...... + +u+0073: +"s": + ...... + ...... + ...... + ...... + .@@@@. + @..... + .@@@.. + ....@. + ....@. + @@@@.. + ...... + ...... + +u+0074: +"t": + ...... + ...... + ..@... + ..@... + .@@@.. + ..@... + ..@... + ..@... + ..@... + ...@@. + ...... + ...... + +u+0075: +"u": + ...... + ...... + ...... + ...... + @...@. + @...@. + @...@. + @...@. + @...@. + .@@@@. + ...... + ...... + +u+0076: +"v": + ...... + ...... + ...... + ...... + @...@. + @...@. + .@.@.. + .@.@.. + ..@... + ..@... + ...... + ...... + +u+0077: +"w": + ...... + ...... + ...... + ...... + @...@. + @...@. + @.@.@. + @.@.@. + @.@.@. + .@@@.. + ...... + ...... + +u+0078: +"x": + ...... + ...... + ...... + ...... + @...@. + .@.@.. + ..@... + ..@... + .@.@.. + @...@. + ...... + ...... + +u+0079: +"y": + ...... + ...... + ...... + ...... + @...@. + @...@. + @...@. + @...@. + @...@. + .@@@@. + ....@. + .@@@.. + +u+007a: +"z": + ...... + ...... + ...... + ...... + @@@@@. + ...@.. + ..@... + .@.... + @..... + @@@@@. + ...... + ...... + +u+007b: +braceleft: + ...... + ...... + ...@@. + ..@... + ..@... + .@.... + ..@... + ..@... + ..@... + ...@@. + ...... + ...... + +u+007c: +bar: + ...... + ...... + ..@... + ..@... + ..@... + ..@... + ..@... + ..@... + ..@... + ..@... + ...... + ...... + +u+007d: +braceright: + ...... + ...... + .@@... + ...@.. + ...@.. + ....@. + ...@.. + ...@.. + ...@.. + .@@... + ...... + ...... + +u+007e: +asciitilde: + ...... + .@..@. + @.@.@. + @..@.. + ...... + ...... + ...... + ...... + ...... + ...... + ...... + ...... + +u+00a0: +nbspace: + ...... + ...... + ...... + ...... + ...... + ...... + ...... + ...... + ...... + ...... + ...... + ...... diff --git a/containers/test/fonts/terminus-ascii-bold-14px.yaff b/containers/test/fonts/terminus-14px.yaff similarity index 98% rename from containers/test/fonts/terminus-ascii-bold-14px.yaff rename to containers/test/fonts/terminus-14px.yaff index 5ce69e881..f3b90312e 100644 --- a/containers/test/fonts/terminus-ascii-bold-14px.yaff +++ b/containers/test/fonts/terminus-14px.yaff @@ -1654,3 +1654,19 @@ asciitilde: ........ ........ +u+00a0: +nbspace: + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ diff --git a/containers/test/fonts/terminus-16px-bold.yaff b/containers/test/fonts/terminus-16px-bold.yaff new file mode 100644 index 000000000..7a2bf0bc1 --- /dev/null +++ b/containers/test/fonts/terminus-16px-bold.yaff @@ -0,0 +1,1866 @@ +name: Terminus ASCII Bold 8x16 +spacing: character-cell +cell-size: 8 16 +family: Terminus ASCII +foundry: xos4 +copyright: Copyright (C) 2020 Dimitar Toshkov Zhekov +notice: Licensed under the SIL Open Font License, Version 1.1 +point-size: 16 +weight: bold +slant: roman +setwidth: normal +dpi: 72 72 +average-width: 8 +ascent: 12 +descent: 4 +shift-up: -4 +encoding: iso10646-1 +default-char: u+fffd +min-word-space: 8 +converter: monobit v0.32 +source-name: ter-u16b.bdf +source-format: BDF v2.1 +history: load --format=bdf + +u+0000: +char0: + ........ + ........ + @@@.@@@. + @@...@@. + ........ + @@...@@. + @@...@@. + @@...@@. + ........ + @@...@@. + @@...@@. + @@@.@@@. + ........ + ........ + ........ + ........ + +u+0020: +space: + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + +u+0021: +exclam: + ........ + ........ + ..@@@... + ..@@@... + ..@@@... + ..@@@... + ..@@@... + ..@@@... + ..@@@... + ........ + ..@@@... + ..@@@... + ........ + ........ + ........ + ........ + +u+0022: +quotedbl: + ........ + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + +u+0023: +numbersign: + ........ + ........ + .@@@.@@. + .@@@.@@. + .@@@.@@. + @@@@@@@@ + .@@@.@@. + .@@@.@@. + @@@@@@@@ + .@@@.@@. + .@@@.@@. + .@@@.@@. + ........ + ........ + ........ + ........ + +u+0024: +dollar: + ........ + ...@.... + ...@.... + .@@@@@.. + @@@..@@. + @@@..... + @@@..... + .@@@@@.. + ....@@@. + ....@@@. + @@..@@@. + .@@@@@.. + ...@.... + ...@.... + ........ + ........ + +u+0025: +percent: + ........ + ........ + @@@.@@@. + @@@.@@@. + @@@@@@.. + ...@@@.. + ..@@@... + ..@@@... + .@@@.... + .@@@@@@. + @@@.@@@. + @@@.@@@. + ........ + ........ + ........ + ........ + +u+0026: +ampersand: + ........ + ........ + .@@@@... + @@@.@@.. + @@@.@@.. + .@@@@... + .@@@@.@@ + @@@@@@@. + @@@.@@.. + @@@.@@.. + @@@@@@@. + .@@@.@@@ + ........ + ........ + ........ + ........ + +u+0027: +quotesingle: + ........ + ..@@@... + ..@@@... + ..@@@... + ..@@@... + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + +u+0028: +parenleft: + ........ + ........ + ...@@@.. + ..@@@... + .@@@.... + .@@@.... + .@@@.... + .@@@.... + .@@@.... + .@@@.... + ..@@@... + ...@@@.. + ........ + ........ + ........ + ........ + +u+0029: +parenright: + ........ + ........ + .@@@.... + ..@@@... + ...@@@.. + ...@@@.. + ...@@@.. + ...@@@.. + ...@@@.. + ...@@@.. + ..@@@... + .@@@.... + ........ + ........ + ........ + ........ + +u+002a: +asterisk: + ........ + ........ + ........ + ........ + ........ + @@@.@@@. + .@@@@@.. + @@@@@@@. + .@@@@@.. + @@@.@@@. + ........ + ........ + ........ + ........ + ........ + ........ + +u+002b: +plus: + ........ + ........ + ........ + ........ + ........ + ..@@@... + ..@@@... + @@@@@@@. + @@@@@@@. + ..@@@... + ..@@@... + ........ + ........ + ........ + ........ + ........ + +u+002c: +comma: + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ..@@@... + ..@@@... + .@@@.... + .@@..... + ........ + ........ + +u+002d: +hyphen: + ........ + ........ + ........ + ........ + ........ + ........ + ........ + @@@@@@@. + @@@@@@@. + ........ + ........ + ........ + ........ + ........ + ........ + ........ + +u+002e: +period: + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ..@@@... + ..@@@... + ........ + ........ + ........ + ........ + +u+002f: +slash: + ........ + ........ + ....@@@. + ....@@@. + ...@@@.. + ...@@@.. + ..@@@... + ..@@@... + .@@@.... + .@@@.... + @@@..... + @@@..... + ........ + ........ + ........ + ........ + +u+0030: +zero: + ........ + ........ + .@@@@@.. + @@@..@@. + @@@..@@. + @@@.@@@. + @@@@@@@. + @@@@.@@. + @@@..@@. + @@@..@@. + @@@..@@. + .@@@@@.. + ........ + ........ + ........ + ........ + +u+0031: +one: + ........ + ........ + ..@@@... + .@@@@... + @@@@@... + ..@@@... + ..@@@... + ..@@@... + ..@@@... + ..@@@... + ..@@@... + @@@@@@@. + ........ + ........ + ........ + ........ + +u+0032: +two: + ........ + ........ + .@@@@@.. + @@..@@@. + @@..@@@. + ....@@@. + ...@@@.. + ..@@@... + .@@@.... + @@@..... + @@@..... + @@@@@@@. + ........ + ........ + ........ + ........ + +u+0033: +three: + ........ + ........ + .@@@@@.. + @@..@@@. + @@..@@@. + ....@@@. + ..@@@@.. + ....@@@. + ....@@@. + @@..@@@. + @@..@@@. + .@@@@@.. + ........ + ........ + ........ + ........ + +u+0034: +four: + ........ + ........ + .....@@. + ....@@@. + ...@@@@. + ..@@@@@. + .@@.@@@. + @@..@@@. + @@@@@@@. + ....@@@. + ....@@@. + ....@@@. + ........ + ........ + ........ + ........ + +u+0035: +five: + ........ + ........ + @@@@@@@. + @@@..... + @@@..... + @@@..... + @@@@@@.. + ....@@@. + ....@@@. + ....@@@. + @@..@@@. + .@@@@@.. + ........ + ........ + ........ + ........ + +u+0036: +six: + ........ + ........ + ..@@@@@. + .@@@.... + @@@..... + @@@..... + @@@@@@.. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + .@@@@@.. + ........ + ........ + ........ + ........ + +u+0037: +seven: + ........ + ........ + @@@@@@@. + ....@@@. + ....@@@. + ...@@@.. + ...@@@.. + ..@@@... + ..@@@... + .@@@.... + .@@@.... + .@@@.... + ........ + ........ + ........ + ........ + +u+0038: +eight: + ........ + ........ + .@@@@@.. + @@@.@@@. + @@@.@@@. + @@@.@@@. + .@@@@@.. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + .@@@@@.. + ........ + ........ + ........ + ........ + +u+0039: +nine: + ........ + ........ + .@@@@@.. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + .@@@@@@. + ....@@@. + ....@@@. + ...@@@.. + .@@@@... + ........ + ........ + ........ + ........ + +u+003a: +colon: + ........ + ........ + ........ + ........ + ........ + ..@@@... + ..@@@... + ........ + ........ + ........ + ..@@@... + ..@@@... + ........ + ........ + ........ + ........ + +u+003b: +semicolon: + ........ + ........ + ........ + ........ + ........ + ..@@@... + ..@@@... + ........ + ........ + ........ + ..@@@... + ..@@@... + .@@@.... + .@@..... + ........ + ........ + +u+003c: +less: + ........ + ........ + ........ + ....@@@. + ...@@@.. + ..@@@... + .@@@.... + @@@..... + .@@@.... + ..@@@... + ...@@@.. + ....@@@. + ........ + ........ + ........ + ........ + +u+003d: +equal: + ........ + ........ + ........ + ........ + ........ + @@@@@@@. + @@@@@@@. + ........ + @@@@@@@. + @@@@@@@. + ........ + ........ + ........ + ........ + ........ + ........ + +u+003e: +greater: + ........ + ........ + ........ + @@@..... + .@@@.... + ..@@@... + ...@@@.. + ....@@@. + ...@@@.. + ..@@@... + .@@@.... + @@@..... + ........ + ........ + ........ + ........ + +u+003f: +question: + ........ + ........ + .@@@@@.. + @@@.@@@. + @@@.@@@. + @@@.@@@. + ...@@@.. + ..@@@... + ..@@@... + ........ + ..@@@... + ..@@@... + ........ + ........ + ........ + ........ + +u+0040: +at: + ........ + ........ + .@@@@@.. + @@@..@@. + @@@.@@@. + @@@@.@@. + @@@@.@@. + @@@@.@@. + @@@@.@@. + @@@.@@@. + @@@..... + .@@@@@.. + ........ + ........ + ........ + ........ + +u+0041: +"A": + ........ + ........ + .@@@@@.. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@@@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + ........ + ........ + ........ + ........ + +u+0042: +"B": + ........ + ........ + @@@@@@.. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@@@@.. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@@@@.. + ........ + ........ + ........ + ........ + +u+0043: +"C": + ........ + ........ + .@@@@@.. + @@@..@@. + @@@..@@. + @@@..... + @@@..... + @@@..... + @@@..... + @@@..@@. + @@@..@@. + .@@@@@.. + ........ + ........ + ........ + ........ + +u+0044: +"D": + ........ + ........ + @@@@@... + @@@.@@.. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@.. + @@@@@... + ........ + ........ + ........ + ........ + +u+0045: +"E": + ........ + ........ + @@@@@@@. + @@@..... + @@@..... + @@@..... + @@@@@@.. + @@@..... + @@@..... + @@@..... + @@@..... + @@@@@@@. + ........ + ........ + ........ + ........ + +u+0046: +"F": + ........ + ........ + @@@@@@@. + @@@..... + @@@..... + @@@..... + @@@@@@.. + @@@..... + @@@..... + @@@..... + @@@..... + @@@..... + ........ + ........ + ........ + ........ + +u+0047: +"G": + ........ + ........ + .@@@@@.. + @@@..@@. + @@@..@@. + @@@..... + @@@..... + @@@.@@@. + @@@.@@@. + @@@..@@. + @@@..@@. + .@@@@@.. + ........ + ........ + ........ + ........ + +u+0048: +"H": + ........ + ........ + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@@@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + ........ + ........ + ........ + ........ + +u+0049: +"I": + ........ + ........ + .@@@@@.. + ..@@@... + ..@@@... + ..@@@... + ..@@@... + ..@@@... + ..@@@... + ..@@@... + ..@@@... + .@@@@@.. + ........ + ........ + ........ + ........ + +u+004a: +"J": + ........ + ........ + ..@@@@@. + ...@@@.. + ...@@@.. + ...@@@.. + ...@@@.. + ...@@@.. + ...@@@.. + @@.@@@.. + @@.@@@.. + .@@@@... + ........ + ........ + ........ + ........ + +u+004b: +"K": + ........ + ........ + @@@..@@. + @@@..@@. + @@@.@@.. + @@@@@... + @@@@.... + @@@@.... + @@@@@... + @@@.@@.. + @@@..@@. + @@@..@@. + ........ + ........ + ........ + ........ + +u+004c: +"L": + ........ + ........ + @@@..... + @@@..... + @@@..... + @@@..... + @@@..... + @@@..... + @@@..... + @@@..... + @@@..... + @@@@@@@. + ........ + ........ + ........ + ........ + +u+004d: +"M": + .......... + .......... + @.......@. + @@.....@@. + @@@...@@@. + @@@@.@@@@. + @@@@@@@@@. + @@@.@.@@@. + @@@...@@@. + @@@...@@@. + @@@...@@@. + @@@...@@@. + .......... + .......... + .......... + .......... + +u+004e: +"N": + ......... + ......... + @@@..@@@. + @@@..@@@. + @@@..@@@. + @@@@.@@@. + @@@@.@@@. + @@@@@@@@. + @@@.@@@@. + @@@.@@@@. + @@@..@@@. + @@@..@@@. + ......... + ......... + ......... + ......... + +u+004f: +"O": + ........ + ........ + .@@@@@.. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + .@@@@@.. + ........ + ........ + ........ + ........ + +u+0050: +"P": + ........ + ........ + @@@@@@.. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@@@@.. + @@@..... + @@@..... + @@@..... + @@@..... + ........ + ........ + ........ + ........ + +u+0051: +"Q": + ........ + ........ + .@@@@@.. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@@@@@. + .@@@@@.. + ....@@@. + ........ + ........ + ........ + +u+0052: +"R": + ........ + ........ + @@@@@@.. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@@@@.. + @@@@@... + @@@@@@.. + @@@.@@@. + @@@..@@@ + ........ + ........ + ........ + ........ + +u+0053: +"S": + ........ + ........ + .@@@@@.. + @@@..@@. + @@@..... + @@@..... + .@@@@@.. + ....@@@. + ....@@@. + @@..@@@. + @@..@@@. + .@@@@@.. + ........ + ........ + ........ + ........ + +u+0054: +"T": + ........ + ........ + @@@@@@@. + ..@@@... + ..@@@... + ..@@@... + ..@@@... + ..@@@... + ..@@@... + ..@@@... + ..@@@... + ..@@@... + ........ + ........ + ........ + ........ + +u+0055: +"U": + ........ + ........ + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + .@@@@@.. + ........ + ........ + ........ + ........ + +u+0056: +"V": + ........ + ........ + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + .@@.@@.. + .@@.@@.. + .@@.@@.. + ..@@@... + ..@@@... + ........ + ........ + ........ + ........ + +u+0057: +"W": + .......... + .......... + @@@...@@@. + @@@...@@@. + @@@...@@@. + @@@...@@@. + @@@...@@@. + @@@.@.@@@. + @@@@@@@@@. + @@@@.@@@@. + @@@...@@@. + @@.....@@. + .......... + .......... + .......... + .......... + +u+0058: +"X": + ........ + ........ + @@@.@@@. + @@@.@@@. + .@@@@@.. + .@@@@@.. + ..@@@... + ..@@@... + .@@@@@.. + .@@@@@.. + @@@.@@@. + @@@.@@@. + ........ + ........ + ........ + ........ + +u+0059: +"Y": + ........ + ........ + @@@...@@ + @@@...@@ + .@@@.@@. + .@@@.@@. + ..@@@@.. + ...@@@.. + ...@@@.. + ...@@@.. + ...@@@.. + ...@@@.. + ........ + ........ + ........ + ........ + +u+005a: +"Z": + ........ + ........ + @@@@@@@. + ....@@@. + ....@@@. + ...@@@.. + ..@@@... + .@@@.... + @@@..... + @@@..... + @@@..... + @@@@@@@. + ........ + ........ + ........ + ........ + +u+005b: +bracketleft: + ........ + ........ + .@@@@@.. + .@@@.... + .@@@.... + .@@@.... + .@@@.... + .@@@.... + .@@@.... + .@@@.... + .@@@.... + .@@@@@.. + ........ + ........ + ........ + ........ + +u+005c: +backslash: + ........ + ........ + @@@..... + @@@..... + .@@@.... + .@@@.... + ..@@@... + ..@@@... + ...@@@.. + ...@@@.. + ....@@@. + ....@@@. + ........ + ........ + ........ + ........ + +u+005d: +bracketright: + ........ + ........ + .@@@@@.. + ...@@@.. + ...@@@.. + ...@@@.. + ...@@@.. + ...@@@.. + ...@@@.. + ...@@@.. + ...@@@.. + .@@@@@.. + ........ + ........ + ........ + ........ + +u+005e: +asciicircum: + ...@@... + ..@@@@.. + .@@@@@@. + @@@..@@@ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + +u+005f: +underscore: + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + @@@@@@@. + @@@@@@@. + ........ + +u+0060: +grave: + ........ + .@@@.... + ..@@@... + ...@@@.. + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + +u+0061: +"a": + ........ + ........ + ........ + ........ + ........ + .@@@@@.. + ....@@@. + .@@@@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + .@@@@@@. + ........ + ........ + ........ + ........ + +u+0062: +"b": + ........ + ........ + @@@..... + @@@..... + @@@..... + @@@@@@.. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@@@@.. + ........ + ........ + ........ + ........ + +u+0063: +"c": + ........ + ........ + ........ + ........ + ........ + .@@@@@.. + @@@..@@. + @@@..... + @@@..... + @@@..... + @@@..@@. + .@@@@@.. + ........ + ........ + ........ + ........ + +u+0064: +"d": + ........ + ........ + ....@@@. + ....@@@. + ....@@@. + .@@@@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + .@@@@@@. + ........ + ........ + ........ + ........ + +u+0065: +"e": + ........ + ........ + ........ + ........ + ........ + .@@@@@.. + @@@..@@. + @@@..@@. + @@@@@@@. + @@@..... + @@@..... + .@@@@@.. + ........ + ........ + ........ + ........ + +u+0066: +"f": + ........ + ........ + ...@@@@. + ..@@@... + ..@@@... + @@@@@@@. + ..@@@... + ..@@@... + ..@@@... + ..@@@... + ..@@@... + ..@@@... + ........ + ........ + ........ + ........ + +u+0067: +"g": + ........ + ........ + ........ + ........ + ........ + .@@@@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + .@@@@@@. + ....@@@. + ....@@@. + .@@@@@.. + ........ + +u+0068: +"h": + ........ + ........ + @@@..... + @@@..... + @@@..... + @@@@@@.. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + ........ + ........ + ........ + ........ + +u+0069: +"i": + ........ + ........ + ..@@@... + ..@@@... + ........ + .@@@@... + ..@@@... + ..@@@... + ..@@@... + ..@@@... + ..@@@... + .@@@@@.. + ........ + ........ + ........ + ........ + +u+006a: +"j": + ........ + ........ + ...@@@.. + ...@@@.. + ........ + ..@@@@.. + ...@@@.. + ...@@@.. + ...@@@.. + ...@@@.. + ...@@@.. + ...@@@.. + @@.@@@.. + @@.@@@.. + .@@@@... + ........ + +u+006b: +"k": + ........ + ........ + @@@..... + @@@..... + @@@..... + @@@..@@. + @@@.@@.. + @@@@@... + @@@@@... + @@@@@@.. + @@@.@@@. + @@@..@@@ + ........ + ........ + ........ + ........ + +u+006c: +"l": + ........ + ........ + .@@@@... + ..@@@... + ..@@@... + ..@@@... + ..@@@... + ..@@@... + ..@@@... + ..@@@... + ..@@@... + .@@@@@.. + ........ + ........ + ........ + ........ + +u+006d: +"m": + ............ + ............ + ............ + ............ + ............ + @@@@@@@@@@.. + @@@.@@@.@@@. + @@@.@@@.@@@. + @@@.@@@.@@@. + @@@.@@@.@@@. + @@@.@@@.@@@. + @@@.@@@.@@@. + ............ + ............ + ............ + ............ + +u+006e: +"n": + ........ + ........ + ........ + ........ + ........ + @@@@@@.. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + ........ + ........ + ........ + ........ + +u+006f: +"o": + ........ + ........ + ........ + ........ + ........ + .@@@@@.. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + .@@@@@.. + ........ + ........ + ........ + ........ + +u+0070: +"p": + ........ + ........ + ........ + ........ + ........ + @@@@@@.. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@@@@.. + @@@..... + @@@..... + @@@..... + ........ + +u+0071: +"q": + ........ + ........ + ........ + ........ + ........ + .@@@@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + .@@@@@@. + ....@@@. + ....@@@. + ....@@@. + ........ + +u+0072: +"r": + ........ + ........ + ........ + ........ + ........ + @@@.@@@. + @@@@@... + @@@@.... + @@@..... + @@@..... + @@@..... + @@@..... + ........ + ........ + ........ + ........ + +u+0073: +"s": + ........ + ........ + ........ + ........ + ........ + .@@@@@@. + @@@..... + @@@..... + .@@@@@.. + ....@@@. + ....@@@. + @@@@@@.. + ........ + ........ + ........ + ........ + +u+0074: +"t": + ........ + ........ + ..@@@... + ..@@@... + ..@@@... + @@@@@@@. + ..@@@... + ..@@@... + ..@@@... + ..@@@... + ..@@@... + ...@@@@. + ........ + ........ + ........ + ........ + +u+0075: +"u": + ........ + ........ + ........ + ........ + ........ + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + .@@@@@@. + ........ + ........ + ........ + ........ + +u+0076: +"v": + ........ + ........ + ........ + ........ + ........ + @@@.@@@. + @@@.@@@. + @@@.@@@. + .@@.@@.. + .@@.@@.. + ..@@@... + ..@@@... + ........ + ........ + ........ + ........ + +u+0077: +"w": + ........... + ........... + ........... + ........... + ........... + @@@....@@@. + @@@....@@@. + @@@.@@.@@@. + @@@.@@.@@@. + @@@.@@.@@@. + @@@.@@.@@@. + .@@@@@@@@.. + ........... + ........... + ........... + ........... + +u+0078: +"x": + ........ + ........ + ........ + ........ + ........ + @@...@@. + @@@.@@@. + .@@@@@.. + ..@@@... + .@@@@@.. + @@@.@@@. + @@...@@. + ........ + ........ + ........ + ........ + +u+0079: +"y": + ........ + ........ + ........ + ........ + ........ + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + @@@.@@@. + .@@@@@@. + ....@@@. + ....@@@. + .@@@@@.. + ........ + +u+007a: +"z": + ........ + ........ + ........ + ........ + ........ + @@@@@@@. + ....@@@. + ...@@@.. + ..@@@... + .@@@.... + @@@..... + @@@@@@@. + ........ + ........ + ........ + ........ + +u+007b: +braceleft: + ........ + ........ + ...@@@@. + ..@@@... + ..@@@... + ..@@@... + .@@@.... + ..@@@... + ..@@@... + ..@@@... + ..@@@... + ...@@@@. + ........ + ........ + ........ + ........ + +u+007c: +bar: + ........ + ..@@@... + ..@@@... + ..@@@... + ..@@@... + ..@@@... + ..@@@... + ..@@@... + ..@@@... + ..@@@... + ..@@@... + ..@@@... + ..@@@... + ........ + ........ + ........ + +u+007d: +braceright: + ........ + ........ + .@@@@... + ...@@@.. + ...@@@.. + ...@@@.. + ....@@@. + ...@@@.. + ...@@@.. + ...@@@.. + ...@@@.. + .@@@@... + ........ + ........ + ........ + ........ + +u+007e: +asciitilde: + ........ + .@@@..@@ + @@@@@@@@ + @@@.@@@. + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + +u+00a0: +nbspace: + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ diff --git a/containers/test/fonts/terminus-ascii-bold-16px.yaff b/containers/test/fonts/terminus-16px.yaff similarity index 98% rename from containers/test/fonts/terminus-ascii-bold-16px.yaff rename to containers/test/fonts/terminus-16px.yaff index 9316bb7f7..6f59223a9 100644 --- a/containers/test/fonts/terminus-ascii-bold-16px.yaff +++ b/containers/test/fonts/terminus-16px.yaff @@ -1846,3 +1846,21 @@ asciitilde: ........ ........ +u+00a0: +nbspace: + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ + ........ diff --git a/containers/test/fonts/terminus-ascii-bold-18px.yaff b/containers/test/fonts/terminus-18px.yaff similarity index 98% rename from containers/test/fonts/terminus-ascii-bold-18px.yaff rename to containers/test/fonts/terminus-18px.yaff index d13e3fb60..0887d9ffb 100644 --- a/containers/test/fonts/terminus-ascii-bold-18px.yaff +++ b/containers/test/fonts/terminus-18px.yaff @@ -2038,3 +2038,23 @@ asciitilde: .......... .......... +u+00a0: +nbspace: + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... diff --git a/containers/test/fonts/terminus-ascii-bold-20px.yaff b/containers/test/fonts/terminus-20px.yaff similarity index 93% rename from containers/test/fonts/terminus-ascii-bold-20px.yaff rename to containers/test/fonts/terminus-20px.yaff index d6c2401f5..0783d98ab 100644 --- a/containers/test/fonts/terminus-ascii-bold-20px.yaff +++ b/containers/test/fonts/terminus-20px.yaff @@ -1,2232 +1,2254 @@ -name: Terminus Bold 10x20 -spacing: character-cell -cell-size: 10 20 -family: Terminus -foundry: xos4 -copyright: Copyright (C) 2020 Dimitar Toshkov Zhekov -notice: Licensed under the SIL Open Font License, Version 1.1 -point-size: 20 -weight: bold -slant: roman -setwidth: normal -dpi: 72 72 -average-width: 10 -ascent: 16 -descent: 4 -shift-up: -4 -encoding: iso10646-1 -default-char: u+fffd -min-word-space: 10 -converter: monobit v0.32 -source-name: ter-u20b.bdf -source-format: BDF v2.1 -history: load --format=bdf - -u+0000: -char0: - .......... - .......... - .......... - .@@@..@@@. - .@@....@@. - .@@....@@. - .......... - .......... - .@@....@@. - .@@....@@. - .@@....@@. - .......... - .......... - .@@....@@. - .@@....@@. - .@@@..@@@. - .......... - .......... - .......... - .......... - -u+0020: -space: - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - -u+0021: -exclam: - .......... - .......... - .......... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - .......... - .......... - ....@@.... - ....@@.... - .......... - .......... - .......... - .......... - -u+0022: -quotedbl: - .......... - ..@@..@@.. - ..@@..@@.. - ..@@..@@.. - ..@@..@@.. - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - -u+0023: -numbersign: - .......... - .......... - .......... - ..@@..@@.. - ..@@..@@.. - ..@@..@@.. - ..@@..@@.. - .@@@@@@@@. - ..@@..@@.. - ..@@..@@.. - ..@@..@@.. - .@@@@@@@@. - ..@@..@@.. - ..@@..@@.. - ..@@..@@.. - ..@@..@@.. - .......... - .......... - .......... - .......... - -u+0024: -dollar: - .......... - .......... - ....@@.... - ....@@.... - ..@@@@@@.. - .@@.@@.@@. - .@@.@@.... - .@@.@@.... - .@@.@@.... - ..@@@@@@.. - ....@@.@@. - ....@@.@@. - ....@@.@@. - .@@.@@.@@. - ..@@@@@@.. - ....@@.... - ....@@.... - .......... - .......... - .......... - -u+0025: -percent: - .......... - .......... - .......... - .......... - .@@@..@@.. - .@.@..@@.. - .@@@.@@... - .....@@... - ....@@.... - ....@@.... - ...@@..... - ...@@..... - ..@@...... - ..@@.@@@.. - .@@..@.@.. - .@@..@@@.. - .......... - .......... - .......... - .......... - -u+0026: -ampersand: - .......... - .......... - .......... - ..@@@@.... - .@@..@@... - .@@..@@... - .@@..@@... - ..@@@@.... - ...@@..... - ..@@@..@@. - .@@.@@.@@. - @@...@@@.. - @@....@@.. - @@....@@.. - .@@..@@@@. - ..@@@@.@@. - .......... - .......... - .......... - .......... - -u+0027: -quotesingle: - .......... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - -u+0028: -parenleft: - .......... - .......... - .......... - .....@@... - ....@@.... - ....@@.... - ...@@..... - ...@@..... - ...@@..... - ...@@..... - ...@@..... - ...@@..... - ...@@..... - ....@@.... - ....@@.... - .....@@... - .......... - .......... - .......... - .......... - -u+0029: -parenright: - .......... - .......... - .......... - ...@@..... - ....@@.... - ....@@.... - .....@@... - .....@@... - .....@@... - .....@@... - .....@@... - .....@@... - .....@@... - ....@@.... - ....@@.... - ...@@..... - .......... - .......... - .......... - .......... - -u+002a: -asterisk: - .......... - .......... - .......... - .......... - .......... - .......... - .@@...@@.. - ..@@.@@... - ...@@@.... - @@@@@@@@@. - ...@@@.... - ..@@.@@... - .@@...@@.. - .......... - .......... - .......... - .......... - .......... - .......... - .......... - -u+002b: -plus: - .......... - .......... - .......... - .......... - .......... - .......... - ....@@.... - ....@@.... - ....@@.... - .@@@@@@@@. - ....@@.... - ....@@.... - ....@@.... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - -u+002c: -comma: - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - ....@@.... - ....@@.... - ....@@.... - ...@@..... - .......... - .......... - .......... - -u+002d: -hyphen: - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .@@@@@@@@. - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - -u+002e: -period: - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - ....@@.... - ....@@.... - .......... - .......... - .......... - .......... - -u+002f: -slash: - .......... - .......... - .......... - .......... - ......@@.. - ......@@.. - .....@@... - .....@@... - ....@@.... - ....@@.... - ...@@..... - ...@@..... - ..@@...... - ..@@...... - .@@....... - .@@....... - .......... - .......... - .......... - .......... - -u+0030: -zero: - .......... - .......... - .......... - ..@@@@@@.. - .@@....@@. - .@@....@@. - .@@....@@. - .@@...@@@. - .@@..@@@@. - .@@.@@.@@. - .@@@@..@@. - .@@@...@@. - .@@....@@. - .@@....@@. - .@@....@@. - ..@@@@@@.. - .......... - .......... - .......... - .......... - -u+0031: -one: - .......... - .......... - .......... - ....@@.... - ...@@@.... - ..@@@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ..@@@@@@.. - .......... - .......... - .......... - .......... - -u+0032: -two: - .......... - .......... - .......... - ..@@@@@@.. - .@@....@@. - .@@....@@. - .@@....@@. - .......@@. - .......@@. - ......@@.. - .....@@... - ....@@.... - ...@@..... - ..@@...... - .@@....... - .@@@@@@@@. - .......... - .......... - .......... - .......... - -u+0033: -three: - .......... - .......... - .......... - ..@@@@@@.. - .@@....@@. - .@@....@@. - .......@@. - .......@@. - .......@@. - ...@@@@@.. - .......@@. - .......@@. - .......@@. - .@@....@@. - .@@....@@. - ..@@@@@@.. - .......... - .......... - .......... - .......... - -u+0034: -four: - .......... - .......... - .......... - .......@@. - ......@@@. - .....@@@@. - ....@@.@@. - ...@@..@@. - ..@@...@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@@@@@@@. - .......@@. - .......@@. - .......@@. - .......... - .......... - .......... - .......... - -u+0035: -five: - .......... - .......... - .......... - .@@@@@@@@. - .@@....... - .@@....... - .@@....... - .@@....... - .@@@@@@@.. - .......@@. - .......@@. - .......@@. - .......@@. - .@@....@@. - .@@....@@. - ..@@@@@@.. - .......... - .......... - .......... - .......... - -u+0036: -six: - .......... - .......... - .......... - ...@@@@@.. - ..@@...... - .@@....... - .@@....... - .@@....... - .@@@@@@@.. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - ..@@@@@@.. - .......... - .......... - .......... - .......... - -u+0037: -seven: - .......... - .......... - .......... - .@@@@@@@@. - .@@....@@. - .@@....@@. - .......@@. - ......@@.. - ......@@.. - .....@@... - .....@@... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - .......... - .......... - .......... - .......... - -u+0038: -eight: - .......... - .......... - .......... - ..@@@@@@.. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - ..@@@@@@.. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - ..@@@@@@.. - .......... - .......... - .......... - .......... - -u+0039: -nine: - .......... - .......... - .......... - ..@@@@@@.. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - ..@@@@@@@. - .......@@. - .......@@. - .......@@. - ......@@.. - ..@@@@@... - .......... - .......... - .......... - .......... - -u+003a: -colon: - .......... - .......... - .......... - .......... - .......... - .......... - .......... - ....@@.... - ....@@.... - .......... - .......... - .......... - .......... - ....@@.... - ....@@.... - .......... - .......... - .......... - .......... - .......... - -u+003b: -semicolon: - .......... - .......... - .......... - .......... - .......... - .......... - .......... - ....@@.... - ....@@.... - .......... - .......... - .......... - .......... - ....@@.... - ....@@.... - ....@@.... - ...@@..... - .......... - .......... - .......... - -u+003c: -less: - .......... - .......... - .......... - .......@@. - ......@@.. - .....@@... - ....@@.... - ...@@..... - ..@@...... - .@@....... - ..@@...... - ...@@..... - ....@@.... - .....@@... - ......@@.. - .......@@. - .......... - .......... - .......... - .......... - -u+003d: -equal: - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .@@@@@@@@. - .......... - .......... - .......... - .@@@@@@@@. - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - -u+003e: -greater: - .......... - .......... - .......... - .@@....... - ..@@...... - ...@@..... - ....@@.... - .....@@... - ......@@.. - .......@@. - ......@@.. - .....@@... - ....@@.... - ...@@..... - ..@@...... - .@@....... - .......... - .......... - .......... - .......... - -u+003f: -question: - .......... - .......... - .......... - ...@@@@... - ..@@..@@.. - .@@....@@. - .@@....@@. - .......@@. - ......@@.. - .....@@... - ....@@.... - ....@@.... - .......... - .......... - ....@@.... - ....@@.... - .......... - .......... - .......... - .......... - -u+0040: -at: - .......... - .......... - .......... - .@@@@@@@.. - @@.....@@. - @@.....@@. - @@..@@@@@. - @@.@@..@@. - @@.@@..@@. - @@.@@..@@. - @@.@@..@@. - @@.@@..@@. - @@..@@@@@. - @@........ - @@........ - .@@@@@@@@. - .......... - .......... - .......... - .......... - -u+0041: -"A": - .......... - .......... - .......... - ..@@@@@@.. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@@@@@@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .......... - .......... - .......... - .......... - -u+0042: -"B": - .......... - .......... - .......... - .@@@@@@@.. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@@@@@@.. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@@@@@@.. - .......... - .......... - .......... - .......... - -u+0043: -"C": - .......... - .......... - .......... - ..@@@@@@.. - .@@....@@. - .@@....@@. - .@@....... - .@@....... - .@@....... - .@@....... - .@@....... - .@@....... - .@@....... - .@@....@@. - .@@....@@. - ..@@@@@@.. - .......... - .......... - .......... - .......... - -u+0044: -"D": - .......... - .......... - .......... - .@@@@@@... - .@@...@@.. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@...@@.. - .@@@@@@... - .......... - .......... - .......... - .......... - -u+0045: -"E": - .......... - .......... - .......... - .@@@@@@@@. - .@@....... - .@@....... - .@@....... - .@@....... - .@@....... - .@@@@@@... - .@@....... - .@@....... - .@@....... - .@@....... - .@@....... - .@@@@@@@@. - .......... - .......... - .......... - .......... - -u+0046: -"F": - .......... - .......... - .......... - .@@@@@@@@. - .@@....... - .@@....... - .@@....... - .@@....... - .@@....... - .@@@@@@... - .@@....... - .@@....... - .@@....... - .@@....... - .@@....... - .@@....... - .......... - .......... - .......... - .......... - -u+0047: -"G": - .......... - .......... - .......... - ..@@@@@@.. - .@@....@@. - .@@....@@. - .@@....... - .@@....... - .@@....... - .@@..@@@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - ..@@@@@@.. - .......... - .......... - .......... - .......... - -u+0048: -"H": - .......... - .......... - .......... - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@@@@@@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .......... - .......... - .......... - .......... - -u+0049: -"I": - .......... - .......... - .......... - ...@@@@... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ...@@@@... - .......... - .......... - .......... - .......... - -u+004a: -"J": - .......... - .......... - .......... - .....@@@@. - ......@@.. - ......@@.. - ......@@.. - ......@@.. - ......@@.. - ......@@.. - ......@@.. - ......@@.. - .@@...@@.. - .@@...@@.. - .@@...@@.. - ..@@@@@... - .......... - .......... - .......... - .......... - -u+004b: -"K": - .......... - .......... - .......... - .@@....@@. - .@@....@@. - .@@...@@.. - .@@..@@... - .@@.@@.... - .@@@@..... - .@@@...... - .@@@@..... - .@@.@@.... - .@@..@@... - .@@...@@.. - .@@....@@. - .@@....@@. - .......... - .......... - .......... - .......... - -u+004c: -"L": - .......... - .......... - .......... - .@@....... - .@@....... - .@@....... - .@@....... - .@@....... - .@@....... - .@@....... - .@@....... - .@@....... - .@@....... - .@@....... - .@@....... - .@@@@@@@@. - .......... - .......... - .......... - .......... - -u+004d: -"M": - .......... - .......... - .......... - @.......@. - @@.....@@. - @@@...@@@. - @@@@.@@@@. - @@.@@@.@@. - @@..@..@@. - @@.....@@. - @@.....@@. - @@.....@@. - @@.....@@. - @@.....@@. - @@.....@@. - @@.....@@. - .......... - .......... - .......... - .......... - -u+004e: -"N": - .......... - .......... - .......... - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@@...@@. - .@@@@..@@. - .@@.@@.@@. - .@@..@@@@. - .@@...@@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .......... - .......... - .......... - .......... - -u+004f: -"O": - .......... - .......... - .......... - ..@@@@@@.. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - ..@@@@@@.. - .......... - .......... - .......... - .......... - -u+0050: -"P": - .......... - .......... - .......... - .@@@@@@@.. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@@@@@@.. - .@@....... - .@@....... - .@@....... - .@@....... - .@@....... - .@@....... - .......... - .......... - .......... - .......... - -u+0051: -"Q": - .......... - .......... - .......... - ..@@@@@@.. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@..@@@@. - ..@@@@@@.. - ......@@.. - .......@@. - .......... - .......... - -u+0052: -"R": - .......... - .......... - .......... - .@@@@@@@.. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@@@@@@.. - .@@@@..... - .@@.@@.... - .@@..@@... - .@@...@@.. - .@@....@@. - .@@....@@. - .......... - .......... - .......... - .......... - -u+0053: -"S": - .......... - .......... - .......... - ..@@@@@@.. - .@@....@@. - .@@....@@. - .@@....... - .@@....... - .@@....... - ..@@@@@@.. - .......@@. - .......@@. - .......@@. - .@@....@@. - .@@....@@. - ..@@@@@@.. - .......... - .......... - .......... - .......... - -u+0054: -"T": - .......... - .......... - .......... - .@@@@@@@@. - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - .......... - .......... - .......... - .......... - -u+0055: -"U": - .......... - .......... - .......... - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - ..@@@@@@.. - .......... - .......... - .......... - .......... - -u+0056: -"V": - .......... - .......... - .......... - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - ..@@..@@.. - ..@@..@@.. - ..@@..@@.. - ..@@..@@.. - ...@@@@... - ...@@@@... - ....@@.... - ....@@.... - .......... - .......... - .......... - .......... - -u+0057: -"W": - .......... - .......... - .......... - @@.....@@. - @@.....@@. - @@.....@@. - @@.....@@. - @@.....@@. - @@.....@@. - @@.....@@. - @@..@..@@. - @@.@@@.@@. - @@@@.@@@@. - @@@...@@@. - @@.....@@. - @.......@. - .......... - .......... - .......... - .......... - -u+0058: -"X": - .......... - .......... - .......... - .@@....@@. - .@@....@@. - .@@....@@. - ..@@..@@.. - ..@@..@@.. - ...@@@@... - ....@@.... - ...@@@@... - ..@@..@@.. - ..@@..@@.. - .@@....@@. - .@@....@@. - .@@....@@. - .......... - .......... - .......... - .......... - -u+0059: -"Y": - .......... - .......... - .......... - .@@....@@. - .@@....@@. - .@@....@@. - ..@@..@@.. - ..@@..@@.. - ..@@..@@.. - ...@@@@... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - .......... - .......... - .......... - .......... - -u+005a: -"Z": - .......... - .......... - .......... - .@@@@@@@@. - .......@@. - .......@@. - .......@@. - ......@@.. - .....@@... - ....@@.... - ...@@..... - ..@@...... - .@@....... - .@@....... - .@@....... - .@@@@@@@@. - .......... - .......... - .......... - .......... - -u+005b: -bracketleft: - .......... - .......... - .......... - ...@@@@... - ...@@..... - ...@@..... - ...@@..... - ...@@..... - ...@@..... - ...@@..... - ...@@..... - ...@@..... - ...@@..... - ...@@..... - ...@@..... - ...@@@@... - .......... - .......... - .......... - .......... - -u+005c: -backslash: - .......... - .......... - .......... - .......... - .@@....... - .@@....... - ..@@...... - ..@@...... - ...@@..... - ...@@..... - ....@@.... - ....@@.... - .....@@... - .....@@... - ......@@.. - ......@@.. - .......... - .......... - .......... - .......... - -u+005d: -bracketright: - .......... - .......... - .......... - ...@@@@... - .....@@... - .....@@... - .....@@... - .....@@... - .....@@... - .....@@... - .....@@... - .....@@... - .....@@... - .....@@... - .....@@... - ...@@@@... - .......... - .......... - .......... - .......... - -u+005e: -asciicircum: - .......... - ....@@.... - ...@@@@... - ..@@..@@.. - .@@....@@. - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - -u+005f: -underscore: - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .@@@@@@@@. - .......... - .......... - -u+0060: -grave: - ..@@...... - ...@@..... - ....@@.... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - -u+0061: -"a": - .......... - .......... - .......... - .......... - .......... - .......... - .......... - ..@@@@@@.. - .......@@. - .......@@. - ..@@@@@@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - ..@@@@@@@. - .......... - .......... - .......... - .......... - -u+0062: -"b": - .......... - .......... - .......... - .@@....... - .@@....... - .@@....... - .@@....... - .@@@@@@@.. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@@@@@@.. - .......... - .......... - .......... - .......... - -u+0063: -"c": - .......... - .......... - .......... - .......... - .......... - .......... - .......... - ..@@@@@@.. - .@@....@@. - .@@....... - .@@....... - .@@....... - .@@....... - .@@....... - .@@....@@. - ..@@@@@@.. - .......... - .......... - .......... - .......... - -u+0064: -"d": - .......... - .......... - .......... - .......@@. - .......@@. - .......@@. - .......@@. - ..@@@@@@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - ..@@@@@@@. - .......... - .......... - .......... - .......... - -u+0065: -"e": - .......... - .......... - .......... - .......... - .......... - .......... - .......... - ..@@@@@@.. - .@@....@@. - .@@....@@. - .@@....@@. - .@@@@@@@@. - .@@....... - .@@....... - .@@....@@. - ..@@@@@@.. - .......... - .......... - .......... - .......... - -u+0066: -"f": - .......... - .......... - .......... - .....@@@@. - ....@@.... - ....@@.... - ....@@.... - ..@@@@@@.. - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - .......... - .......... - .......... - .......... - -u+0067: -"g": - .......... - .......... - .......... - .......... - .......... - .......... - .......... - ..@@@@@@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - ..@@@@@@@. - .......@@. - .......@@. - ..@@@@@@.. - .......... - -u+0068: -"h": - .......... - .......... - .......... - .@@....... - .@@....... - .@@....... - .@@....... - .@@@@@@@.. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .......... - .......... - .......... - .......... - -u+0069: -"i": - .......... - .......... - .......... - ....@@.... - ....@@.... - .......... - .......... - ...@@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ...@@@@... - .......... - .......... - .......... - .......... - -u+006a: -"j": - .......... - .......... - .......... - ......@@.. - ......@@.. - .......... - .......... - .....@@@.. - ......@@.. - ......@@.. - ......@@.. - ......@@.. - ......@@.. - ......@@.. - ......@@.. - ......@@.. - ..@@..@@.. - ..@@..@@.. - ...@@@@... - .......... - -u+006b: -"k": - .......... - .......... - .......... - .@@....... - .@@....... - .@@....... - .@@....... - .@@....@@. - .@@...@@.. - .@@..@@... - .@@.@@.... - .@@@@..... - .@@.@@.... - .@@..@@... - .@@...@@.. - .@@....@@. - .......... - .......... - .......... - .......... - -u+006c: -"l": - .......... - .......... - .......... - ...@@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ...@@@@... - .......... - .......... - .......... - .......... - -u+006d: -"m": - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .@@@@@@@.. - .@@.@@.@@. - .@@.@@.@@. - .@@.@@.@@. - .@@.@@.@@. - .@@.@@.@@. - .@@.@@.@@. - .@@.@@.@@. - .@@.@@.@@. - .......... - .......... - .......... - .......... - -u+006e: -"n": - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .@@@@@@@.. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .......... - .......... - .......... - .......... - -u+006f: -"o": - .......... - .......... - .......... - .......... - .......... - .......... - .......... - ..@@@@@@.. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - ..@@@@@@.. - .......... - .......... - .......... - .......... - -u+0070: -"p": - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .@@@@@@@.. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@@@@@@.. - .@@....... - .@@....... - .@@....... - .......... - -u+0071: -"q": - .......... - .......... - .......... - .......... - .......... - .......... - .......... - ..@@@@@@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - ..@@@@@@@. - .......@@. - .......@@. - .......@@. - .......... - -u+0072: -"r": - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .@@.@@@@@. - .@@@@..... - .@@@...... - .@@....... - .@@....... - .@@....... - .@@....... - .@@....... - .@@....... - .......... - .......... - .......... - .......... - -u+0073: -"s": - .......... - .......... - .......... - .......... - .......... - .......... - .......... - ..@@@@@@.. - .@@....@@. - .@@....... - .@@....... - ..@@@@@@.. - .......@@. - .......@@. - .@@....@@. - ..@@@@@@.. - .......... - .......... - .......... - .......... - -u+0074: -"t": - .......... - .......... - .......... - ...@@..... - ...@@..... - ...@@..... - ...@@..... - .@@@@@@... - ...@@..... - ...@@..... - ...@@..... - ...@@..... - ...@@..... - ...@@..... - ...@@..... - ....@@@@.. - .......... - .......... - .......... - .......... - -u+0075: -"u": - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - ..@@@@@@@. - .......... - .......... - .......... - .......... - -u+0076: -"v": - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .@@....@@. - .@@....@@. - .@@....@@. - ..@@..@@.. - ..@@..@@.. - ..@@..@@.. - ...@@@@... - ....@@.... - ....@@.... - .......... - .......... - .......... - .......... - -u+0077: -"w": - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .@@....@@. - .@@....@@. - .@@....@@. - .@@.@@.@@. - .@@.@@.@@. - .@@.@@.@@. - .@@.@@.@@. - .@@.@@.@@. - ..@@@@@@.. - .......... - .......... - .......... - .......... - -u+0078: -"x": - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .@@....@@. - .@@....@@. - ..@@..@@.. - ...@@@@... - ....@@.... - ...@@@@... - ..@@..@@.. - .@@....@@. - .@@....@@. - .......... - .......... - .......... - .......... - -u+0079: -"y": - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - .@@....@@. - ..@@@@@@@. - .......@@. - .......@@. - ..@@@@@@.. - .......... - -u+007a: -"z": - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .@@@@@@@@. - .......@@. - ......@@.. - .....@@... - ....@@.... - ...@@..... - ..@@...... - .@@....... - .@@@@@@@@. - .......... - .......... - .......... - .......... - -u+007b: -braceleft: - .......... - .......... - .......... - .....@@@.. - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ..@@@..... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - .....@@@.. - .......... - .......... - .......... - .......... - -u+007c: -bar: - .......... - .......... - .......... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - .......... - .......... - .......... - .......... - -u+007d: -braceright: - .......... - .......... - .......... - ..@@@..... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - .....@@@.. - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ....@@.... - ..@@@..... - .......... - .......... - .......... - .......... - -u+007e: -asciitilde: - .......... - ..@@@..@@. - .@@.@@.@@. - .@@.@@.@@. - .@@..@@@.. - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - .......... - +name: Terminus Bold 10x20 +spacing: character-cell +cell-size: 10 20 +family: Terminus +foundry: xos4 +copyright: Copyright (C) 2020 Dimitar Toshkov Zhekov +notice: Licensed under the SIL Open Font License, Version 1.1 +point-size: 20 +weight: bold +slant: roman +setwidth: normal +dpi: 72 72 +average-width: 10 +ascent: 16 +descent: 4 +shift-up: -4 +encoding: iso10646-1 +default-char: u+fffd +min-word-space: 10 +converter: monobit v0.32 +source-name: ter-u20b.bdf +source-format: BDF v2.1 +history: load --format=bdf + +u+0000: +char0: + .......... + .......... + .......... + .@@@..@@@. + .@@....@@. + .@@....@@. + .......... + .......... + .@@....@@. + .@@....@@. + .@@....@@. + .......... + .......... + .@@....@@. + .@@....@@. + .@@@..@@@. + .......... + .......... + .......... + .......... + +u+0020: +space: + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + +u+0021: +exclam: + .......... + .......... + .......... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + .......... + .......... + ....@@.... + ....@@.... + .......... + .......... + .......... + .......... + +u+0022: +quotedbl: + .......... + ..@@..@@.. + ..@@..@@.. + ..@@..@@.. + ..@@..@@.. + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + +u+0023: +numbersign: + .......... + .......... + .......... + ..@@..@@.. + ..@@..@@.. + ..@@..@@.. + ..@@..@@.. + .@@@@@@@@. + ..@@..@@.. + ..@@..@@.. + ..@@..@@.. + .@@@@@@@@. + ..@@..@@.. + ..@@..@@.. + ..@@..@@.. + ..@@..@@.. + .......... + .......... + .......... + .......... + +u+0024: +dollar: + .......... + .......... + ....@@.... + ....@@.... + ..@@@@@@.. + .@@.@@.@@. + .@@.@@.... + .@@.@@.... + .@@.@@.... + ..@@@@@@.. + ....@@.@@. + ....@@.@@. + ....@@.@@. + .@@.@@.@@. + ..@@@@@@.. + ....@@.... + ....@@.... + .......... + .......... + .......... + +u+0025: +percent: + .......... + .......... + .......... + .......... + .@@@..@@.. + .@.@..@@.. + .@@@.@@... + .....@@... + ....@@.... + ....@@.... + ...@@..... + ...@@..... + ..@@...... + ..@@.@@@.. + .@@..@.@.. + .@@..@@@.. + .......... + .......... + .......... + .......... + +u+0026: +ampersand: + .......... + .......... + .......... + ..@@@@.... + .@@..@@... + .@@..@@... + .@@..@@... + ..@@@@.... + ...@@..... + ..@@@..@@. + .@@.@@.@@. + @@...@@@.. + @@....@@.. + @@....@@.. + .@@..@@@@. + ..@@@@.@@. + .......... + .......... + .......... + .......... + +u+0027: +quotesingle: + .......... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + +u+0028: +parenleft: + .......... + .......... + .......... + .....@@... + ....@@.... + ....@@.... + ...@@..... + ...@@..... + ...@@..... + ...@@..... + ...@@..... + ...@@..... + ...@@..... + ....@@.... + ....@@.... + .....@@... + .......... + .......... + .......... + .......... + +u+0029: +parenright: + .......... + .......... + .......... + ...@@..... + ....@@.... + ....@@.... + .....@@... + .....@@... + .....@@... + .....@@... + .....@@... + .....@@... + .....@@... + ....@@.... + ....@@.... + ...@@..... + .......... + .......... + .......... + .......... + +u+002a: +asterisk: + .......... + .......... + .......... + .......... + .......... + .......... + .@@...@@.. + ..@@.@@... + ...@@@.... + @@@@@@@@@. + ...@@@.... + ..@@.@@... + .@@...@@.. + .......... + .......... + .......... + .......... + .......... + .......... + .......... + +u+002b: +plus: + .......... + .......... + .......... + .......... + .......... + .......... + ....@@.... + ....@@.... + ....@@.... + .@@@@@@@@. + ....@@.... + ....@@.... + ....@@.... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + +u+002c: +comma: + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + ....@@.... + ....@@.... + ....@@.... + ...@@..... + .......... + .......... + .......... + +u+002d: +hyphen: + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .@@@@@@@@. + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + +u+002e: +period: + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + ....@@.... + ....@@.... + .......... + .......... + .......... + .......... + +u+002f: +slash: + .......... + .......... + .......... + .......... + ......@@.. + ......@@.. + .....@@... + .....@@... + ....@@.... + ....@@.... + ...@@..... + ...@@..... + ..@@...... + ..@@...... + .@@....... + .@@....... + .......... + .......... + .......... + .......... + +u+0030: +zero: + .......... + .......... + .......... + ..@@@@@@.. + .@@....@@. + .@@....@@. + .@@....@@. + .@@...@@@. + .@@..@@@@. + .@@.@@.@@. + .@@@@..@@. + .@@@...@@. + .@@....@@. + .@@....@@. + .@@....@@. + ..@@@@@@.. + .......... + .......... + .......... + .......... + +u+0031: +one: + .......... + .......... + .......... + ....@@.... + ...@@@.... + ..@@@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ..@@@@@@.. + .......... + .......... + .......... + .......... + +u+0032: +two: + .......... + .......... + .......... + ..@@@@@@.. + .@@....@@. + .@@....@@. + .@@....@@. + .......@@. + .......@@. + ......@@.. + .....@@... + ....@@.... + ...@@..... + ..@@...... + .@@....... + .@@@@@@@@. + .......... + .......... + .......... + .......... + +u+0033: +three: + .......... + .......... + .......... + ..@@@@@@.. + .@@....@@. + .@@....@@. + .......@@. + .......@@. + .......@@. + ...@@@@@.. + .......@@. + .......@@. + .......@@. + .@@....@@. + .@@....@@. + ..@@@@@@.. + .......... + .......... + .......... + .......... + +u+0034: +four: + .......... + .......... + .......... + .......@@. + ......@@@. + .....@@@@. + ....@@.@@. + ...@@..@@. + ..@@...@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@@@@@@@. + .......@@. + .......@@. + .......@@. + .......... + .......... + .......... + .......... + +u+0035: +five: + .......... + .......... + .......... + .@@@@@@@@. + .@@....... + .@@....... + .@@....... + .@@....... + .@@@@@@@.. + .......@@. + .......@@. + .......@@. + .......@@. + .@@....@@. + .@@....@@. + ..@@@@@@.. + .......... + .......... + .......... + .......... + +u+0036: +six: + .......... + .......... + .......... + ...@@@@@.. + ..@@...... + .@@....... + .@@....... + .@@....... + .@@@@@@@.. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + ..@@@@@@.. + .......... + .......... + .......... + .......... + +u+0037: +seven: + .......... + .......... + .......... + .@@@@@@@@. + .@@....@@. + .@@....@@. + .......@@. + ......@@.. + ......@@.. + .....@@... + .....@@... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + .......... + .......... + .......... + .......... + +u+0038: +eight: + .......... + .......... + .......... + ..@@@@@@.. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + ..@@@@@@.. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + ..@@@@@@.. + .......... + .......... + .......... + .......... + +u+0039: +nine: + .......... + .......... + .......... + ..@@@@@@.. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + ..@@@@@@@. + .......@@. + .......@@. + .......@@. + ......@@.. + ..@@@@@... + .......... + .......... + .......... + .......... + +u+003a: +colon: + .......... + .......... + .......... + .......... + .......... + .......... + .......... + ....@@.... + ....@@.... + .......... + .......... + .......... + .......... + ....@@.... + ....@@.... + .......... + .......... + .......... + .......... + .......... + +u+003b: +semicolon: + .......... + .......... + .......... + .......... + .......... + .......... + .......... + ....@@.... + ....@@.... + .......... + .......... + .......... + .......... + ....@@.... + ....@@.... + ....@@.... + ...@@..... + .......... + .......... + .......... + +u+003c: +less: + .......... + .......... + .......... + .......@@. + ......@@.. + .....@@... + ....@@.... + ...@@..... + ..@@...... + .@@....... + ..@@...... + ...@@..... + ....@@.... + .....@@... + ......@@.. + .......@@. + .......... + .......... + .......... + .......... + +u+003d: +equal: + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .@@@@@@@@. + .......... + .......... + .......... + .@@@@@@@@. + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + +u+003e: +greater: + .......... + .......... + .......... + .@@....... + ..@@...... + ...@@..... + ....@@.... + .....@@... + ......@@.. + .......@@. + ......@@.. + .....@@... + ....@@.... + ...@@..... + ..@@...... + .@@....... + .......... + .......... + .......... + .......... + +u+003f: +question: + .......... + .......... + .......... + ...@@@@... + ..@@..@@.. + .@@....@@. + .@@....@@. + .......@@. + ......@@.. + .....@@... + ....@@.... + ....@@.... + .......... + .......... + ....@@.... + ....@@.... + .......... + .......... + .......... + .......... + +u+0040: +at: + .......... + .......... + .......... + .@@@@@@@.. + @@.....@@. + @@.....@@. + @@..@@@@@. + @@.@@..@@. + @@.@@..@@. + @@.@@..@@. + @@.@@..@@. + @@.@@..@@. + @@..@@@@@. + @@........ + @@........ + .@@@@@@@@. + .......... + .......... + .......... + .......... + +u+0041: +"A": + .......... + .......... + .......... + ..@@@@@@.. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@@@@@@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .......... + .......... + .......... + .......... + +u+0042: +"B": + .......... + .......... + .......... + .@@@@@@@.. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@@@@@@.. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@@@@@@.. + .......... + .......... + .......... + .......... + +u+0043: +"C": + .......... + .......... + .......... + ..@@@@@@.. + .@@....@@. + .@@....@@. + .@@....... + .@@....... + .@@....... + .@@....... + .@@....... + .@@....... + .@@....... + .@@....@@. + .@@....@@. + ..@@@@@@.. + .......... + .......... + .......... + .......... + +u+0044: +"D": + .......... + .......... + .......... + .@@@@@@... + .@@...@@.. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@...@@.. + .@@@@@@... + .......... + .......... + .......... + .......... + +u+0045: +"E": + .......... + .......... + .......... + .@@@@@@@@. + .@@....... + .@@....... + .@@....... + .@@....... + .@@....... + .@@@@@@... + .@@....... + .@@....... + .@@....... + .@@....... + .@@....... + .@@@@@@@@. + .......... + .......... + .......... + .......... + +u+0046: +"F": + .......... + .......... + .......... + .@@@@@@@@. + .@@....... + .@@....... + .@@....... + .@@....... + .@@....... + .@@@@@@... + .@@....... + .@@....... + .@@....... + .@@....... + .@@....... + .@@....... + .......... + .......... + .......... + .......... + +u+0047: +"G": + .......... + .......... + .......... + ..@@@@@@.. + .@@....@@. + .@@....@@. + .@@....... + .@@....... + .@@....... + .@@..@@@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + ..@@@@@@.. + .......... + .......... + .......... + .......... + +u+0048: +"H": + .......... + .......... + .......... + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@@@@@@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .......... + .......... + .......... + .......... + +u+0049: +"I": + .......... + .......... + .......... + ...@@@@... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ...@@@@... + .......... + .......... + .......... + .......... + +u+004a: +"J": + .......... + .......... + .......... + .....@@@@. + ......@@.. + ......@@.. + ......@@.. + ......@@.. + ......@@.. + ......@@.. + ......@@.. + ......@@.. + .@@...@@.. + .@@...@@.. + .@@...@@.. + ..@@@@@... + .......... + .......... + .......... + .......... + +u+004b: +"K": + .......... + .......... + .......... + .@@....@@. + .@@....@@. + .@@...@@.. + .@@..@@... + .@@.@@.... + .@@@@..... + .@@@...... + .@@@@..... + .@@.@@.... + .@@..@@... + .@@...@@.. + .@@....@@. + .@@....@@. + .......... + .......... + .......... + .......... + +u+004c: +"L": + .......... + .......... + .......... + .@@....... + .@@....... + .@@....... + .@@....... + .@@....... + .@@....... + .@@....... + .@@....... + .@@....... + .@@....... + .@@....... + .@@....... + .@@@@@@@@. + .......... + .......... + .......... + .......... + +u+004d: +"M": + .......... + .......... + .......... + @.......@. + @@.....@@. + @@@...@@@. + @@@@.@@@@. + @@.@@@.@@. + @@..@..@@. + @@.....@@. + @@.....@@. + @@.....@@. + @@.....@@. + @@.....@@. + @@.....@@. + @@.....@@. + .......... + .......... + .......... + .......... + +u+004e: +"N": + .......... + .......... + .......... + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@@...@@. + .@@@@..@@. + .@@.@@.@@. + .@@..@@@@. + .@@...@@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .......... + .......... + .......... + .......... + +u+004f: +"O": + .......... + .......... + .......... + ..@@@@@@.. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + ..@@@@@@.. + .......... + .......... + .......... + .......... + +u+0050: +"P": + .......... + .......... + .......... + .@@@@@@@.. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@@@@@@.. + .@@....... + .@@....... + .@@....... + .@@....... + .@@....... + .@@....... + .......... + .......... + .......... + .......... + +u+0051: +"Q": + .......... + .......... + .......... + ..@@@@@@.. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@..@@@@. + ..@@@@@@.. + ......@@.. + .......@@. + .......... + .......... + +u+0052: +"R": + .......... + .......... + .......... + .@@@@@@@.. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@@@@@@.. + .@@@@..... + .@@.@@.... + .@@..@@... + .@@...@@.. + .@@....@@. + .@@....@@. + .......... + .......... + .......... + .......... + +u+0053: +"S": + .......... + .......... + .......... + ..@@@@@@.. + .@@....@@. + .@@....@@. + .@@....... + .@@....... + .@@....... + ..@@@@@@.. + .......@@. + .......@@. + .......@@. + .@@....@@. + .@@....@@. + ..@@@@@@.. + .......... + .......... + .......... + .......... + +u+0054: +"T": + .......... + .......... + .......... + .@@@@@@@@. + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + .......... + .......... + .......... + .......... + +u+0055: +"U": + .......... + .......... + .......... + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + ..@@@@@@.. + .......... + .......... + .......... + .......... + +u+0056: +"V": + .......... + .......... + .......... + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + ..@@..@@.. + ..@@..@@.. + ..@@..@@.. + ..@@..@@.. + ...@@@@... + ...@@@@... + ....@@.... + ....@@.... + .......... + .......... + .......... + .......... + +u+0057: +"W": + .......... + .......... + .......... + @@.....@@. + @@.....@@. + @@.....@@. + @@.....@@. + @@.....@@. + @@.....@@. + @@.....@@. + @@..@..@@. + @@.@@@.@@. + @@@@.@@@@. + @@@...@@@. + @@.....@@. + @.......@. + .......... + .......... + .......... + .......... + +u+0058: +"X": + .......... + .......... + .......... + .@@....@@. + .@@....@@. + .@@....@@. + ..@@..@@.. + ..@@..@@.. + ...@@@@... + ....@@.... + ...@@@@... + ..@@..@@.. + ..@@..@@.. + .@@....@@. + .@@....@@. + .@@....@@. + .......... + .......... + .......... + .......... + +u+0059: +"Y": + .......... + .......... + .......... + .@@....@@. + .@@....@@. + .@@....@@. + ..@@..@@.. + ..@@..@@.. + ..@@..@@.. + ...@@@@... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + .......... + .......... + .......... + .......... + +u+005a: +"Z": + .......... + .......... + .......... + .@@@@@@@@. + .......@@. + .......@@. + .......@@. + ......@@.. + .....@@... + ....@@.... + ...@@..... + ..@@...... + .@@....... + .@@....... + .@@....... + .@@@@@@@@. + .......... + .......... + .......... + .......... + +u+005b: +bracketleft: + .......... + .......... + .......... + ...@@@@... + ...@@..... + ...@@..... + ...@@..... + ...@@..... + ...@@..... + ...@@..... + ...@@..... + ...@@..... + ...@@..... + ...@@..... + ...@@..... + ...@@@@... + .......... + .......... + .......... + .......... + +u+005c: +backslash: + .......... + .......... + .......... + .......... + .@@....... + .@@....... + ..@@...... + ..@@...... + ...@@..... + ...@@..... + ....@@.... + ....@@.... + .....@@... + .....@@... + ......@@.. + ......@@.. + .......... + .......... + .......... + .......... + +u+005d: +bracketright: + .......... + .......... + .......... + ...@@@@... + .....@@... + .....@@... + .....@@... + .....@@... + .....@@... + .....@@... + .....@@... + .....@@... + .....@@... + .....@@... + .....@@... + ...@@@@... + .......... + .......... + .......... + .......... + +u+005e: +asciicircum: + .......... + ....@@.... + ...@@@@... + ..@@..@@.. + .@@....@@. + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + +u+005f: +underscore: + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .@@@@@@@@. + .......... + .......... + +u+0060: +grave: + ..@@...... + ...@@..... + ....@@.... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + +u+0061: +"a": + .......... + .......... + .......... + .......... + .......... + .......... + .......... + ..@@@@@@.. + .......@@. + .......@@. + ..@@@@@@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + ..@@@@@@@. + .......... + .......... + .......... + .......... + +u+0062: +"b": + .......... + .......... + .......... + .@@....... + .@@....... + .@@....... + .@@....... + .@@@@@@@.. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@@@@@@.. + .......... + .......... + .......... + .......... + +u+0063: +"c": + .......... + .......... + .......... + .......... + .......... + .......... + .......... + ..@@@@@@.. + .@@....@@. + .@@....... + .@@....... + .@@....... + .@@....... + .@@....... + .@@....@@. + ..@@@@@@.. + .......... + .......... + .......... + .......... + +u+0064: +"d": + .......... + .......... + .......... + .......@@. + .......@@. + .......@@. + .......@@. + ..@@@@@@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + ..@@@@@@@. + .......... + .......... + .......... + .......... + +u+0065: +"e": + .......... + .......... + .......... + .......... + .......... + .......... + .......... + ..@@@@@@.. + .@@....@@. + .@@....@@. + .@@....@@. + .@@@@@@@@. + .@@....... + .@@....... + .@@....@@. + ..@@@@@@.. + .......... + .......... + .......... + .......... + +u+0066: +"f": + .......... + .......... + .......... + .....@@@@. + ....@@.... + ....@@.... + ....@@.... + ..@@@@@@.. + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + .......... + .......... + .......... + .......... + +u+0067: +"g": + .......... + .......... + .......... + .......... + .......... + .......... + .......... + ..@@@@@@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + ..@@@@@@@. + .......@@. + .......@@. + ..@@@@@@.. + .......... + +u+0068: +"h": + .......... + .......... + .......... + .@@....... + .@@....... + .@@....... + .@@....... + .@@@@@@@.. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .......... + .......... + .......... + .......... + +u+0069: +"i": + .......... + .......... + .......... + ....@@.... + ....@@.... + .......... + .......... + ...@@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ...@@@@... + .......... + .......... + .......... + .......... + +u+006a: +"j": + .......... + .......... + .......... + ......@@.. + ......@@.. + .......... + .......... + .....@@@.. + ......@@.. + ......@@.. + ......@@.. + ......@@.. + ......@@.. + ......@@.. + ......@@.. + ......@@.. + ..@@..@@.. + ..@@..@@.. + ...@@@@... + .......... + +u+006b: +"k": + .......... + .......... + .......... + .@@....... + .@@....... + .@@....... + .@@....... + .@@....@@. + .@@...@@.. + .@@..@@... + .@@.@@.... + .@@@@..... + .@@.@@.... + .@@..@@... + .@@...@@.. + .@@....@@. + .......... + .......... + .......... + .......... + +u+006c: +"l": + .......... + .......... + .......... + ...@@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ...@@@@... + .......... + .......... + .......... + .......... + +u+006d: +"m": + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .@@@@@@@.. + .@@.@@.@@. + .@@.@@.@@. + .@@.@@.@@. + .@@.@@.@@. + .@@.@@.@@. + .@@.@@.@@. + .@@.@@.@@. + .@@.@@.@@. + .......... + .......... + .......... + .......... + +u+006e: +"n": + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .@@@@@@@.. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .......... + .......... + .......... + .......... + +u+006f: +"o": + .......... + .......... + .......... + .......... + .......... + .......... + .......... + ..@@@@@@.. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + ..@@@@@@.. + .......... + .......... + .......... + .......... + +u+0070: +"p": + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .@@@@@@@.. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@@@@@@.. + .@@....... + .@@....... + .@@....... + .......... + +u+0071: +"q": + .......... + .......... + .......... + .......... + .......... + .......... + .......... + ..@@@@@@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + ..@@@@@@@. + .......@@. + .......@@. + .......@@. + .......... + +u+0072: +"r": + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .@@.@@@@@. + .@@@@..... + .@@@...... + .@@....... + .@@....... + .@@....... + .@@....... + .@@....... + .@@....... + .......... + .......... + .......... + .......... + +u+0073: +"s": + .......... + .......... + .......... + .......... + .......... + .......... + .......... + ..@@@@@@.. + .@@....@@. + .@@....... + .@@....... + ..@@@@@@.. + .......@@. + .......@@. + .@@....@@. + ..@@@@@@.. + .......... + .......... + .......... + .......... + +u+0074: +"t": + .......... + .......... + .......... + ...@@..... + ...@@..... + ...@@..... + ...@@..... + .@@@@@@... + ...@@..... + ...@@..... + ...@@..... + ...@@..... + ...@@..... + ...@@..... + ...@@..... + ....@@@@.. + .......... + .......... + .......... + .......... + +u+0075: +"u": + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + ..@@@@@@@. + .......... + .......... + .......... + .......... + +u+0076: +"v": + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .@@....@@. + .@@....@@. + .@@....@@. + ..@@..@@.. + ..@@..@@.. + ..@@..@@.. + ...@@@@... + ....@@.... + ....@@.... + .......... + .......... + .......... + .......... + +u+0077: +"w": + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .@@....@@. + .@@....@@. + .@@....@@. + .@@.@@.@@. + .@@.@@.@@. + .@@.@@.@@. + .@@.@@.@@. + .@@.@@.@@. + ..@@@@@@.. + .......... + .......... + .......... + .......... + +u+0078: +"x": + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .@@....@@. + .@@....@@. + ..@@..@@.. + ...@@@@... + ....@@.... + ...@@@@... + ..@@..@@.. + .@@....@@. + .@@....@@. + .......... + .......... + .......... + .......... + +u+0079: +"y": + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + .@@....@@. + ..@@@@@@@. + .......@@. + .......@@. + ..@@@@@@.. + .......... + +u+007a: +"z": + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .@@@@@@@@. + .......@@. + ......@@.. + .....@@... + ....@@.... + ...@@..... + ..@@...... + .@@....... + .@@@@@@@@. + .......... + .......... + .......... + .......... + +u+007b: +braceleft: + .......... + .......... + .......... + .....@@@.. + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ..@@@..... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + .....@@@.. + .......... + .......... + .......... + .......... + +u+007c: +bar: + .......... + .......... + .......... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + .......... + .......... + .......... + .......... + +u+007d: +braceright: + .......... + .......... + .......... + ..@@@..... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + .....@@@.. + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ....@@.... + ..@@@..... + .......... + .......... + .......... + .......... + +u+007e: +asciitilde: + .......... + ..@@@..@@. + .@@.@@.@@. + .@@.@@.@@. + .@@..@@@.. + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + +u+00a0: +nbspace: + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... + .......... diff --git a/containers/test/fonts/terminus-ascii-bold-22px.yaff b/containers/test/fonts/terminus-22px.yaff similarity index 93% rename from containers/test/fonts/terminus-ascii-bold-22px.yaff rename to containers/test/fonts/terminus-22px.yaff index 25f6aa599..82d33ccff 100644 --- a/containers/test/fonts/terminus-ascii-bold-22px.yaff +++ b/containers/test/fonts/terminus-22px.yaff @@ -1,2424 +1,2448 @@ -name: Terminus Bold 11x22 -spacing: character-cell -cell-size: 11 22 -family: Terminus -foundry: xos4 -copyright: Copyright (C) 2020 Dimitar Toshkov Zhekov -notice: Licensed under the SIL Open Font License, Version 1.1 -point-size: 22 -weight: bold -slant: roman -setwidth: normal -dpi: 72 72 -average-width: 11 -ascent: 17 -descent: 5 -shift-up: -5 -encoding: iso10646-1 -default-char: u+fffd -min-word-space: 11 -converter: monobit v0.32 -source-name: ter-u22b.bdf -source-format: BDF v2.1 -history: load --format=bdf - -u+0000: -char0: - ........... - ........... - ........... - .@@@@.@@@@. - .@@.....@@. - .@@.....@@. - ........... - ........... - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - ........... - ........... - .@@.....@@. - .@@.....@@. - .@@@@.@@@@. - ........... - ........... - ........... - ........... - ........... - -u+0020: -space: - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - -u+0021: -exclam: - ........... - ........... - ........... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ........... - ........... - ....@@..... - ....@@..... - ....@@..... - ........... - ........... - ........... - ........... - ........... - -u+0022: -quotedbl: - ........... - ..@@..@@... - ..@@..@@... - ..@@..@@... - ..@@..@@... - ..@@..@@... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - -u+0023: -numbersign: - ........... - ........... - ........... - ..@@..@@... - ..@@..@@... - ..@@..@@... - ..@@..@@... - @@@@@@@@@@. - ..@@..@@... - ..@@..@@... - ..@@..@@... - ..@@..@@... - @@@@@@@@@@. - ..@@..@@... - ..@@..@@... - ..@@..@@... - ..@@..@@... - ........... - ........... - ........... - ........... - ........... - -u+0024: -dollar: - ........... - ........... - ....@@..... - ....@@..... - ..@@@@@@... - .@@.@@.@@.. - @@..@@..@@. - @@..@@..... - @@..@@..... - .@@.@@..... - ..@@@@@@... - ....@@.@@.. - ....@@..@@. - ....@@..@@. - @@..@@..@@. - .@@.@@.@@.. - ..@@@@@@... - ....@@..... - ....@@..... - ........... - ........... - ........... - -u+0025: -percent: - ........... - ........... - ........... - .@@@...@@.. - @@.@@..@@.. - @@.@@.@@... - .@@@..@@... - .....@@.... - .....@@.... - ....@@..... - ....@@..... - ...@@...... - ...@@...... - ..@@..@@@.. - ..@@.@@.@@. - .@@..@@.@@. - .@@...@@@.. - ........... - ........... - ........... - ........... - ........... - -u+0026: -ampersand: - ........... - ........... - ........... - ..@@@@@.... - .@@...@@... - .@@...@@... - .@@...@@... - ..@@.@@.... - ...@@@..... - ..@@@@..... - .@@..@@.@@. - @@....@@@@. - @@.....@@.. - @@.....@@.. - @@.....@@.. - .@@...@@@@. - ..@@@@@.@@. - ........... - ........... - ........... - ........... - ........... - -u+0027: -quotesingle: - ........... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - -u+0028: -parenleft: - ........... - ........... - ........... - ......@@... - .....@@.... - ....@@..... - ....@@..... - ...@@...... - ...@@...... - ...@@...... - ...@@...... - ...@@...... - ...@@...... - ....@@..... - ....@@..... - .....@@.... - ......@@... - ........... - ........... - ........... - ........... - ........... - -u+0029: -parenright: - ........... - ........... - ........... - ...@@...... - ....@@..... - .....@@.... - .....@@.... - ......@@... - ......@@... - ......@@... - ......@@... - ......@@... - ......@@... - .....@@.... - .....@@.... - ....@@..... - ...@@...... - ........... - ........... - ........... - ........... - ........... - -u+002a: -asterisk: - ........... - ........... - ........... - ........... - ........... - ........... - .@@....@@.. - ..@@..@@... - ...@@@@.... - ....@@..... - @@@@@@@@@@. - ....@@..... - ...@@@@.... - ..@@..@@... - .@@....@@.. - ........... - ........... - ........... - ........... - ........... - ........... - ........... - -u+002b: -plus: - ........... - ........... - ........... - ........... - ........... - ........... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - @@@@@@@@@@. - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - -u+002c: -comma: - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ...@@...... - ........... - ........... - ........... - ........... - -u+002d: -hyphen: - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - .@@@@@@@@@. - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - -u+002e: -period: - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ....@@..... - ....@@..... - ....@@..... - ........... - ........... - ........... - ........... - ........... - -u+002f: -slash: - ........... - ........... - ........... - .......@@.. - .......@@.. - ......@@... - ......@@... - .....@@.... - .....@@.... - ....@@..... - ....@@..... - ...@@...... - ...@@...... - ..@@....... - ..@@....... - .@@........ - .@@........ - ........... - ........... - ........... - ........... - ........... - -u+0030: -zero: - ........... - ........... - ........... - ...@@@@@... - ..@@...@@.. - .@@.....@@. - .@@.....@@. - .@@....@@@. - .@@...@@@@. - .@@..@@.@@. - .@@.@@..@@. - .@@@@...@@. - .@@@....@@. - .@@.....@@. - .@@.....@@. - ..@@...@@.. - ...@@@@@... - ........... - ........... - ........... - ........... - ........... - -u+0031: -one: - ........... - ........... - ........... - .....@@.... - ....@@@.... - ...@@@@.... - ..@@.@@.... - .....@@.... - .....@@.... - .....@@.... - .....@@.... - .....@@.... - .....@@.... - .....@@.... - .....@@.... - .....@@.... - ..@@@@@@@@. - ........... - ........... - ........... - ........... - ........... - -u+0032: -two: - ........... - ........... - ........... - ...@@@@@... - ..@@...@@.. - .@@.....@@. - .@@.....@@. - .@@.....@@. - ........@@. - .......@@.. - ......@@... - .....@@.... - ....@@..... - ...@@...... - ..@@....... - .@@........ - .@@@@@@@@@. - ........... - ........... - ........... - ........... - ........... - -u+0033: -three: - ........... - ........... - ........... - ...@@@@@... - ..@@...@@.. - .@@.....@@. - ........@@. - ........@@. - .......@@.. - ....@@@@... - .......@@.. - ........@@. - ........@@. - ........@@. - .@@.....@@. - ..@@...@@.. - ...@@@@@... - ........... - ........... - ........... - ........... - ........... - -u+0034: -four: - ........... - ........... - ........... - ........@@. - .......@@@. - ......@@@@. - .....@@.@@. - ....@@..@@. - ...@@...@@. - ..@@....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@@@@@@@@. - ........@@. - ........@@. - ........@@. - ........... - ........... - ........... - ........... - ........... - -u+0035: -five: - ........... - ........... - ........... - .@@@@@@@@@. - .@@........ - .@@........ - .@@........ - .@@........ - .@@@@@@@... - .......@@.. - ........@@. - ........@@. - ........@@. - .@@.....@@. - .@@.....@@. - ..@@...@@.. - ...@@@@@... - ........... - ........... - ........... - ........... - ........... - -u+0036: -six: - ........... - ........... - ........... - ...@@@@@@.. - ..@@....... - .@@........ - .@@........ - .@@........ - .@@@@@@@... - .@@....@@.. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - ..@@...@@.. - ...@@@@@... - ........... - ........... - ........... - ........... - ........... - -u+0037: -seven: - ........... - ........... - ........... - .@@@@@@@@@. - .@@.....@@. - .@@.....@@. - ........@@. - .......@@.. - .......@@.. - ......@@... - ......@@... - .....@@.... - .....@@.... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ........... - ........... - ........... - ........... - ........... - -u+0038: -eight: - ........... - ........... - ........... - ...@@@@@... - ..@@...@@.. - .@@.....@@. - .@@.....@@. - .@@.....@@. - ..@@...@@.. - ...@@@@@... - ..@@...@@.. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - ..@@...@@.. - ...@@@@@... - ........... - ........... - ........... - ........... - ........... - -u+0039: -nine: - ........... - ........... - ........... - ...@@@@@... - ..@@...@@.. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - ..@@....@@. - ...@@@@@@@. - ........@@. - ........@@. - ........@@. - .......@@.. - ..@@@@@@... - ........... - ........... - ........... - ........... - ........... - -u+003a: -colon: - ........... - ........... - ........... - ........... - ........... - ........... - ....@@..... - ....@@..... - ....@@..... - ........... - ........... - ........... - ........... - ....@@..... - ....@@..... - ....@@..... - ........... - ........... - ........... - ........... - ........... - ........... - -u+003b: -semicolon: - ........... - ........... - ........... - ........... - ........... - ........... - ....@@..... - ....@@..... - ....@@..... - ........... - ........... - ........... - ........... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ...@@...... - ........... - ........... - ........... - ........... - -u+003c: -less: - ........... - ........... - ........... - .......@@.. - ......@@... - .....@@.... - ....@@..... - ...@@...... - ..@@....... - .@@........ - .@@........ - ..@@....... - ...@@...... - ....@@..... - .....@@.... - ......@@... - .......@@.. - ........... - ........... - ........... - ........... - ........... - -u+003d: -equal: - ........... - ........... - ........... - ........... - ........... - ........... - ........... - .@@@@@@@@@. - ........... - ........... - ........... - ........... - .@@@@@@@@@. - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - -u+003e: -greater: - ........... - ........... - ........... - .@@........ - ..@@....... - ...@@...... - ....@@..... - .....@@.... - ......@@... - .......@@.. - .......@@.. - ......@@... - .....@@.... - ....@@..... - ...@@...... - ..@@....... - .@@........ - ........... - ........... - ........... - ........... - ........... - -u+003f: -question: - ........... - ........... - ........... - ...@@@@@... - ..@@...@@.. - .@@.....@@. - .@@.....@@. - ........@@. - .......@@.. - ......@@... - .....@@.... - .....@@.... - ........... - ........... - .....@@.... - .....@@.... - .....@@.... - ........... - ........... - ........... - ........... - ........... - -u+0040: -at: - ........... - ........... - ........... - ..@@@@@@... - .@@....@@.. - @@......@@. - @@...@@@@@. - @@..@@..@@. - @@.@@...@@. - @@.@@...@@. - @@.@@...@@. - @@.@@...@@. - @@..@@..@@. - @@...@@@@@. - @@......... - .@@........ - ..@@@@@@@@. - ........... - ........... - ........... - ........... - ........... - -u+0041: -"A": - ........... - ........... - ........... - ...@@@@@... - ..@@...@@.. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@@@@@@@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - ........... - ........... - ........... - ........... - ........... - -u+0042: -"B": - ........... - ........... - ........... - .@@@@@@@... - .@@....@@.. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@....@@.. - .@@@@@@@... - .@@....@@.. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@....@@.. - .@@@@@@@... - ........... - ........... - ........... - ........... - ........... - -u+0043: -"C": - ........... - ........... - ........... - ...@@@@@... - ..@@...@@.. - .@@.....@@. - .@@.....@@. - .@@........ - .@@........ - .@@........ - .@@........ - .@@........ - .@@........ - .@@.....@@. - .@@.....@@. - ..@@...@@.. - ...@@@@@... - ........... - ........... - ........... - ........... - ........... - -u+0044: -"D": - ........... - ........... - ........... - .@@@@@@@... - .@@....@@.. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@....@@.. - .@@@@@@@... - ........... - ........... - ........... - ........... - ........... - -u+0045: -"E": - ........... - ........... - ........... - .@@@@@@@@@. - .@@........ - .@@........ - .@@........ - .@@........ - .@@........ - .@@@@@@@... - .@@........ - .@@........ - .@@........ - .@@........ - .@@........ - .@@........ - .@@@@@@@@@. - ........... - ........... - ........... - ........... - ........... - -u+0046: -"F": - ........... - ........... - ........... - .@@@@@@@@@. - .@@........ - .@@........ - .@@........ - .@@........ - .@@........ - .@@@@@@@... - .@@........ - .@@........ - .@@........ - .@@........ - .@@........ - .@@........ - .@@........ - ........... - ........... - ........... - ........... - ........... - -u+0047: -"G": - ........... - ........... - ........... - ...@@@@@... - ..@@...@@.. - .@@.....@@. - .@@.....@@. - .@@........ - .@@........ - .@@........ - .@@..@@@@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - ..@@...@@.. - ...@@@@@... - ........... - ........... - ........... - ........... - ........... - -u+0048: -"H": - ........... - ........... - ........... - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@@@@@@@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - ........... - ........... - ........... - ........... - ........... - -u+0049: -"I": - ........... - ........... - ........... - ..@@@@@@... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ..@@@@@@... - ........... - ........... - ........... - ........... - ........... - -u+004a: -"J": - ........... - ........... - ........... - .....@@@@@@ - .......@@.. - .......@@.. - .......@@.. - .......@@.. - .......@@.. - .......@@.. - .......@@.. - .......@@.. - .@@....@@.. - .@@....@@.. - .@@....@@.. - ..@@..@@... - ...@@@@.... - ........... - ........... - ........... - ........... - ........... - -u+004b: -"K": - ........... - ........... - ........... - .@@.....@@. - .@@.....@@. - .@@....@@.. - .@@...@@... - .@@..@@.... - .@@.@@..... - .@@@@...... - .@@@@...... - .@@.@@..... - .@@..@@.... - .@@...@@... - .@@....@@.. - .@@.....@@. - .@@.....@@. - ........... - ........... - ........... - ........... - ........... - -u+004c: -"L": - ........... - ........... - ........... - .@@........ - .@@........ - .@@........ - .@@........ - .@@........ - .@@........ - .@@........ - .@@........ - .@@........ - .@@........ - .@@........ - .@@........ - .@@........ - .@@@@@@@@@. - ........... - ........... - ........... - ........... - ........... - -u+004d: -"M": - ........... - ........... - ........... - @........@. - @@......@@. - @@@....@@@. - @@@@..@@@@. - @@.@@@@.@@. - @@..@@..@@. - @@......@@. - @@......@@. - @@......@@. - @@......@@. - @@......@@. - @@......@@. - @@......@@. - @@......@@. - ........... - ........... - ........... - ........... - ........... - -u+004e: -"N": - ........... - ........... - ........... - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@@....@@. - .@@@@...@@. - .@@.@@..@@. - .@@..@@.@@. - .@@...@@@@. - .@@....@@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - ........... - ........... - ........... - ........... - ........... - -u+004f: -"O": - ........... - ........... - ........... - ...@@@@@... - ..@@...@@.. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - ..@@...@@.. - ...@@@@@... - ........... - ........... - ........... - ........... - ........... - -u+0050: -"P": - ........... - ........... - ........... - .@@@@@@@... - .@@....@@.. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@....@@.. - .@@@@@@@... - .@@........ - .@@........ - .@@........ - .@@........ - .@@........ - .@@........ - ........... - ........... - ........... - ........... - ........... - -u+0051: -"Q": - ........... - ........... - ........... - ...@@@@@... - ..@@...@@.. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - ..@@.@@@@.. - ...@@@@@... - .......@@.. - ........@@. - ........... - ........... - ........... - -u+0052: -"R": - ........... - ........... - ........... - .@@@@@@@... - .@@....@@.. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@....@@.. - .@@@@@@@... - .@@@@...... - .@@.@@..... - .@@..@@.... - .@@...@@... - .@@....@@.. - .@@.....@@. - ........... - ........... - ........... - ........... - ........... - -u+0053: -"S": - ........... - ........... - ........... - ...@@@@@... - ..@@...@@.. - .@@.....@@. - .@@........ - .@@........ - ..@@....... - ...@@@@@... - .......@@.. - ........@@. - ........@@. - ........@@. - .@@.....@@. - ..@@...@@.. - ...@@@@@... - ........... - ........... - ........... - ........... - ........... - -u+0054: -"T": - ........... - ........... - ........... - @@@@@@@@@@. - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ........... - ........... - ........... - ........... - ........... - -u+0055: -"U": - ........... - ........... - ........... - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - ..@@...@@.. - ...@@@@@... - ........... - ........... - ........... - ........... - ........... - -u+0056: -"V": - ........... - ........... - ........... - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - ..@@...@@.. - ..@@...@@.. - ..@@...@@.. - ..@@...@@.. - ...@@.@@... - ...@@.@@... - ....@@@.... - ....@@@.... - ....@@@.... - ........... - ........... - ........... - ........... - ........... - -u+0057: -"W": - ........... - ........... - ........... - @@......@@. - @@......@@. - @@......@@. - @@......@@. - @@......@@. - @@......@@. - @@......@@. - @@..@@..@@. - @@..@@..@@. - @@.@@@@.@@. - @@@@..@@@@. - @@@....@@@. - @@......@@. - @........@. - ........... - ........... - ........... - ........... - ........... - -u+0058: -"X": - ........... - ........... - ........... - .@@.....@@. - .@@.....@@. - ..@@...@@.. - ..@@...@@.. - ...@@.@@... - ...@@.@@... - ....@@@.... - ....@@@.... - ...@@.@@... - ...@@.@@... - ..@@...@@.. - ..@@...@@.. - .@@.....@@. - .@@.....@@. - ........... - ........... - ........... - ........... - ........... - -u+0059: -"Y": - ........... - ........... - ........... - @@......@@. - @@......@@. - .@@....@@.. - .@@....@@.. - ..@@..@@... - ..@@..@@... - ...@@@@.... - ...@@@@.... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ........... - ........... - ........... - ........... - ........... - -u+005a: -"Z": - ........... - ........... - ........... - .@@@@@@@@@. - ........@@. - ........@@. - ........@@. - .......@@.. - ......@@... - .....@@.... - ....@@..... - ...@@...... - ..@@....... - .@@........ - .@@........ - .@@........ - .@@@@@@@@@. - ........... - ........... - ........... - ........... - ........... - -u+005b: -bracketleft: - ........... - ........... - ........... - ...@@@@@... - ...@@...... - ...@@...... - ...@@...... - ...@@...... - ...@@...... - ...@@...... - ...@@...... - ...@@...... - ...@@...... - ...@@...... - ...@@...... - ...@@...... - ...@@@@@... - ........... - ........... - ........... - ........... - ........... - -u+005c: -backslash: - ........... - ........... - ........... - .@@........ - .@@........ - ..@@....... - ..@@....... - ...@@...... - ...@@...... - ....@@..... - ....@@..... - .....@@.... - .....@@.... - ......@@... - ......@@... - .......@@.. - .......@@.. - ........... - ........... - ........... - ........... - ........... - -u+005d: -bracketright: - ........... - ........... - ........... - ...@@@@@... - ......@@... - ......@@... - ......@@... - ......@@... - ......@@... - ......@@... - ......@@... - ......@@... - ......@@... - ......@@... - ......@@... - ......@@... - ...@@@@@... - ........... - ........... - ........... - ........... - ........... - -u+005e: -asciicircum: - ........... - .....@..... - ....@@@.... - ...@@.@@... - ..@@...@@.. - .@@.....@@. - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - -u+005f: -underscore: - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - .@@@@@@@@@. - ........... - ........... - ........... - -u+0060: -grave: - ...@@...... - ....@@..... - .....@@.... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - -u+0061: -"a": - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ..@@@@@@... - .......@@.. - ........@@. - ........@@. - ..@@@@@@@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - ..@@@@@@@@. - ........... - ........... - ........... - ........... - ........... - -u+0062: -"b": - ........... - ........... - ........... - .@@........ - .@@........ - .@@........ - .@@........ - .@@@@@@@... - .@@....@@.. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@....@@.. - .@@@@@@@... - ........... - ........... - ........... - ........... - ........... - -u+0063: -"c": - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ...@@@@@... - ..@@...@@.. - .@@.....@@. - .@@........ - .@@........ - .@@........ - .@@........ - .@@.....@@. - ..@@...@@.. - ...@@@@@... - ........... - ........... - ........... - ........... - ........... - -u+0064: -"d": - ........... - ........... - ........... - ........@@. - ........@@. - ........@@. - ........@@. - ...@@@@@@@. - ..@@....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - ..@@....@@. - ...@@@@@@@. - ........... - ........... - ........... - ........... - ........... - -u+0065: -"e": - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ...@@@@@... - ..@@...@@.. - .@@.....@@. - .@@.....@@. - .@@@@@@@@@. - .@@........ - .@@........ - .@@........ - ..@@....@@. - ...@@@@@@.. - ........... - ........... - ........... - ........... - ........... - -u+0066: -"f": - ........... - ........... - ........... - .....@@@@@. - ....@@..... - ....@@..... - ....@@..... - .@@@@@@@@.. - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ........... - ........... - ........... - ........... - ........... - -u+0067: -"g": - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ...@@@@@@@. - ..@@....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - ..@@....@@. - ...@@@@@@@. - ........@@. - ........@@. - .......@@.. - ..@@@@@@... - ........... - -u+0068: -"h": - ........... - ........... - ........... - .@@........ - .@@........ - .@@........ - .@@........ - .@@@@@@@... - .@@....@@.. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - ........... - ........... - ........... - ........... - ........... - -u+0069: -"i": - ........... - ........... - ........... - .....@@.... - .....@@.... - .....@@.... - ........... - ...@@@@.... - .....@@.... - .....@@.... - .....@@.... - .....@@.... - .....@@.... - .....@@.... - .....@@.... - .....@@.... - ...@@@@@@.. - ........... - ........... - ........... - ........... - ........... - -u+006a: -"j": - ........... - ........... - ........... - .......@@.. - .......@@.. - .......@@.. - ........... - .....@@@@.. - .......@@.. - .......@@.. - .......@@.. - .......@@.. - .......@@.. - .......@@.. - .......@@.. - .......@@.. - .......@@.. - ..@@...@@.. - ..@@...@@.. - ..@@...@@.. - ...@@@@@... - ........... - -u+006b: -"k": - ........... - ........... - ........... - ..@@....... - ..@@....... - ..@@....... - ..@@....... - ..@@....@@. - ..@@...@@.. - ..@@..@@... - ..@@.@@.... - ..@@@@..... - ..@@@@..... - ..@@.@@.... - ..@@..@@... - ..@@...@@.. - ..@@....@@. - ........... - ........... - ........... - ........... - ........... - -u+006c: -"l": - ........... - ........... - ........... - ...@@@@.... - .....@@.... - .....@@.... - .....@@.... - .....@@.... - .....@@.... - .....@@.... - .....@@.... - .....@@.... - .....@@.... - .....@@.... - .....@@.... - .....@@.... - ...@@@@@@.. - ........... - ........... - ........... - ........... - ........... - -u+006d: -"m": - ........... - ........... - ........... - ........... - ........... - ........... - ........... - @@@@@@@@... - @@..@@.@@.. - @@..@@..@@. - @@..@@..@@. - @@..@@..@@. - @@..@@..@@. - @@..@@..@@. - @@..@@..@@. - @@..@@..@@. - @@..@@..@@. - ........... - ........... - ........... - ........... - ........... - -u+006e: -"n": - ........... - ........... - ........... - ........... - ........... - ........... - ........... - .@@@@@@@... - .@@....@@.. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - ........... - ........... - ........... - ........... - ........... - -u+006f: -"o": - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ...@@@@@... - ..@@...@@.. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - ..@@...@@.. - ...@@@@@... - ........... - ........... - ........... - ........... - ........... - -u+0070: -"p": - ........... - ........... - ........... - ........... - ........... - ........... - ........... - .@@@@@@@... - .@@....@@.. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@....@@.. - .@@@@@@@... - .@@........ - .@@........ - .@@........ - .@@........ - ........... - -u+0071: -"q": - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ...@@@@@@@. - ..@@....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - ..@@....@@. - ...@@@@@@@. - ........@@. - ........@@. - ........@@. - ........@@. - ........... - -u+0072: -"r": - ........... - ........... - ........... - ........... - ........... - ........... - ........... - .@@..@@@@@. - .@@.@@..... - .@@@@...... - .@@@....... - .@@........ - .@@........ - .@@........ - .@@........ - .@@........ - .@@........ - ........... - ........... - ........... - ........... - ........... - -u+0073: -"s": - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ..@@@@@@@.. - .@@.....@@. - .@@........ - .@@........ - ..@@@@@@@.. - ........@@. - ........@@. - ........@@. - .@@.....@@. - ..@@@@@@@.. - ........... - ........... - ........... - ........... - ........... - -u+0074: -"t": - ........... - ........... - ........... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - .@@@@@@@@.. - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - .....@@@@@. - ........... - ........... - ........... - ........... - ........... - -u+0075: -"u": - ........... - ........... - ........... - ........... - ........... - ........... - ........... - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - ..@@....@@. - ...@@@@@@@. - ........... - ........... - ........... - ........... - ........... - -u+0076: -"v": - ........... - ........... - ........... - ........... - ........... - ........... - ........... - .@@.....@@. - .@@.....@@. - .@@.....@@. - ..@@...@@.. - ..@@...@@.. - ..@@...@@.. - ...@@.@@... - ...@@.@@... - ....@@@.... - ....@@@.... - ........... - ........... - ........... - ........... - ........... - -u+0077: -"w": - ........... - ........... - ........... - ........... - ........... - ........... - ........... - @@......@@. - @@......@@. - @@......@@. - @@..@@..@@. - @@..@@..@@. - @@..@@..@@. - @@..@@..@@. - @@..@@..@@. - @@..@@..@@. - .@@@@@@@@.. - ........... - ........... - ........... - ........... - ........... - -u+0078: -"x": - ........... - ........... - ........... - ........... - ........... - ........... - ........... - .@@.....@@. - .@@.....@@. - ..@@...@@.. - ...@@.@@... - ....@@@.... - ....@@@.... - ...@@.@@... - ..@@...@@.. - .@@.....@@. - .@@.....@@. - ........... - ........... - ........... - ........... - ........... - -u+0079: -"y": - ........... - ........... - ........... - ........... - ........... - ........... - ........... - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - .@@.....@@. - ..@@....@@. - ...@@@@@@@. - ........@@. - ........@@. - .......@@.. - ..@@@@@@... - ........... - -u+007a: -"z": - ........... - ........... - ........... - ........... - ........... - ........... - ........... - .@@@@@@@@@. - ........@@. - .......@@.. - ......@@... - .....@@.... - ....@@..... - ...@@...... - ..@@....... - .@@........ - .@@@@@@@@@. - ........... - ........... - ........... - ........... - ........... - -u+007b: -braceleft: - ........... - ........... - ........... - ......@@@.. - .....@@.... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ..@@@...... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - .....@@.... - ......@@@.. - ........... - ........... - ........... - ........... - ........... - -u+007c: -bar: - ........... - ........... - ........... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ....@@..... - ........... - ........... - ........... - ........... - ........... - -u+007d: -braceright: - ........... - ........... - ........... - ..@@@...... - ....@@..... - .....@@.... - .....@@.... - .....@@.... - .....@@.... - ......@@@.. - .....@@.... - .....@@.... - .....@@.... - .....@@.... - .....@@.... - ....@@..... - ..@@@...... - ........... - ........... - ........... - ........... - ........... - -u+007e: -asciitilde: - ........... - ..@@@...@@. - .@@.@@..@@. - .@@..@@.@@. - .@@...@@@.. - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - ........... - +name: Terminus Bold 11x22 +spacing: character-cell +cell-size: 11 22 +family: Terminus +foundry: xos4 +copyright: Copyright (C) 2020 Dimitar Toshkov Zhekov +notice: Licensed under the SIL Open Font License, Version 1.1 +point-size: 22 +weight: bold +slant: roman +setwidth: normal +dpi: 72 72 +average-width: 11 +ascent: 17 +descent: 5 +shift-up: -5 +encoding: iso10646-1 +default-char: u+fffd +min-word-space: 11 +converter: monobit v0.32 +source-name: ter-u22b.bdf +source-format: BDF v2.1 +history: load --format=bdf + +u+0000: +char0: + ........... + ........... + ........... + .@@@@.@@@@. + .@@.....@@. + .@@.....@@. + ........... + ........... + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + ........... + ........... + .@@.....@@. + .@@.....@@. + .@@@@.@@@@. + ........... + ........... + ........... + ........... + ........... + +u+0020: +space: + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + +u+0021: +exclam: + ........... + ........... + ........... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ........... + ........... + ....@@..... + ....@@..... + ....@@..... + ........... + ........... + ........... + ........... + ........... + +u+0022: +quotedbl: + ........... + ..@@..@@... + ..@@..@@... + ..@@..@@... + ..@@..@@... + ..@@..@@... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + +u+0023: +numbersign: + ........... + ........... + ........... + ..@@..@@... + ..@@..@@... + ..@@..@@... + ..@@..@@... + @@@@@@@@@@. + ..@@..@@... + ..@@..@@... + ..@@..@@... + ..@@..@@... + @@@@@@@@@@. + ..@@..@@... + ..@@..@@... + ..@@..@@... + ..@@..@@... + ........... + ........... + ........... + ........... + ........... + +u+0024: +dollar: + ........... + ........... + ....@@..... + ....@@..... + ..@@@@@@... + .@@.@@.@@.. + @@..@@..@@. + @@..@@..... + @@..@@..... + .@@.@@..... + ..@@@@@@... + ....@@.@@.. + ....@@..@@. + ....@@..@@. + @@..@@..@@. + .@@.@@.@@.. + ..@@@@@@... + ....@@..... + ....@@..... + ........... + ........... + ........... + +u+0025: +percent: + ........... + ........... + ........... + .@@@...@@.. + @@.@@..@@.. + @@.@@.@@... + .@@@..@@... + .....@@.... + .....@@.... + ....@@..... + ....@@..... + ...@@...... + ...@@...... + ..@@..@@@.. + ..@@.@@.@@. + .@@..@@.@@. + .@@...@@@.. + ........... + ........... + ........... + ........... + ........... + +u+0026: +ampersand: + ........... + ........... + ........... + ..@@@@@.... + .@@...@@... + .@@...@@... + .@@...@@... + ..@@.@@.... + ...@@@..... + ..@@@@..... + .@@..@@.@@. + @@....@@@@. + @@.....@@.. + @@.....@@.. + @@.....@@.. + .@@...@@@@. + ..@@@@@.@@. + ........... + ........... + ........... + ........... + ........... + +u+0027: +quotesingle: + ........... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + +u+0028: +parenleft: + ........... + ........... + ........... + ......@@... + .....@@.... + ....@@..... + ....@@..... + ...@@...... + ...@@...... + ...@@...... + ...@@...... + ...@@...... + ...@@...... + ....@@..... + ....@@..... + .....@@.... + ......@@... + ........... + ........... + ........... + ........... + ........... + +u+0029: +parenright: + ........... + ........... + ........... + ...@@...... + ....@@..... + .....@@.... + .....@@.... + ......@@... + ......@@... + ......@@... + ......@@... + ......@@... + ......@@... + .....@@.... + .....@@.... + ....@@..... + ...@@...... + ........... + ........... + ........... + ........... + ........... + +u+002a: +asterisk: + ........... + ........... + ........... + ........... + ........... + ........... + .@@....@@.. + ..@@..@@... + ...@@@@.... + ....@@..... + @@@@@@@@@@. + ....@@..... + ...@@@@.... + ..@@..@@... + .@@....@@.. + ........... + ........... + ........... + ........... + ........... + ........... + ........... + +u+002b: +plus: + ........... + ........... + ........... + ........... + ........... + ........... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + @@@@@@@@@@. + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + +u+002c: +comma: + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ...@@...... + ........... + ........... + ........... + ........... + +u+002d: +hyphen: + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + .@@@@@@@@@. + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + +u+002e: +period: + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ....@@..... + ....@@..... + ....@@..... + ........... + ........... + ........... + ........... + ........... + +u+002f: +slash: + ........... + ........... + ........... + .......@@.. + .......@@.. + ......@@... + ......@@... + .....@@.... + .....@@.... + ....@@..... + ....@@..... + ...@@...... + ...@@...... + ..@@....... + ..@@....... + .@@........ + .@@........ + ........... + ........... + ........... + ........... + ........... + +u+0030: +zero: + ........... + ........... + ........... + ...@@@@@... + ..@@...@@.. + .@@.....@@. + .@@.....@@. + .@@....@@@. + .@@...@@@@. + .@@..@@.@@. + .@@.@@..@@. + .@@@@...@@. + .@@@....@@. + .@@.....@@. + .@@.....@@. + ..@@...@@.. + ...@@@@@... + ........... + ........... + ........... + ........... + ........... + +u+0031: +one: + ........... + ........... + ........... + .....@@.... + ....@@@.... + ...@@@@.... + ..@@.@@.... + .....@@.... + .....@@.... + .....@@.... + .....@@.... + .....@@.... + .....@@.... + .....@@.... + .....@@.... + .....@@.... + ..@@@@@@@@. + ........... + ........... + ........... + ........... + ........... + +u+0032: +two: + ........... + ........... + ........... + ...@@@@@... + ..@@...@@.. + .@@.....@@. + .@@.....@@. + .@@.....@@. + ........@@. + .......@@.. + ......@@... + .....@@.... + ....@@..... + ...@@...... + ..@@....... + .@@........ + .@@@@@@@@@. + ........... + ........... + ........... + ........... + ........... + +u+0033: +three: + ........... + ........... + ........... + ...@@@@@... + ..@@...@@.. + .@@.....@@. + ........@@. + ........@@. + .......@@.. + ....@@@@... + .......@@.. + ........@@. + ........@@. + ........@@. + .@@.....@@. + ..@@...@@.. + ...@@@@@... + ........... + ........... + ........... + ........... + ........... + +u+0034: +four: + ........... + ........... + ........... + ........@@. + .......@@@. + ......@@@@. + .....@@.@@. + ....@@..@@. + ...@@...@@. + ..@@....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@@@@@@@@. + ........@@. + ........@@. + ........@@. + ........... + ........... + ........... + ........... + ........... + +u+0035: +five: + ........... + ........... + ........... + .@@@@@@@@@. + .@@........ + .@@........ + .@@........ + .@@........ + .@@@@@@@... + .......@@.. + ........@@. + ........@@. + ........@@. + .@@.....@@. + .@@.....@@. + ..@@...@@.. + ...@@@@@... + ........... + ........... + ........... + ........... + ........... + +u+0036: +six: + ........... + ........... + ........... + ...@@@@@@.. + ..@@....... + .@@........ + .@@........ + .@@........ + .@@@@@@@... + .@@....@@.. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + ..@@...@@.. + ...@@@@@... + ........... + ........... + ........... + ........... + ........... + +u+0037: +seven: + ........... + ........... + ........... + .@@@@@@@@@. + .@@.....@@. + .@@.....@@. + ........@@. + .......@@.. + .......@@.. + ......@@... + ......@@... + .....@@.... + .....@@.... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ........... + ........... + ........... + ........... + ........... + +u+0038: +eight: + ........... + ........... + ........... + ...@@@@@... + ..@@...@@.. + .@@.....@@. + .@@.....@@. + .@@.....@@. + ..@@...@@.. + ...@@@@@... + ..@@...@@.. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + ..@@...@@.. + ...@@@@@... + ........... + ........... + ........... + ........... + ........... + +u+0039: +nine: + ........... + ........... + ........... + ...@@@@@... + ..@@...@@.. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + ..@@....@@. + ...@@@@@@@. + ........@@. + ........@@. + ........@@. + .......@@.. + ..@@@@@@... + ........... + ........... + ........... + ........... + ........... + +u+003a: +colon: + ........... + ........... + ........... + ........... + ........... + ........... + ....@@..... + ....@@..... + ....@@..... + ........... + ........... + ........... + ........... + ....@@..... + ....@@..... + ....@@..... + ........... + ........... + ........... + ........... + ........... + ........... + +u+003b: +semicolon: + ........... + ........... + ........... + ........... + ........... + ........... + ....@@..... + ....@@..... + ....@@..... + ........... + ........... + ........... + ........... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ...@@...... + ........... + ........... + ........... + ........... + +u+003c: +less: + ........... + ........... + ........... + .......@@.. + ......@@... + .....@@.... + ....@@..... + ...@@...... + ..@@....... + .@@........ + .@@........ + ..@@....... + ...@@...... + ....@@..... + .....@@.... + ......@@... + .......@@.. + ........... + ........... + ........... + ........... + ........... + +u+003d: +equal: + ........... + ........... + ........... + ........... + ........... + ........... + ........... + .@@@@@@@@@. + ........... + ........... + ........... + ........... + .@@@@@@@@@. + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + +u+003e: +greater: + ........... + ........... + ........... + .@@........ + ..@@....... + ...@@...... + ....@@..... + .....@@.... + ......@@... + .......@@.. + .......@@.. + ......@@... + .....@@.... + ....@@..... + ...@@...... + ..@@....... + .@@........ + ........... + ........... + ........... + ........... + ........... + +u+003f: +question: + ........... + ........... + ........... + ...@@@@@... + ..@@...@@.. + .@@.....@@. + .@@.....@@. + ........@@. + .......@@.. + ......@@... + .....@@.... + .....@@.... + ........... + ........... + .....@@.... + .....@@.... + .....@@.... + ........... + ........... + ........... + ........... + ........... + +u+0040: +at: + ........... + ........... + ........... + ..@@@@@@... + .@@....@@.. + @@......@@. + @@...@@@@@. + @@..@@..@@. + @@.@@...@@. + @@.@@...@@. + @@.@@...@@. + @@.@@...@@. + @@..@@..@@. + @@...@@@@@. + @@......... + .@@........ + ..@@@@@@@@. + ........... + ........... + ........... + ........... + ........... + +u+0041: +"A": + ........... + ........... + ........... + ...@@@@@... + ..@@...@@.. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@@@@@@@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + ........... + ........... + ........... + ........... + ........... + +u+0042: +"B": + ........... + ........... + ........... + .@@@@@@@... + .@@....@@.. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@....@@.. + .@@@@@@@... + .@@....@@.. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@....@@.. + .@@@@@@@... + ........... + ........... + ........... + ........... + ........... + +u+0043: +"C": + ........... + ........... + ........... + ...@@@@@... + ..@@...@@.. + .@@.....@@. + .@@.....@@. + .@@........ + .@@........ + .@@........ + .@@........ + .@@........ + .@@........ + .@@.....@@. + .@@.....@@. + ..@@...@@.. + ...@@@@@... + ........... + ........... + ........... + ........... + ........... + +u+0044: +"D": + ........... + ........... + ........... + .@@@@@@@... + .@@....@@.. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@....@@.. + .@@@@@@@... + ........... + ........... + ........... + ........... + ........... + +u+0045: +"E": + ........... + ........... + ........... + .@@@@@@@@@. + .@@........ + .@@........ + .@@........ + .@@........ + .@@........ + .@@@@@@@... + .@@........ + .@@........ + .@@........ + .@@........ + .@@........ + .@@........ + .@@@@@@@@@. + ........... + ........... + ........... + ........... + ........... + +u+0046: +"F": + ........... + ........... + ........... + .@@@@@@@@@. + .@@........ + .@@........ + .@@........ + .@@........ + .@@........ + .@@@@@@@... + .@@........ + .@@........ + .@@........ + .@@........ + .@@........ + .@@........ + .@@........ + ........... + ........... + ........... + ........... + ........... + +u+0047: +"G": + ........... + ........... + ........... + ...@@@@@... + ..@@...@@.. + .@@.....@@. + .@@.....@@. + .@@........ + .@@........ + .@@........ + .@@..@@@@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + ..@@...@@.. + ...@@@@@... + ........... + ........... + ........... + ........... + ........... + +u+0048: +"H": + ........... + ........... + ........... + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@@@@@@@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + ........... + ........... + ........... + ........... + ........... + +u+0049: +"I": + ........... + ........... + ........... + ..@@@@@@... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ..@@@@@@... + ........... + ........... + ........... + ........... + ........... + +u+004a: +"J": + ........... + ........... + ........... + .....@@@@@@ + .......@@.. + .......@@.. + .......@@.. + .......@@.. + .......@@.. + .......@@.. + .......@@.. + .......@@.. + .@@....@@.. + .@@....@@.. + .@@....@@.. + ..@@..@@... + ...@@@@.... + ........... + ........... + ........... + ........... + ........... + +u+004b: +"K": + ........... + ........... + ........... + .@@.....@@. + .@@.....@@. + .@@....@@.. + .@@...@@... + .@@..@@.... + .@@.@@..... + .@@@@...... + .@@@@...... + .@@.@@..... + .@@..@@.... + .@@...@@... + .@@....@@.. + .@@.....@@. + .@@.....@@. + ........... + ........... + ........... + ........... + ........... + +u+004c: +"L": + ........... + ........... + ........... + .@@........ + .@@........ + .@@........ + .@@........ + .@@........ + .@@........ + .@@........ + .@@........ + .@@........ + .@@........ + .@@........ + .@@........ + .@@........ + .@@@@@@@@@. + ........... + ........... + ........... + ........... + ........... + +u+004d: +"M": + ........... + ........... + ........... + @........@. + @@......@@. + @@@....@@@. + @@@@..@@@@. + @@.@@@@.@@. + @@..@@..@@. + @@......@@. + @@......@@. + @@......@@. + @@......@@. + @@......@@. + @@......@@. + @@......@@. + @@......@@. + ........... + ........... + ........... + ........... + ........... + +u+004e: +"N": + ........... + ........... + ........... + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@@....@@. + .@@@@...@@. + .@@.@@..@@. + .@@..@@.@@. + .@@...@@@@. + .@@....@@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + ........... + ........... + ........... + ........... + ........... + +u+004f: +"O": + ........... + ........... + ........... + ...@@@@@... + ..@@...@@.. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + ..@@...@@.. + ...@@@@@... + ........... + ........... + ........... + ........... + ........... + +u+0050: +"P": + ........... + ........... + ........... + .@@@@@@@... + .@@....@@.. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@....@@.. + .@@@@@@@... + .@@........ + .@@........ + .@@........ + .@@........ + .@@........ + .@@........ + ........... + ........... + ........... + ........... + ........... + +u+0051: +"Q": + ........... + ........... + ........... + ...@@@@@... + ..@@...@@.. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + ..@@.@@@@.. + ...@@@@@... + .......@@.. + ........@@. + ........... + ........... + ........... + +u+0052: +"R": + ........... + ........... + ........... + .@@@@@@@... + .@@....@@.. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@....@@.. + .@@@@@@@... + .@@@@...... + .@@.@@..... + .@@..@@.... + .@@...@@... + .@@....@@.. + .@@.....@@. + ........... + ........... + ........... + ........... + ........... + +u+0053: +"S": + ........... + ........... + ........... + ...@@@@@... + ..@@...@@.. + .@@.....@@. + .@@........ + .@@........ + ..@@....... + ...@@@@@... + .......@@.. + ........@@. + ........@@. + ........@@. + .@@.....@@. + ..@@...@@.. + ...@@@@@... + ........... + ........... + ........... + ........... + ........... + +u+0054: +"T": + ........... + ........... + ........... + @@@@@@@@@@. + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ........... + ........... + ........... + ........... + ........... + +u+0055: +"U": + ........... + ........... + ........... + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + ..@@...@@.. + ...@@@@@... + ........... + ........... + ........... + ........... + ........... + +u+0056: +"V": + ........... + ........... + ........... + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + ..@@...@@.. + ..@@...@@.. + ..@@...@@.. + ..@@...@@.. + ...@@.@@... + ...@@.@@... + ....@@@.... + ....@@@.... + ....@@@.... + ........... + ........... + ........... + ........... + ........... + +u+0057: +"W": + ........... + ........... + ........... + @@......@@. + @@......@@. + @@......@@. + @@......@@. + @@......@@. + @@......@@. + @@......@@. + @@..@@..@@. + @@..@@..@@. + @@.@@@@.@@. + @@@@..@@@@. + @@@....@@@. + @@......@@. + @........@. + ........... + ........... + ........... + ........... + ........... + +u+0058: +"X": + ........... + ........... + ........... + .@@.....@@. + .@@.....@@. + ..@@...@@.. + ..@@...@@.. + ...@@.@@... + ...@@.@@... + ....@@@.... + ....@@@.... + ...@@.@@... + ...@@.@@... + ..@@...@@.. + ..@@...@@.. + .@@.....@@. + .@@.....@@. + ........... + ........... + ........... + ........... + ........... + +u+0059: +"Y": + ........... + ........... + ........... + @@......@@. + @@......@@. + .@@....@@.. + .@@....@@.. + ..@@..@@... + ..@@..@@... + ...@@@@.... + ...@@@@.... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ........... + ........... + ........... + ........... + ........... + +u+005a: +"Z": + ........... + ........... + ........... + .@@@@@@@@@. + ........@@. + ........@@. + ........@@. + .......@@.. + ......@@... + .....@@.... + ....@@..... + ...@@...... + ..@@....... + .@@........ + .@@........ + .@@........ + .@@@@@@@@@. + ........... + ........... + ........... + ........... + ........... + +u+005b: +bracketleft: + ........... + ........... + ........... + ...@@@@@... + ...@@...... + ...@@...... + ...@@...... + ...@@...... + ...@@...... + ...@@...... + ...@@...... + ...@@...... + ...@@...... + ...@@...... + ...@@...... + ...@@...... + ...@@@@@... + ........... + ........... + ........... + ........... + ........... + +u+005c: +backslash: + ........... + ........... + ........... + .@@........ + .@@........ + ..@@....... + ..@@....... + ...@@...... + ...@@...... + ....@@..... + ....@@..... + .....@@.... + .....@@.... + ......@@... + ......@@... + .......@@.. + .......@@.. + ........... + ........... + ........... + ........... + ........... + +u+005d: +bracketright: + ........... + ........... + ........... + ...@@@@@... + ......@@... + ......@@... + ......@@... + ......@@... + ......@@... + ......@@... + ......@@... + ......@@... + ......@@... + ......@@... + ......@@... + ......@@... + ...@@@@@... + ........... + ........... + ........... + ........... + ........... + +u+005e: +asciicircum: + ........... + .....@..... + ....@@@.... + ...@@.@@... + ..@@...@@.. + .@@.....@@. + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + +u+005f: +underscore: + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + .@@@@@@@@@. + ........... + ........... + ........... + +u+0060: +grave: + ...@@...... + ....@@..... + .....@@.... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + +u+0061: +"a": + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ..@@@@@@... + .......@@.. + ........@@. + ........@@. + ..@@@@@@@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + ..@@@@@@@@. + ........... + ........... + ........... + ........... + ........... + +u+0062: +"b": + ........... + ........... + ........... + .@@........ + .@@........ + .@@........ + .@@........ + .@@@@@@@... + .@@....@@.. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@....@@.. + .@@@@@@@... + ........... + ........... + ........... + ........... + ........... + +u+0063: +"c": + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ...@@@@@... + ..@@...@@.. + .@@.....@@. + .@@........ + .@@........ + .@@........ + .@@........ + .@@.....@@. + ..@@...@@.. + ...@@@@@... + ........... + ........... + ........... + ........... + ........... + +u+0064: +"d": + ........... + ........... + ........... + ........@@. + ........@@. + ........@@. + ........@@. + ...@@@@@@@. + ..@@....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + ..@@....@@. + ...@@@@@@@. + ........... + ........... + ........... + ........... + ........... + +u+0065: +"e": + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ...@@@@@... + ..@@...@@.. + .@@.....@@. + .@@.....@@. + .@@@@@@@@@. + .@@........ + .@@........ + .@@........ + ..@@....@@. + ...@@@@@@.. + ........... + ........... + ........... + ........... + ........... + +u+0066: +"f": + ........... + ........... + ........... + .....@@@@@. + ....@@..... + ....@@..... + ....@@..... + .@@@@@@@@.. + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ........... + ........... + ........... + ........... + ........... + +u+0067: +"g": + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ...@@@@@@@. + ..@@....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + ..@@....@@. + ...@@@@@@@. + ........@@. + ........@@. + .......@@.. + ..@@@@@@... + ........... + +u+0068: +"h": + ........... + ........... + ........... + .@@........ + .@@........ + .@@........ + .@@........ + .@@@@@@@... + .@@....@@.. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + ........... + ........... + ........... + ........... + ........... + +u+0069: +"i": + ........... + ........... + ........... + .....@@.... + .....@@.... + .....@@.... + ........... + ...@@@@.... + .....@@.... + .....@@.... + .....@@.... + .....@@.... + .....@@.... + .....@@.... + .....@@.... + .....@@.... + ...@@@@@@.. + ........... + ........... + ........... + ........... + ........... + +u+006a: +"j": + ........... + ........... + ........... + .......@@.. + .......@@.. + .......@@.. + ........... + .....@@@@.. + .......@@.. + .......@@.. + .......@@.. + .......@@.. + .......@@.. + .......@@.. + .......@@.. + .......@@.. + .......@@.. + ..@@...@@.. + ..@@...@@.. + ..@@...@@.. + ...@@@@@... + ........... + +u+006b: +"k": + ........... + ........... + ........... + ..@@....... + ..@@....... + ..@@....... + ..@@....... + ..@@....@@. + ..@@...@@.. + ..@@..@@... + ..@@.@@.... + ..@@@@..... + ..@@@@..... + ..@@.@@.... + ..@@..@@... + ..@@...@@.. + ..@@....@@. + ........... + ........... + ........... + ........... + ........... + +u+006c: +"l": + ........... + ........... + ........... + ...@@@@.... + .....@@.... + .....@@.... + .....@@.... + .....@@.... + .....@@.... + .....@@.... + .....@@.... + .....@@.... + .....@@.... + .....@@.... + .....@@.... + .....@@.... + ...@@@@@@.. + ........... + ........... + ........... + ........... + ........... + +u+006d: +"m": + ........... + ........... + ........... + ........... + ........... + ........... + ........... + @@@@@@@@... + @@..@@.@@.. + @@..@@..@@. + @@..@@..@@. + @@..@@..@@. + @@..@@..@@. + @@..@@..@@. + @@..@@..@@. + @@..@@..@@. + @@..@@..@@. + ........... + ........... + ........... + ........... + ........... + +u+006e: +"n": + ........... + ........... + ........... + ........... + ........... + ........... + ........... + .@@@@@@@... + .@@....@@.. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + ........... + ........... + ........... + ........... + ........... + +u+006f: +"o": + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ...@@@@@... + ..@@...@@.. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + ..@@...@@.. + ...@@@@@... + ........... + ........... + ........... + ........... + ........... + +u+0070: +"p": + ........... + ........... + ........... + ........... + ........... + ........... + ........... + .@@@@@@@... + .@@....@@.. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@....@@.. + .@@@@@@@... + .@@........ + .@@........ + .@@........ + .@@........ + ........... + +u+0071: +"q": + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ...@@@@@@@. + ..@@....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + ..@@....@@. + ...@@@@@@@. + ........@@. + ........@@. + ........@@. + ........@@. + ........... + +u+0072: +"r": + ........... + ........... + ........... + ........... + ........... + ........... + ........... + .@@..@@@@@. + .@@.@@..... + .@@@@...... + .@@@....... + .@@........ + .@@........ + .@@........ + .@@........ + .@@........ + .@@........ + ........... + ........... + ........... + ........... + ........... + +u+0073: +"s": + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ..@@@@@@@.. + .@@.....@@. + .@@........ + .@@........ + ..@@@@@@@.. + ........@@. + ........@@. + ........@@. + .@@.....@@. + ..@@@@@@@.. + ........... + ........... + ........... + ........... + ........... + +u+0074: +"t": + ........... + ........... + ........... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + .@@@@@@@@.. + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + .....@@@@@. + ........... + ........... + ........... + ........... + ........... + +u+0075: +"u": + ........... + ........... + ........... + ........... + ........... + ........... + ........... + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + ..@@....@@. + ...@@@@@@@. + ........... + ........... + ........... + ........... + ........... + +u+0076: +"v": + ........... + ........... + ........... + ........... + ........... + ........... + ........... + .@@.....@@. + .@@.....@@. + .@@.....@@. + ..@@...@@.. + ..@@...@@.. + ..@@...@@.. + ...@@.@@... + ...@@.@@... + ....@@@.... + ....@@@.... + ........... + ........... + ........... + ........... + ........... + +u+0077: +"w": + ........... + ........... + ........... + ........... + ........... + ........... + ........... + @@......@@. + @@......@@. + @@......@@. + @@..@@..@@. + @@..@@..@@. + @@..@@..@@. + @@..@@..@@. + @@..@@..@@. + @@..@@..@@. + .@@@@@@@@.. + ........... + ........... + ........... + ........... + ........... + +u+0078: +"x": + ........... + ........... + ........... + ........... + ........... + ........... + ........... + .@@.....@@. + .@@.....@@. + ..@@...@@.. + ...@@.@@... + ....@@@.... + ....@@@.... + ...@@.@@... + ..@@...@@.. + .@@.....@@. + .@@.....@@. + ........... + ........... + ........... + ........... + ........... + +u+0079: +"y": + ........... + ........... + ........... + ........... + ........... + ........... + ........... + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + .@@.....@@. + ..@@....@@. + ...@@@@@@@. + ........@@. + ........@@. + .......@@.. + ..@@@@@@... + ........... + +u+007a: +"z": + ........... + ........... + ........... + ........... + ........... + ........... + ........... + .@@@@@@@@@. + ........@@. + .......@@.. + ......@@... + .....@@.... + ....@@..... + ...@@...... + ..@@....... + .@@........ + .@@@@@@@@@. + ........... + ........... + ........... + ........... + ........... + +u+007b: +braceleft: + ........... + ........... + ........... + ......@@@.. + .....@@.... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ..@@@...... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + .....@@.... + ......@@@.. + ........... + ........... + ........... + ........... + ........... + +u+007c: +bar: + ........... + ........... + ........... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ....@@..... + ........... + ........... + ........... + ........... + ........... + +u+007d: +braceright: + ........... + ........... + ........... + ..@@@...... + ....@@..... + .....@@.... + .....@@.... + .....@@.... + .....@@.... + ......@@@.. + .....@@.... + .....@@.... + .....@@.... + .....@@.... + .....@@.... + ....@@..... + ..@@@...... + ........... + ........... + ........... + ........... + ........... + +u+007e: +asciitilde: + ........... + ..@@@...@@. + .@@.@@..@@. + .@@..@@.@@. + .@@...@@@.. + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + +u+00a0: +nbspace: + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... + ........... diff --git a/containers/test/fonts/terminus-ascii-bold-24px.yaff b/containers/test/fonts/terminus-24px.yaff similarity index 94% rename from containers/test/fonts/terminus-ascii-bold-24px.yaff rename to containers/test/fonts/terminus-24px.yaff index c2822dfa2..68cea8411 100644 --- a/containers/test/fonts/terminus-ascii-bold-24px.yaff +++ b/containers/test/fonts/terminus-24px.yaff @@ -1,2616 +1,2642 @@ -name: Terminus Bold 12x24 -spacing: character-cell -cell-size: 12 24 -family: Terminus -foundry: xos4 -copyright: Copyright (C) 2020 Dimitar Toshkov Zhekov -notice: Licensed under the SIL Open Font License, Version 1.1 -point-size: 24 -weight: bold -slant: roman -setwidth: normal -dpi: 72 72 -average-width: 12 -ascent: 19 -descent: 5 -shift-up: -5 -encoding: iso10646-1 -default-char: u+fffd -min-word-space: 12 -converter: monobit v0.32 -source-name: ter-u24b.bdf -source-format: BDF v2.1 -history: load --format=bdf - -u+0000: -char0: - ............ - ............ - ............ - ............ - .@@@@..@@@@. - .@@......@@. - .@@......@@. - .@@......@@. - ............ - ............ - .@@......@@. - .@@......@@. - .@@......@@. - ............ - ............ - .@@......@@. - .@@......@@. - .@@......@@. - .@@@@..@@@@. - ............ - ............ - ............ - ............ - ............ - -u+0020: -space: - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - -u+0021: -exclam: - ............ - ............ - ............ - ............ - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - ............ - ............ - .....@@..... - .....@@..... - .....@@..... - ............ - ............ - ............ - ............ - ............ - -u+0022: -quotedbl: - ............ - ............ - ...@@..@@... - ...@@..@@... - ...@@..@@... - ...@@..@@... - ...@@..@@... - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - -u+0023: -numbersign: - ............ - ............ - ............ - ............ - ...@@..@@... - ...@@..@@... - ...@@..@@... - ...@@..@@... - .@@@@@@@@@@. - ...@@..@@... - ...@@..@@... - ...@@..@@... - ...@@..@@... - ...@@..@@... - .@@@@@@@@@@. - ...@@..@@... - ...@@..@@... - ...@@..@@... - ...@@..@@... - ............ - ............ - ............ - ............ - ............ - -u+0024: -dollar: - ............ - ............ - ............ - .....@@..... - .....@@..... - ...@@@@@@... - ..@@.@@.@@.. - .@@..@@..@@. - .@@..@@..... - .@@..@@..... - ..@@.@@..... - ...@@@@@@... - .....@@.@@.. - .....@@..@@. - .....@@..@@. - .@@..@@..@@. - ..@@.@@.@@.. - ...@@@@@@... - .....@@..... - .....@@..... - ............ - ............ - ............ - ............ - -u+0025: -percent: - ............ - ............ - ............ - ............ - ............ - ..@@@...@@.. - .@@.@@..@@.. - .@@.@@.@@... - ..@@@..@@... - ......@@.... - ......@@.... - .....@@..... - .....@@..... - ....@@...... - ....@@...... - ...@@..@@@.. - ...@@.@@.@@. - ..@@..@@.@@. - ..@@...@@@.. - ............ - ............ - ............ - ............ - ............ - -u+0026: -ampersand: - ............ - ............ - ............ - ............ - ....@@@..... - ...@@.@@.... - ..@@...@@... - ..@@...@@... - ..@@...@@... - ...@@.@@.... - ....@@@..... - ...@@@@..@@. - ..@@..@@.@@. - .@@....@@@.. - .@@.....@@.. - .@@.....@@.. - .@@....@@@.. - ..@@..@@.@@. - ...@@@@..@@. - ............ - ............ - ............ - ............ - ............ - -u+0027: -quotesingle: - ............ - ............ - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - -u+0028: -parenleft: - ............ - ............ - ............ - ............ - ......@@.... - .....@@..... - ....@@...... - ....@@...... - ...@@....... - ...@@....... - ...@@....... - ...@@....... - ...@@....... - ...@@....... - ...@@....... - ....@@...... - ....@@...... - .....@@..... - ......@@.... - ............ - ............ - ............ - ............ - ............ - -u+0029: -parenright: - ............ - ............ - ............ - ............ - ...@@....... - ....@@...... - .....@@..... - .....@@..... - ......@@.... - ......@@.... - ......@@.... - ......@@.... - ......@@.... - ......@@.... - ......@@.... - .....@@..... - .....@@..... - ....@@...... - ...@@....... - ............ - ............ - ............ - ............ - ............ - -u+002a: -asterisk: - ............ - ............ - ............ - ............ - ............ - ............ - ............ - .@@.....@@.. - ..@@...@@... - ...@@.@@.... - ....@@@..... - @@@@@@@@@@@. - ....@@@..... - ...@@.@@.... - ..@@...@@... - .@@.....@@.. - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - -u+002b: -plus: - ............ - ............ - ............ - ............ - ............ - ............ - ............ - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .@@@@@@@@@@. - .....@@..... - .....@@..... - .....@@..... - .....@@..... - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - -u+002c: -comma: - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - .....@@..... - .....@@..... - .....@@..... - .....@@..... - ....@@...... - ............ - ............ - ............ - ............ - -u+002d: -hyphen: - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - .@@@@@@@@@@. - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - -u+002e: -period: - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - .....@@..... - .....@@..... - .....@@..... - ............ - ............ - ............ - ............ - ............ - -u+002f: -slash: - ............ - ............ - ............ - ............ - ............ - ........@@.. - ........@@.. - .......@@... - .......@@... - ......@@.... - ......@@.... - .....@@..... - .....@@..... - ....@@...... - ....@@...... - ...@@....... - ...@@....... - ..@@........ - ..@@........ - ............ - ............ - ............ - ............ - ............ - -u+0030: -zero: - ............ - ............ - ............ - ............ - ...@@@@@@... - ..@@....@@.. - .@@......@@. - .@@......@@. - .@@.....@@@. - .@@....@@@@. - .@@...@@.@@. - .@@..@@..@@. - .@@.@@...@@. - .@@@@....@@. - .@@@.....@@. - .@@......@@. - .@@......@@. - ..@@....@@.. - ...@@@@@@... - ............ - ............ - ............ - ............ - ............ - -u+0031: -one: - ............ - ............ - ............ - ............ - .....@@..... - ....@@@..... - ...@@@@..... - ..@@.@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - ..@@@@@@@@.. - ............ - ............ - ............ - ............ - ............ - -u+0032: -two: - ............ - ............ - ............ - ............ - ...@@@@@@... - ..@@....@@.. - .@@......@@. - .@@......@@. - .@@......@@. - .........@@. - ........@@.. - .......@@... - ......@@.... - .....@@..... - ....@@...... - ...@@....... - ..@@........ - .@@......... - .@@@@@@@@@@. - ............ - ............ - ............ - ............ - ............ - -u+0033: -three: - ............ - ............ - ............ - ............ - ...@@@@@@... - ..@@....@@.. - .@@......@@. - .........@@. - .........@@. - .........@@. - ........@@.. - ....@@@@@... - ........@@.. - .........@@. - .........@@. - .........@@. - .@@......@@. - ..@@....@@.. - ...@@@@@@... - ............ - ............ - ............ - ............ - ............ - -u+0034: -four: - ............ - ............ - ............ - ............ - .........@@. - ........@@@. - .......@@@@. - ......@@.@@. - .....@@..@@. - ....@@...@@. - ...@@....@@. - ..@@.....@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@@@@@@@@@. - .........@@. - .........@@. - .........@@. - ............ - ............ - ............ - ............ - ............ - -u+0035: -five: - ............ - ............ - ............ - ............ - .@@@@@@@@@@. - .@@......... - .@@......... - .@@......... - .@@......... - .@@......... - .@@@@@@@@... - ........@@.. - .........@@. - .........@@. - .........@@. - .........@@. - .@@......@@. - ..@@....@@.. - ...@@@@@@... - ............ - ............ - ............ - ............ - ............ - -u+0036: -six: - ............ - ............ - ............ - ............ - ...@@@@@@@.. - ..@@........ - .@@......... - .@@......... - .@@......... - .@@......... - .@@@@@@@@... - .@@.....@@.. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - ..@@....@@.. - ...@@@@@@... - ............ - ............ - ............ - ............ - ............ - -u+0037: -seven: - ............ - ............ - ............ - ............ - .@@@@@@@@@@. - .@@......@@. - .@@......@@. - .........@@. - ........@@.. - ........@@.. - .......@@... - .......@@... - ......@@.... - ......@@.... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - ............ - ............ - ............ - ............ - ............ - -u+0038: -eight: - ............ - ............ - ............ - ............ - ...@@@@@@... - ..@@....@@.. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - ..@@....@@.. - ...@@@@@@... - ..@@....@@.. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - ..@@....@@.. - ...@@@@@@... - ............ - ............ - ............ - ............ - ............ - -u+0039: -nine: - ............ - ............ - ............ - ............ - ...@@@@@@... - ..@@....@@.. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - ..@@.....@@. - ...@@@@@@@@. - .........@@. - .........@@. - .........@@. - .........@@. - ........@@.. - ..@@@@@@@... - ............ - ............ - ............ - ............ - ............ - -u+003a: -colon: - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - .....@@..... - .....@@..... - .....@@..... - ............ - ............ - ............ - ............ - .....@@..... - .....@@..... - .....@@..... - ............ - ............ - ............ - ............ - ............ - ............ - -u+003b: -semicolon: - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - .....@@..... - .....@@..... - .....@@..... - ............ - ............ - ............ - ............ - .....@@..... - .....@@..... - .....@@..... - .....@@..... - ....@@...... - ............ - ............ - ............ - ............ - -u+003c: -less: - ............ - ............ - ............ - ............ - ........@@.. - .......@@... - ......@@.... - .....@@..... - ....@@...... - ...@@....... - ..@@........ - .@@......... - ..@@........ - ...@@....... - ....@@...... - .....@@..... - ......@@.... - .......@@... - ........@@.. - ............ - ............ - ............ - ............ - ............ - -u+003d: -equal: - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - .@@@@@@@@@@. - ............ - ............ - ............ - ............ - .@@@@@@@@@@. - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - -u+003e: -greater: - ............ - ............ - ............ - ............ - .@@......... - ..@@........ - ...@@....... - ....@@...... - .....@@..... - ......@@.... - .......@@... - ........@@.. - .......@@... - ......@@.... - .....@@..... - ....@@...... - ...@@....... - ..@@........ - .@@......... - ............ - ............ - ............ - ............ - ............ - -u+003f: -question: - ............ - ............ - ............ - ............ - ...@@@@@@... - ..@@....@@.. - .@@......@@. - .@@......@@. - .@@......@@. - ........@@.. - .......@@... - ......@@.... - .....@@..... - .....@@..... - ............ - ............ - .....@@..... - .....@@..... - .....@@..... - ............ - ............ - ............ - ............ - ............ - -u+0040: -at: - ............ - ............ - ............ - ............ - ..@@@@@@@... - .@@.....@@.. - @@.......@@. - @@....@@@@@. - @@...@@..@@. - @@..@@...@@. - @@..@@...@@. - @@..@@...@@. - @@..@@...@@. - @@..@@...@@. - @@...@@..@@. - @@....@@@@@. - @@.......... - .@@......... - ..@@@@@@@@@. - ............ - ............ - ............ - ............ - ............ - -u+0041: -"A": - ............ - ............ - ............ - ............ - ...@@@@@@... - ..@@....@@.. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@@@@@@@@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - ............ - ............ - ............ - ............ - ............ - -u+0042: -"B": - ............ - ............ - ............ - ............ - .@@@@@@@@... - .@@.....@@.. - .@@......@@. - .@@......@@. - .@@......@@. - .@@.....@@.. - .@@@@@@@@... - .@@.....@@.. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@.....@@.. - .@@@@@@@@... - ............ - ............ - ............ - ............ - ............ - -u+0043: -"C": - ............ - ............ - ............ - ............ - ...@@@@@@... - ..@@....@@.. - .@@......@@. - .@@......@@. - .@@......... - .@@......... - .@@......... - .@@......... - .@@......... - .@@......... - .@@......... - .@@......@@. - .@@......@@. - ..@@....@@.. - ...@@@@@@... - ............ - ............ - ............ - ............ - ............ - -u+0044: -"D": - ............ - ............ - ............ - ............ - .@@@@@@@@... - .@@.....@@.. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@.....@@.. - .@@@@@@@@... - ............ - ............ - ............ - ............ - ............ - -u+0045: -"E": - ............ - ............ - ............ - ............ - .@@@@@@@@@@. - .@@......... - .@@......... - .@@......... - .@@......... - .@@......... - .@@......... - .@@@@@@@@... - .@@......... - .@@......... - .@@......... - .@@......... - .@@......... - .@@......... - .@@@@@@@@@@. - ............ - ............ - ............ - ............ - ............ - -u+0046: -"F": - ............ - ............ - ............ - ............ - .@@@@@@@@@@. - .@@......... - .@@......... - .@@......... - .@@......... - .@@......... - .@@......... - .@@@@@@@@... - .@@......... - .@@......... - .@@......... - .@@......... - .@@......... - .@@......... - .@@......... - ............ - ............ - ............ - ............ - ............ - -u+0047: -"G": - ............ - ............ - ............ - ............ - ...@@@@@@... - ..@@....@@.. - .@@......@@. - .@@......@@. - .@@......... - .@@......... - .@@......... - .@@...@@@@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - ..@@....@@.. - ...@@@@@@... - ............ - ............ - ............ - ............ - ............ - -u+0048: -"H": - ............ - ............ - ............ - ............ - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@@@@@@@@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - ............ - ............ - ............ - ............ - ............ - -u+0049: -"I": - ............ - ............ - ............ - ............ - ...@@@@@@... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - ...@@@@@@... - ............ - ............ - ............ - ............ - ............ - -u+004a: -"J": - ............ - ............ - ............ - ............ - ......@@@@@@ - ........@@.. - ........@@.. - ........@@.. - ........@@.. - ........@@.. - ........@@.. - ........@@.. - ........@@.. - ........@@.. - .@@.....@@.. - .@@.....@@.. - .@@.....@@.. - ..@@...@@... - ...@@@@@.... - ............ - ............ - ............ - ............ - ............ - -u+004b: -"K": - ............ - ............ - ............ - ............ - .@@......@@. - .@@.....@@.. - .@@....@@... - .@@...@@.... - .@@..@@..... - .@@.@@...... - .@@@@....... - .@@@........ - .@@@@....... - .@@.@@...... - .@@..@@..... - .@@...@@.... - .@@....@@... - .@@.....@@.. - .@@......@@. - ............ - ............ - ............ - ............ - ............ - -u+004c: -"L": - ............ - ............ - ............ - ............ - .@@......... - .@@......... - .@@......... - .@@......... - .@@......... - .@@......... - .@@......... - .@@......... - .@@......... - .@@......... - .@@......... - .@@......... - .@@......... - .@@......... - .@@@@@@@@@@. - ............ - ............ - ............ - ............ - ............ - -u+004d: -"M": - ............ - ............ - ............ - ............ - @.........@. - @@.......@@. - @@@.....@@@. - @@@@...@@@@. - @@.@@.@@.@@. - @@..@@@..@@. - @@...@...@@. - @@.......@@. - @@.......@@. - @@.......@@. - @@.......@@. - @@.......@@. - @@.......@@. - @@.......@@. - @@.......@@. - ............ - ............ - ............ - ............ - ............ - -u+004e: -"N": - ............ - ............ - ............ - ............ - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@@.....@@. - .@@@@....@@. - .@@.@@...@@. - .@@..@@..@@. - .@@...@@.@@. - .@@....@@@@. - .@@.....@@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - ............ - ............ - ............ - ............ - ............ - -u+004f: -"O": - ............ - ............ - ............ - ............ - ...@@@@@@... - ..@@....@@.. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - ..@@....@@.. - ...@@@@@@... - ............ - ............ - ............ - ............ - ............ - -u+0050: -"P": - ............ - ............ - ............ - ............ - .@@@@@@@@... - .@@.....@@.. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@.....@@.. - .@@@@@@@@... - .@@......... - .@@......... - .@@......... - .@@......... - .@@......... - .@@......... - .@@......... - ............ - ............ - ............ - ............ - ............ - -u+0051: -"Q": - ............ - ............ - ............ - ............ - ...@@@@@@... - ..@@....@@.. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@..@@..@@. - ..@@..@@@@.. - ...@@@@@@... - ........@@.. - .........@@. - ............ - ............ - ............ - -u+0052: -"R": - ............ - ............ - ............ - ............ - .@@@@@@@@... - .@@.....@@.. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@.....@@.. - .@@@@@@@@... - .@@@@....... - .@@.@@...... - .@@..@@..... - .@@...@@.... - .@@....@@... - .@@.....@@.. - .@@......@@. - ............ - ............ - ............ - ............ - ............ - -u+0053: -"S": - ............ - ............ - ............ - ............ - ...@@@@@@... - ..@@....@@.. - .@@......@@. - .@@......... - .@@......... - .@@......... - ..@@........ - ...@@@@@@... - ........@@.. - .........@@. - .........@@. - .........@@. - .@@......@@. - ..@@....@@.. - ...@@@@@@... - ............ - ............ - ............ - ............ - ............ - -u+0054: -"T": - ............ - ............ - ............ - ............ - .@@@@@@@@@@. - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - ............ - ............ - ............ - ............ - ............ - -u+0055: -"U": - ............ - ............ - ............ - ............ - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - ..@@....@@.. - ...@@@@@@... - ............ - ............ - ............ - ............ - ............ - -u+0056: -"V": - ............ - ............ - ............ - ............ - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - ..@@....@@.. - ..@@....@@.. - ..@@....@@.. - ..@@....@@.. - ...@@..@@... - ...@@..@@... - ...@@..@@... - ....@@@@.... - ....@@@@.... - .....@@..... - .....@@..... - ............ - ............ - ............ - ............ - ............ - -u+0057: -"W": - ............ - ............ - ............ - ............ - @@.......@@. - @@.......@@. - @@.......@@. - @@.......@@. - @@.......@@. - @@.......@@. - @@.......@@. - @@.......@@. - @@...@...@@. - @@..@@@..@@. - @@.@@.@@.@@. - @@@@...@@@@. - @@@.....@@@. - @@.......@@. - @.........@. - ............ - ............ - ............ - ............ - ............ - -u+0058: -"X": - ............ - ............ - ............ - ............ - .@@......@@. - .@@......@@. - ..@@....@@.. - ..@@....@@.. - ...@@..@@... - ...@@..@@... - ....@@@@.... - .....@@..... - ....@@@@.... - ...@@..@@... - ...@@..@@... - ..@@....@@.. - ..@@....@@.. - .@@......@@. - .@@......@@. - ............ - ............ - ............ - ............ - ............ - -u+0059: -"Y": - ............ - ............ - ............ - ............ - .@@......@@. - .@@......@@. - ..@@....@@.. - ..@@....@@.. - ...@@..@@... - ...@@..@@... - ....@@@@.... - ....@@@@.... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - ............ - ............ - ............ - ............ - ............ - -u+005a: -"Z": - ............ - ............ - ............ - ............ - .@@@@@@@@@@. - .........@@. - .........@@. - .........@@. - ........@@.. - .......@@... - ......@@.... - .....@@..... - ....@@...... - ...@@....... - ..@@........ - .@@......... - .@@......... - .@@......... - .@@@@@@@@@@. - ............ - ............ - ............ - ............ - ............ - -u+005b: -bracketleft: - ............ - ............ - ............ - ............ - ...@@@@@.... - ...@@....... - ...@@....... - ...@@....... - ...@@....... - ...@@....... - ...@@....... - ...@@....... - ...@@....... - ...@@....... - ...@@....... - ...@@....... - ...@@....... - ...@@....... - ...@@@@@.... - ............ - ............ - ............ - ............ - ............ - -u+005c: -backslash: - ............ - ............ - ............ - ............ - ............ - ..@@........ - ..@@........ - ...@@....... - ...@@....... - ....@@...... - ....@@...... - .....@@..... - .....@@..... - ......@@.... - ......@@.... - .......@@... - .......@@... - ........@@.. - ........@@.. - ............ - ............ - ............ - ............ - ............ - -u+005d: -bracketright: - ............ - ............ - ............ - ............ - ...@@@@@.... - ......@@.... - ......@@.... - ......@@.... - ......@@.... - ......@@.... - ......@@.... - ......@@.... - ......@@.... - ......@@.... - ......@@.... - ......@@.... - ......@@.... - ......@@.... - ...@@@@@.... - ............ - ............ - ............ - ............ - ............ - -u+005e: -asciicircum: - ............ - ............ - .....@@..... - ....@@@@.... - ...@@..@@... - ..@@....@@.. - .@@......@@. - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - -u+005f: -underscore: - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - .@@@@@@@@@@. - ............ - ............ - ............ - -u+0060: -grave: - ............ - ...@@....... - ....@@...... - .....@@..... - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - -u+0061: -"a": - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ..@@@@@@@... - ........@@.. - .........@@. - .........@@. - ...@@@@@@@@. - ..@@.....@@. - .@@......@@. - .@@......@@. - .@@......@@. - ..@@.....@@. - ...@@@@@@@@. - ............ - ............ - ............ - ............ - ............ - -u+0062: -"b": - ............ - ............ - ............ - ............ - .@@......... - .@@......... - .@@......... - .@@......... - .@@@@@@@@... - .@@.....@@.. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@.....@@.. - .@@@@@@@@... - ............ - ............ - ............ - ............ - ............ - -u+0063: -"c": - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ...@@@@@@... - ..@@....@@.. - .@@......@@. - .@@......... - .@@......... - .@@......... - .@@......... - .@@......... - .@@......@@. - ..@@....@@.. - ...@@@@@@... - ............ - ............ - ............ - ............ - ............ - -u+0064: -"d": - ............ - ............ - ............ - ............ - .........@@. - .........@@. - .........@@. - .........@@. - ...@@@@@@@@. - ..@@.....@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - ..@@.....@@. - ...@@@@@@@@. - ............ - ............ - ............ - ............ - ............ - -u+0065: -"e": - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ...@@@@@@... - ..@@....@@.. - .@@......@@. - .@@......@@. - .@@......@@. - .@@@@@@@@@@. - .@@......... - .@@......... - .@@......... - ..@@.....@@. - ...@@@@@@@.. - ............ - ............ - ............ - ............ - ............ - -u+0066: -"f": - ............ - ............ - ............ - ............ - ......@@@@@. - .....@@..... - .....@@..... - .....@@..... - ..@@@@@@@@.. - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - ............ - ............ - ............ - ............ - ............ - -u+0067: -"g": - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ...@@@@@@@@. - ..@@.....@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - ..@@....@@@. - ...@@@@@@@@. - .........@@. - .........@@. - ........@@.. - ..@@@@@@@... - ............ - -u+0068: -"h": - ............ - ............ - ............ - ............ - .@@......... - .@@......... - .@@......... - .@@......... - .@@@@@@@@... - .@@.....@@.. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - ............ - ............ - ............ - ............ - ............ - -u+0069: -"i": - ............ - ............ - ............ - ............ - .....@@..... - .....@@..... - .....@@..... - ............ - ...@@@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - ...@@@@@@... - ............ - ............ - ............ - ............ - ............ - -u+006a: -"j": - ............ - ............ - ............ - ............ - ........@@.. - ........@@.. - ........@@.. - ............ - ......@@@@.. - ........@@.. - ........@@.. - ........@@.. - ........@@.. - ........@@.. - ........@@.. - ........@@.. - ........@@.. - ........@@.. - ........@@.. - ..@@....@@.. - ..@@....@@.. - ...@@..@@... - ....@@@@.... - ............ - -u+006b: -"k": - ............ - ............ - ............ - ............ - ..@@........ - ..@@........ - ..@@........ - ..@@........ - ..@@.....@@. - ..@@....@@.. - ..@@...@@... - ..@@..@@.... - ..@@.@@..... - ..@@@@...... - ..@@.@@..... - ..@@..@@.... - ..@@...@@... - ..@@....@@.. - ..@@.....@@. - ............ - ............ - ............ - ............ - ............ - -u+006c: -"l": - ............ - ............ - ............ - ............ - ...@@@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - ...@@@@@@... - ............ - ............ - ............ - ............ - ............ - -u+006d: -"m": - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - .@@@@@@@@... - .@@..@@.@@.. - .@@..@@..@@. - .@@..@@..@@. - .@@..@@..@@. - .@@..@@..@@. - .@@..@@..@@. - .@@..@@..@@. - .@@..@@..@@. - .@@..@@..@@. - .@@..@@..@@. - ............ - ............ - ............ - ............ - ............ - -u+006e: -"n": - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - .@@@@@@@@... - .@@.....@@.. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - ............ - ............ - ............ - ............ - ............ - -u+006f: -"o": - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ...@@@@@@... - ..@@....@@.. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - ..@@....@@.. - ...@@@@@@... - ............ - ............ - ............ - ............ - ............ - -u+0070: -"p": - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - .@@@@@@@@... - .@@.....@@.. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@.....@@.. - .@@@@@@@@... - .@@......... - .@@......... - .@@......... - .@@......... - ............ - -u+0071: -"q": - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ...@@@@@@@@. - ..@@.....@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - ..@@.....@@. - ...@@@@@@@@. - .........@@. - .........@@. - .........@@. - .........@@. - ............ - -u+0072: -"r": - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - .@@..@@@@@@. - .@@.@@...... - .@@@@....... - .@@@........ - .@@......... - .@@......... - .@@......... - .@@......... - .@@......... - .@@......... - .@@......... - ............ - ............ - ............ - ............ - ............ - -u+0073: -"s": - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ..@@@@@@@@.. - .@@......@@. - .@@......... - .@@......... - .@@......... - ..@@@@@@@@.. - .........@@. - .........@@. - .........@@. - .@@......@@. - ..@@@@@@@@.. - ............ - ............ - ............ - ............ - ............ - -u+0074: -"t": - ............ - ............ - ............ - ............ - ....@@...... - ....@@...... - ....@@...... - ....@@...... - .@@@@@@@@... - ....@@...... - ....@@...... - ....@@...... - ....@@...... - ....@@...... - ....@@...... - ....@@...... - ....@@...... - ....@@...... - .....@@@@@.. - ............ - ............ - ............ - ............ - ............ - -u+0075: -"u": - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - ..@@.....@@. - ...@@@@@@@@. - ............ - ............ - ............ - ............ - ............ - -u+0076: -"v": - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - .@@......@@. - .@@......@@. - .@@......@@. - ..@@....@@.. - ..@@....@@.. - ...@@..@@... - ...@@..@@... - ....@@@@.... - ....@@@@.... - .....@@..... - .....@@..... - ............ - ............ - ............ - ............ - ............ - -u+0077: -"w": - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@..@@..@@. - .@@..@@..@@. - .@@..@@..@@. - .@@..@@..@@. - .@@..@@..@@. - .@@..@@..@@. - ..@@@@@@@@.. - ............ - ............ - ............ - ............ - ............ - -u+0078: -"x": - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - .@@......@@. - .@@......@@. - ..@@....@@.. - ...@@..@@... - ....@@@@.... - .....@@..... - ....@@@@.... - ...@@..@@... - ..@@....@@.. - .@@......@@. - .@@......@@. - ............ - ............ - ............ - ............ - ............ - -u+0079: -"y": - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - .@@......@@. - ..@@....@@@. - ...@@@@@@@@. - .........@@. - .........@@. - ........@@.. - ..@@@@@@@... - ............ - -u+007a: -"z": - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - .@@@@@@@@@@. - .........@@. - ........@@.. - .......@@... - ......@@.... - .....@@..... - ....@@...... - ...@@....... - ..@@........ - .@@......... - .@@@@@@@@@@. - ............ - ............ - ............ - ............ - ............ - -u+007b: -braceleft: - ............ - ............ - ............ - ............ - ......@@@... - .....@@..... - ....@@...... - ....@@...... - ....@@...... - ....@@...... - ....@@...... - ..@@@....... - ....@@...... - ....@@...... - ....@@...... - ....@@...... - ....@@...... - .....@@..... - ......@@@... - ............ - ............ - ............ - ............ - ............ - -u+007c: -bar: - ............ - ............ - ............ - ............ - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - ............ - ............ - ............ - ............ - ............ - -u+007d: -braceright: - ............ - ............ - ............ - ............ - ..@@@....... - ....@@...... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - ......@@@... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - .....@@..... - ....@@...... - ..@@@....... - ............ - ............ - ............ - ............ - ............ - -u+007e: -asciitilde: - ............ - ............ - ..@@@@...@@. - .@@..@@..@@. - .@@..@@..@@. - .@@...@@@@.. - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - ............ - +name: Terminus Bold 12x24 +spacing: character-cell +cell-size: 12 24 +family: Terminus +foundry: xos4 +copyright: Copyright (C) 2020 Dimitar Toshkov Zhekov +notice: Licensed under the SIL Open Font License, Version 1.1 +point-size: 24 +weight: bold +slant: roman +setwidth: normal +dpi: 72 72 +average-width: 12 +ascent: 19 +descent: 5 +shift-up: -5 +encoding: iso10646-1 +default-char: u+fffd +min-word-space: 12 +converter: monobit v0.32 +source-name: ter-u24b.bdf +source-format: BDF v2.1 +history: load --format=bdf + +u+0000: +char0: + ............ + ............ + ............ + ............ + .@@@@..@@@@. + .@@......@@. + .@@......@@. + .@@......@@. + ............ + ............ + .@@......@@. + .@@......@@. + .@@......@@. + ............ + ............ + .@@......@@. + .@@......@@. + .@@......@@. + .@@@@..@@@@. + ............ + ............ + ............ + ............ + ............ + +u+0020: +space: + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + +u+0021: +exclam: + ............ + ............ + ............ + ............ + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + ............ + ............ + .....@@..... + .....@@..... + .....@@..... + ............ + ............ + ............ + ............ + ............ + +u+0022: +quotedbl: + ............ + ............ + ...@@..@@... + ...@@..@@... + ...@@..@@... + ...@@..@@... + ...@@..@@... + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + +u+0023: +numbersign: + ............ + ............ + ............ + ............ + ...@@..@@... + ...@@..@@... + ...@@..@@... + ...@@..@@... + .@@@@@@@@@@. + ...@@..@@... + ...@@..@@... + ...@@..@@... + ...@@..@@... + ...@@..@@... + .@@@@@@@@@@. + ...@@..@@... + ...@@..@@... + ...@@..@@... + ...@@..@@... + ............ + ............ + ............ + ............ + ............ + +u+0024: +dollar: + ............ + ............ + ............ + .....@@..... + .....@@..... + ...@@@@@@... + ..@@.@@.@@.. + .@@..@@..@@. + .@@..@@..... + .@@..@@..... + ..@@.@@..... + ...@@@@@@... + .....@@.@@.. + .....@@..@@. + .....@@..@@. + .@@..@@..@@. + ..@@.@@.@@.. + ...@@@@@@... + .....@@..... + .....@@..... + ............ + ............ + ............ + ............ + +u+0025: +percent: + ............ + ............ + ............ + ............ + ............ + ..@@@...@@.. + .@@.@@..@@.. + .@@.@@.@@... + ..@@@..@@... + ......@@.... + ......@@.... + .....@@..... + .....@@..... + ....@@...... + ....@@...... + ...@@..@@@.. + ...@@.@@.@@. + ..@@..@@.@@. + ..@@...@@@.. + ............ + ............ + ............ + ............ + ............ + +u+0026: +ampersand: + ............ + ............ + ............ + ............ + ....@@@..... + ...@@.@@.... + ..@@...@@... + ..@@...@@... + ..@@...@@... + ...@@.@@.... + ....@@@..... + ...@@@@..@@. + ..@@..@@.@@. + .@@....@@@.. + .@@.....@@.. + .@@.....@@.. + .@@....@@@.. + ..@@..@@.@@. + ...@@@@..@@. + ............ + ............ + ............ + ............ + ............ + +u+0027: +quotesingle: + ............ + ............ + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + +u+0028: +parenleft: + ............ + ............ + ............ + ............ + ......@@.... + .....@@..... + ....@@...... + ....@@...... + ...@@....... + ...@@....... + ...@@....... + ...@@....... + ...@@....... + ...@@....... + ...@@....... + ....@@...... + ....@@...... + .....@@..... + ......@@.... + ............ + ............ + ............ + ............ + ............ + +u+0029: +parenright: + ............ + ............ + ............ + ............ + ...@@....... + ....@@...... + .....@@..... + .....@@..... + ......@@.... + ......@@.... + ......@@.... + ......@@.... + ......@@.... + ......@@.... + ......@@.... + .....@@..... + .....@@..... + ....@@...... + ...@@....... + ............ + ............ + ............ + ............ + ............ + +u+002a: +asterisk: + ............ + ............ + ............ + ............ + ............ + ............ + ............ + .@@.....@@.. + ..@@...@@... + ...@@.@@.... + ....@@@..... + @@@@@@@@@@@. + ....@@@..... + ...@@.@@.... + ..@@...@@... + .@@.....@@.. + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + +u+002b: +plus: + ............ + ............ + ............ + ............ + ............ + ............ + ............ + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .@@@@@@@@@@. + .....@@..... + .....@@..... + .....@@..... + .....@@..... + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + +u+002c: +comma: + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + .....@@..... + .....@@..... + .....@@..... + .....@@..... + ....@@...... + ............ + ............ + ............ + ............ + +u+002d: +hyphen: + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + .@@@@@@@@@@. + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + +u+002e: +period: + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + .....@@..... + .....@@..... + .....@@..... + ............ + ............ + ............ + ............ + ............ + +u+002f: +slash: + ............ + ............ + ............ + ............ + ............ + ........@@.. + ........@@.. + .......@@... + .......@@... + ......@@.... + ......@@.... + .....@@..... + .....@@..... + ....@@...... + ....@@...... + ...@@....... + ...@@....... + ..@@........ + ..@@........ + ............ + ............ + ............ + ............ + ............ + +u+0030: +zero: + ............ + ............ + ............ + ............ + ...@@@@@@... + ..@@....@@.. + .@@......@@. + .@@......@@. + .@@.....@@@. + .@@....@@@@. + .@@...@@.@@. + .@@..@@..@@. + .@@.@@...@@. + .@@@@....@@. + .@@@.....@@. + .@@......@@. + .@@......@@. + ..@@....@@.. + ...@@@@@@... + ............ + ............ + ............ + ............ + ............ + +u+0031: +one: + ............ + ............ + ............ + ............ + .....@@..... + ....@@@..... + ...@@@@..... + ..@@.@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + ..@@@@@@@@.. + ............ + ............ + ............ + ............ + ............ + +u+0032: +two: + ............ + ............ + ............ + ............ + ...@@@@@@... + ..@@....@@.. + .@@......@@. + .@@......@@. + .@@......@@. + .........@@. + ........@@.. + .......@@... + ......@@.... + .....@@..... + ....@@...... + ...@@....... + ..@@........ + .@@......... + .@@@@@@@@@@. + ............ + ............ + ............ + ............ + ............ + +u+0033: +three: + ............ + ............ + ............ + ............ + ...@@@@@@... + ..@@....@@.. + .@@......@@. + .........@@. + .........@@. + .........@@. + ........@@.. + ....@@@@@... + ........@@.. + .........@@. + .........@@. + .........@@. + .@@......@@. + ..@@....@@.. + ...@@@@@@... + ............ + ............ + ............ + ............ + ............ + +u+0034: +four: + ............ + ............ + ............ + ............ + .........@@. + ........@@@. + .......@@@@. + ......@@.@@. + .....@@..@@. + ....@@...@@. + ...@@....@@. + ..@@.....@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@@@@@@@@@. + .........@@. + .........@@. + .........@@. + ............ + ............ + ............ + ............ + ............ + +u+0035: +five: + ............ + ............ + ............ + ............ + .@@@@@@@@@@. + .@@......... + .@@......... + .@@......... + .@@......... + .@@......... + .@@@@@@@@... + ........@@.. + .........@@. + .........@@. + .........@@. + .........@@. + .@@......@@. + ..@@....@@.. + ...@@@@@@... + ............ + ............ + ............ + ............ + ............ + +u+0036: +six: + ............ + ............ + ............ + ............ + ...@@@@@@@.. + ..@@........ + .@@......... + .@@......... + .@@......... + .@@......... + .@@@@@@@@... + .@@.....@@.. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + ..@@....@@.. + ...@@@@@@... + ............ + ............ + ............ + ............ + ............ + +u+0037: +seven: + ............ + ............ + ............ + ............ + .@@@@@@@@@@. + .@@......@@. + .@@......@@. + .........@@. + ........@@.. + ........@@.. + .......@@... + .......@@... + ......@@.... + ......@@.... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + ............ + ............ + ............ + ............ + ............ + +u+0038: +eight: + ............ + ............ + ............ + ............ + ...@@@@@@... + ..@@....@@.. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + ..@@....@@.. + ...@@@@@@... + ..@@....@@.. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + ..@@....@@.. + ...@@@@@@... + ............ + ............ + ............ + ............ + ............ + +u+0039: +nine: + ............ + ............ + ............ + ............ + ...@@@@@@... + ..@@....@@.. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + ..@@.....@@. + ...@@@@@@@@. + .........@@. + .........@@. + .........@@. + .........@@. + ........@@.. + ..@@@@@@@... + ............ + ............ + ............ + ............ + ............ + +u+003a: +colon: + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + .....@@..... + .....@@..... + .....@@..... + ............ + ............ + ............ + ............ + .....@@..... + .....@@..... + .....@@..... + ............ + ............ + ............ + ............ + ............ + ............ + +u+003b: +semicolon: + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + .....@@..... + .....@@..... + .....@@..... + ............ + ............ + ............ + ............ + .....@@..... + .....@@..... + .....@@..... + .....@@..... + ....@@...... + ............ + ............ + ............ + ............ + +u+003c: +less: + ............ + ............ + ............ + ............ + ........@@.. + .......@@... + ......@@.... + .....@@..... + ....@@...... + ...@@....... + ..@@........ + .@@......... + ..@@........ + ...@@....... + ....@@...... + .....@@..... + ......@@.... + .......@@... + ........@@.. + ............ + ............ + ............ + ............ + ............ + +u+003d: +equal: + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + .@@@@@@@@@@. + ............ + ............ + ............ + ............ + .@@@@@@@@@@. + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + +u+003e: +greater: + ............ + ............ + ............ + ............ + .@@......... + ..@@........ + ...@@....... + ....@@...... + .....@@..... + ......@@.... + .......@@... + ........@@.. + .......@@... + ......@@.... + .....@@..... + ....@@...... + ...@@....... + ..@@........ + .@@......... + ............ + ............ + ............ + ............ + ............ + +u+003f: +question: + ............ + ............ + ............ + ............ + ...@@@@@@... + ..@@....@@.. + .@@......@@. + .@@......@@. + .@@......@@. + ........@@.. + .......@@... + ......@@.... + .....@@..... + .....@@..... + ............ + ............ + .....@@..... + .....@@..... + .....@@..... + ............ + ............ + ............ + ............ + ............ + +u+0040: +at: + ............ + ............ + ............ + ............ + ..@@@@@@@... + .@@.....@@.. + @@.......@@. + @@....@@@@@. + @@...@@..@@. + @@..@@...@@. + @@..@@...@@. + @@..@@...@@. + @@..@@...@@. + @@..@@...@@. + @@...@@..@@. + @@....@@@@@. + @@.......... + .@@......... + ..@@@@@@@@@. + ............ + ............ + ............ + ............ + ............ + +u+0041: +"A": + ............ + ............ + ............ + ............ + ...@@@@@@... + ..@@....@@.. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@@@@@@@@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + ............ + ............ + ............ + ............ + ............ + +u+0042: +"B": + ............ + ............ + ............ + ............ + .@@@@@@@@... + .@@.....@@.. + .@@......@@. + .@@......@@. + .@@......@@. + .@@.....@@.. + .@@@@@@@@... + .@@.....@@.. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@.....@@.. + .@@@@@@@@... + ............ + ............ + ............ + ............ + ............ + +u+0043: +"C": + ............ + ............ + ............ + ............ + ...@@@@@@... + ..@@....@@.. + .@@......@@. + .@@......@@. + .@@......... + .@@......... + .@@......... + .@@......... + .@@......... + .@@......... + .@@......... + .@@......@@. + .@@......@@. + ..@@....@@.. + ...@@@@@@... + ............ + ............ + ............ + ............ + ............ + +u+0044: +"D": + ............ + ............ + ............ + ............ + .@@@@@@@@... + .@@.....@@.. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@.....@@.. + .@@@@@@@@... + ............ + ............ + ............ + ............ + ............ + +u+0045: +"E": + ............ + ............ + ............ + ............ + .@@@@@@@@@@. + .@@......... + .@@......... + .@@......... + .@@......... + .@@......... + .@@......... + .@@@@@@@@... + .@@......... + .@@......... + .@@......... + .@@......... + .@@......... + .@@......... + .@@@@@@@@@@. + ............ + ............ + ............ + ............ + ............ + +u+0046: +"F": + ............ + ............ + ............ + ............ + .@@@@@@@@@@. + .@@......... + .@@......... + .@@......... + .@@......... + .@@......... + .@@......... + .@@@@@@@@... + .@@......... + .@@......... + .@@......... + .@@......... + .@@......... + .@@......... + .@@......... + ............ + ............ + ............ + ............ + ............ + +u+0047: +"G": + ............ + ............ + ............ + ............ + ...@@@@@@... + ..@@....@@.. + .@@......@@. + .@@......@@. + .@@......... + .@@......... + .@@......... + .@@...@@@@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + ..@@....@@.. + ...@@@@@@... + ............ + ............ + ............ + ............ + ............ + +u+0048: +"H": + ............ + ............ + ............ + ............ + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@@@@@@@@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + ............ + ............ + ............ + ............ + ............ + +u+0049: +"I": + ............ + ............ + ............ + ............ + ...@@@@@@... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + ...@@@@@@... + ............ + ............ + ............ + ............ + ............ + +u+004a: +"J": + ............ + ............ + ............ + ............ + ......@@@@@@ + ........@@.. + ........@@.. + ........@@.. + ........@@.. + ........@@.. + ........@@.. + ........@@.. + ........@@.. + ........@@.. + .@@.....@@.. + .@@.....@@.. + .@@.....@@.. + ..@@...@@... + ...@@@@@.... + ............ + ............ + ............ + ............ + ............ + +u+004b: +"K": + ............ + ............ + ............ + ............ + .@@......@@. + .@@.....@@.. + .@@....@@... + .@@...@@.... + .@@..@@..... + .@@.@@...... + .@@@@....... + .@@@........ + .@@@@....... + .@@.@@...... + .@@..@@..... + .@@...@@.... + .@@....@@... + .@@.....@@.. + .@@......@@. + ............ + ............ + ............ + ............ + ............ + +u+004c: +"L": + ............ + ............ + ............ + ............ + .@@......... + .@@......... + .@@......... + .@@......... + .@@......... + .@@......... + .@@......... + .@@......... + .@@......... + .@@......... + .@@......... + .@@......... + .@@......... + .@@......... + .@@@@@@@@@@. + ............ + ............ + ............ + ............ + ............ + +u+004d: +"M": + ............ + ............ + ............ + ............ + @.........@. + @@.......@@. + @@@.....@@@. + @@@@...@@@@. + @@.@@.@@.@@. + @@..@@@..@@. + @@...@...@@. + @@.......@@. + @@.......@@. + @@.......@@. + @@.......@@. + @@.......@@. + @@.......@@. + @@.......@@. + @@.......@@. + ............ + ............ + ............ + ............ + ............ + +u+004e: +"N": + ............ + ............ + ............ + ............ + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@@.....@@. + .@@@@....@@. + .@@.@@...@@. + .@@..@@..@@. + .@@...@@.@@. + .@@....@@@@. + .@@.....@@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + ............ + ............ + ............ + ............ + ............ + +u+004f: +"O": + ............ + ............ + ............ + ............ + ...@@@@@@... + ..@@....@@.. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + ..@@....@@.. + ...@@@@@@... + ............ + ............ + ............ + ............ + ............ + +u+0050: +"P": + ............ + ............ + ............ + ............ + .@@@@@@@@... + .@@.....@@.. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@.....@@.. + .@@@@@@@@... + .@@......... + .@@......... + .@@......... + .@@......... + .@@......... + .@@......... + .@@......... + ............ + ............ + ............ + ............ + ............ + +u+0051: +"Q": + ............ + ............ + ............ + ............ + ...@@@@@@... + ..@@....@@.. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@..@@..@@. + ..@@..@@@@.. + ...@@@@@@... + ........@@.. + .........@@. + ............ + ............ + ............ + +u+0052: +"R": + ............ + ............ + ............ + ............ + .@@@@@@@@... + .@@.....@@.. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@.....@@.. + .@@@@@@@@... + .@@@@....... + .@@.@@...... + .@@..@@..... + .@@...@@.... + .@@....@@... + .@@.....@@.. + .@@......@@. + ............ + ............ + ............ + ............ + ............ + +u+0053: +"S": + ............ + ............ + ............ + ............ + ...@@@@@@... + ..@@....@@.. + .@@......@@. + .@@......... + .@@......... + .@@......... + ..@@........ + ...@@@@@@... + ........@@.. + .........@@. + .........@@. + .........@@. + .@@......@@. + ..@@....@@.. + ...@@@@@@... + ............ + ............ + ............ + ............ + ............ + +u+0054: +"T": + ............ + ............ + ............ + ............ + .@@@@@@@@@@. + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + ............ + ............ + ............ + ............ + ............ + +u+0055: +"U": + ............ + ............ + ............ + ............ + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + ..@@....@@.. + ...@@@@@@... + ............ + ............ + ............ + ............ + ............ + +u+0056: +"V": + ............ + ............ + ............ + ............ + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + ..@@....@@.. + ..@@....@@.. + ..@@....@@.. + ..@@....@@.. + ...@@..@@... + ...@@..@@... + ...@@..@@... + ....@@@@.... + ....@@@@.... + .....@@..... + .....@@..... + ............ + ............ + ............ + ............ + ............ + +u+0057: +"W": + ............ + ............ + ............ + ............ + @@.......@@. + @@.......@@. + @@.......@@. + @@.......@@. + @@.......@@. + @@.......@@. + @@.......@@. + @@.......@@. + @@...@...@@. + @@..@@@..@@. + @@.@@.@@.@@. + @@@@...@@@@. + @@@.....@@@. + @@.......@@. + @.........@. + ............ + ............ + ............ + ............ + ............ + +u+0058: +"X": + ............ + ............ + ............ + ............ + .@@......@@. + .@@......@@. + ..@@....@@.. + ..@@....@@.. + ...@@..@@... + ...@@..@@... + ....@@@@.... + .....@@..... + ....@@@@.... + ...@@..@@... + ...@@..@@... + ..@@....@@.. + ..@@....@@.. + .@@......@@. + .@@......@@. + ............ + ............ + ............ + ............ + ............ + +u+0059: +"Y": + ............ + ............ + ............ + ............ + .@@......@@. + .@@......@@. + ..@@....@@.. + ..@@....@@.. + ...@@..@@... + ...@@..@@... + ....@@@@.... + ....@@@@.... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + ............ + ............ + ............ + ............ + ............ + +u+005a: +"Z": + ............ + ............ + ............ + ............ + .@@@@@@@@@@. + .........@@. + .........@@. + .........@@. + ........@@.. + .......@@... + ......@@.... + .....@@..... + ....@@...... + ...@@....... + ..@@........ + .@@......... + .@@......... + .@@......... + .@@@@@@@@@@. + ............ + ............ + ............ + ............ + ............ + +u+005b: +bracketleft: + ............ + ............ + ............ + ............ + ...@@@@@.... + ...@@....... + ...@@....... + ...@@....... + ...@@....... + ...@@....... + ...@@....... + ...@@....... + ...@@....... + ...@@....... + ...@@....... + ...@@....... + ...@@....... + ...@@....... + ...@@@@@.... + ............ + ............ + ............ + ............ + ............ + +u+005c: +backslash: + ............ + ............ + ............ + ............ + ............ + ..@@........ + ..@@........ + ...@@....... + ...@@....... + ....@@...... + ....@@...... + .....@@..... + .....@@..... + ......@@.... + ......@@.... + .......@@... + .......@@... + ........@@.. + ........@@.. + ............ + ............ + ............ + ............ + ............ + +u+005d: +bracketright: + ............ + ............ + ............ + ............ + ...@@@@@.... + ......@@.... + ......@@.... + ......@@.... + ......@@.... + ......@@.... + ......@@.... + ......@@.... + ......@@.... + ......@@.... + ......@@.... + ......@@.... + ......@@.... + ......@@.... + ...@@@@@.... + ............ + ............ + ............ + ............ + ............ + +u+005e: +asciicircum: + ............ + ............ + .....@@..... + ....@@@@.... + ...@@..@@... + ..@@....@@.. + .@@......@@. + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + +u+005f: +underscore: + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + .@@@@@@@@@@. + ............ + ............ + ............ + +u+0060: +grave: + ............ + ...@@....... + ....@@...... + .....@@..... + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + +u+0061: +"a": + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ..@@@@@@@... + ........@@.. + .........@@. + .........@@. + ...@@@@@@@@. + ..@@.....@@. + .@@......@@. + .@@......@@. + .@@......@@. + ..@@.....@@. + ...@@@@@@@@. + ............ + ............ + ............ + ............ + ............ + +u+0062: +"b": + ............ + ............ + ............ + ............ + .@@......... + .@@......... + .@@......... + .@@......... + .@@@@@@@@... + .@@.....@@.. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@.....@@.. + .@@@@@@@@... + ............ + ............ + ............ + ............ + ............ + +u+0063: +"c": + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ...@@@@@@... + ..@@....@@.. + .@@......@@. + .@@......... + .@@......... + .@@......... + .@@......... + .@@......... + .@@......@@. + ..@@....@@.. + ...@@@@@@... + ............ + ............ + ............ + ............ + ............ + +u+0064: +"d": + ............ + ............ + ............ + ............ + .........@@. + .........@@. + .........@@. + .........@@. + ...@@@@@@@@. + ..@@.....@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + ..@@.....@@. + ...@@@@@@@@. + ............ + ............ + ............ + ............ + ............ + +u+0065: +"e": + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ...@@@@@@... + ..@@....@@.. + .@@......@@. + .@@......@@. + .@@......@@. + .@@@@@@@@@@. + .@@......... + .@@......... + .@@......... + ..@@.....@@. + ...@@@@@@@.. + ............ + ............ + ............ + ............ + ............ + +u+0066: +"f": + ............ + ............ + ............ + ............ + ......@@@@@. + .....@@..... + .....@@..... + .....@@..... + ..@@@@@@@@.. + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + ............ + ............ + ............ + ............ + ............ + +u+0067: +"g": + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ...@@@@@@@@. + ..@@.....@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + ..@@....@@@. + ...@@@@@@@@. + .........@@. + .........@@. + ........@@.. + ..@@@@@@@... + ............ + +u+0068: +"h": + ............ + ............ + ............ + ............ + .@@......... + .@@......... + .@@......... + .@@......... + .@@@@@@@@... + .@@.....@@.. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + ............ + ............ + ............ + ............ + ............ + +u+0069: +"i": + ............ + ............ + ............ + ............ + .....@@..... + .....@@..... + .....@@..... + ............ + ...@@@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + ...@@@@@@... + ............ + ............ + ............ + ............ + ............ + +u+006a: +"j": + ............ + ............ + ............ + ............ + ........@@.. + ........@@.. + ........@@.. + ............ + ......@@@@.. + ........@@.. + ........@@.. + ........@@.. + ........@@.. + ........@@.. + ........@@.. + ........@@.. + ........@@.. + ........@@.. + ........@@.. + ..@@....@@.. + ..@@....@@.. + ...@@..@@... + ....@@@@.... + ............ + +u+006b: +"k": + ............ + ............ + ............ + ............ + ..@@........ + ..@@........ + ..@@........ + ..@@........ + ..@@.....@@. + ..@@....@@.. + ..@@...@@... + ..@@..@@.... + ..@@.@@..... + ..@@@@...... + ..@@.@@..... + ..@@..@@.... + ..@@...@@... + ..@@....@@.. + ..@@.....@@. + ............ + ............ + ............ + ............ + ............ + +u+006c: +"l": + ............ + ............ + ............ + ............ + ...@@@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + ...@@@@@@... + ............ + ............ + ............ + ............ + ............ + +u+006d: +"m": + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + .@@@@@@@@... + .@@..@@.@@.. + .@@..@@..@@. + .@@..@@..@@. + .@@..@@..@@. + .@@..@@..@@. + .@@..@@..@@. + .@@..@@..@@. + .@@..@@..@@. + .@@..@@..@@. + .@@..@@..@@. + ............ + ............ + ............ + ............ + ............ + +u+006e: +"n": + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + .@@@@@@@@... + .@@.....@@.. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + ............ + ............ + ............ + ............ + ............ + +u+006f: +"o": + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ...@@@@@@... + ..@@....@@.. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + ..@@....@@.. + ...@@@@@@... + ............ + ............ + ............ + ............ + ............ + +u+0070: +"p": + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + .@@@@@@@@... + .@@.....@@.. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@.....@@.. + .@@@@@@@@... + .@@......... + .@@......... + .@@......... + .@@......... + ............ + +u+0071: +"q": + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ...@@@@@@@@. + ..@@.....@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + ..@@.....@@. + ...@@@@@@@@. + .........@@. + .........@@. + .........@@. + .........@@. + ............ + +u+0072: +"r": + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + .@@..@@@@@@. + .@@.@@...... + .@@@@....... + .@@@........ + .@@......... + .@@......... + .@@......... + .@@......... + .@@......... + .@@......... + .@@......... + ............ + ............ + ............ + ............ + ............ + +u+0073: +"s": + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ..@@@@@@@@.. + .@@......@@. + .@@......... + .@@......... + .@@......... + ..@@@@@@@@.. + .........@@. + .........@@. + .........@@. + .@@......@@. + ..@@@@@@@@.. + ............ + ............ + ............ + ............ + ............ + +u+0074: +"t": + ............ + ............ + ............ + ............ + ....@@...... + ....@@...... + ....@@...... + ....@@...... + .@@@@@@@@... + ....@@...... + ....@@...... + ....@@...... + ....@@...... + ....@@...... + ....@@...... + ....@@...... + ....@@...... + ....@@...... + .....@@@@@.. + ............ + ............ + ............ + ............ + ............ + +u+0075: +"u": + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + ..@@.....@@. + ...@@@@@@@@. + ............ + ............ + ............ + ............ + ............ + +u+0076: +"v": + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + .@@......@@. + .@@......@@. + .@@......@@. + ..@@....@@.. + ..@@....@@.. + ...@@..@@... + ...@@..@@... + ....@@@@.... + ....@@@@.... + .....@@..... + .....@@..... + ............ + ............ + ............ + ............ + ............ + +u+0077: +"w": + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@..@@..@@. + .@@..@@..@@. + .@@..@@..@@. + .@@..@@..@@. + .@@..@@..@@. + .@@..@@..@@. + ..@@@@@@@@.. + ............ + ............ + ............ + ............ + ............ + +u+0078: +"x": + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + .@@......@@. + .@@......@@. + ..@@....@@.. + ...@@..@@... + ....@@@@.... + .....@@..... + ....@@@@.... + ...@@..@@... + ..@@....@@.. + .@@......@@. + .@@......@@. + ............ + ............ + ............ + ............ + ............ + +u+0079: +"y": + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + .@@......@@. + ..@@....@@@. + ...@@@@@@@@. + .........@@. + .........@@. + ........@@.. + ..@@@@@@@... + ............ + +u+007a: +"z": + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + .@@@@@@@@@@. + .........@@. + ........@@.. + .......@@... + ......@@.... + .....@@..... + ....@@...... + ...@@....... + ..@@........ + .@@......... + .@@@@@@@@@@. + ............ + ............ + ............ + ............ + ............ + +u+007b: +braceleft: + ............ + ............ + ............ + ............ + ......@@@... + .....@@..... + ....@@...... + ....@@...... + ....@@...... + ....@@...... + ....@@...... + ..@@@....... + ....@@...... + ....@@...... + ....@@...... + ....@@...... + ....@@...... + .....@@..... + ......@@@... + ............ + ............ + ............ + ............ + ............ + +u+007c: +bar: + ............ + ............ + ............ + ............ + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + ............ + ............ + ............ + ............ + ............ + +u+007d: +braceright: + ............ + ............ + ............ + ............ + ..@@@....... + ....@@...... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + ......@@@... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + .....@@..... + ....@@...... + ..@@@....... + ............ + ............ + ............ + ............ + ............ + +u+007e: +asciitilde: + ............ + ............ + ..@@@@...@@. + .@@..@@..@@. + .@@..@@..@@. + .@@...@@@@.. + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + +u+00a0: +nbspace: + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ + ............ diff --git a/containers/test/fonts/terminus-ascii-bold-28px.yaff b/containers/test/fonts/terminus-28px.yaff similarity index 94% rename from containers/test/fonts/terminus-ascii-bold-28px.yaff rename to containers/test/fonts/terminus-28px.yaff index da8541115..bc66ee66c 100644 --- a/containers/test/fonts/terminus-ascii-bold-28px.yaff +++ b/containers/test/fonts/terminus-28px.yaff @@ -1,3000 +1,3030 @@ -name: Terminus Bold 14x28 -spacing: character-cell -cell-size: 14 28 -family: Terminus -foundry: xos4 -copyright: Copyright (C) 2020 Dimitar Toshkov Zhekov -notice: Licensed under the SIL Open Font License, Version 1.1 -point-size: 28 -weight: bold -slant: roman -setwidth: normal -dpi: 72 72 -average-width: 14 -ascent: 22 -descent: 6 -shift-up: -6 -encoding: iso10646-1 -default-char: u+fffd -min-word-space: 14 -converter: monobit v0.32 -source-name: ter-u28b.bdf -source-format: BDF v2.1 -history: load --format=bdf - -u+0000: -char0: - .............. - .............. - .............. - .............. - .@@@@...@@@@.. - .@@@@...@@@@.. - .@@.......@@.. - .@@.......@@.. - .............. - .............. - .............. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .............. - .............. - .............. - .@@.......@@.. - .@@.......@@.. - .@@@@...@@@@.. - .@@@@...@@@@.. - .............. - .............. - .............. - .............. - .............. - .............. - -u+0020: -space: - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - -u+0021: -exclam: - .............. - .............. - .............. - .............. - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - .............. - .............. - ......@@...... - ......@@...... - ......@@...... - ......@@...... - .............. - .............. - .............. - .............. - .............. - .............. - -u+0022: -quotedbl: - .............. - .............. - ...@@...@@.... - ...@@...@@.... - ...@@...@@.... - ...@@...@@.... - ...@@...@@.... - ...@@...@@.... - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - -u+0023: -numbersign: - .............. - .............. - .............. - .............. - ...@@...@@.... - ...@@...@@.... - ...@@...@@.... - ...@@...@@.... - .@@@@@@@@@@@.. - .@@@@@@@@@@@.. - ...@@...@@.... - ...@@...@@.... - ...@@...@@.... - ...@@...@@.... - ...@@...@@.... - ...@@...@@.... - .@@@@@@@@@@@.. - .@@@@@@@@@@@.. - ...@@...@@.... - ...@@...@@.... - ...@@...@@.... - ...@@...@@.... - .............. - .............. - .............. - .............. - .............. - .............. - -u+0024: -dollar: - .............. - .............. - ......@@...... - ......@@...... - ......@@...... - ...@@@@@@@@... - ..@@@@@@@@@@.. - .@@@..@@..@@@. - .@@...@@...@@. - .@@...@@...... - .@@...@@...... - .@@@..@@...... - ..@@@@@@@@@... - ...@@@@@@@@@.. - ......@@..@@@. - ......@@...@@. - ......@@...@@. - .@@...@@...@@. - .@@@..@@..@@@. - ..@@@@@@@@@@.. - ...@@@@@@@@... - ......@@...... - ......@@...... - ......@@...... - .............. - .............. - .............. - .............. - -u+0025: -percent: - .............. - .............. - .............. - .............. - ..@@@@....@@.. - .@@@@@@...@@.. - .@@..@@..@@... - .@@@@@@..@@... - ..@@@@..@@.... - ........@@.... - .......@@..... - .......@@..... - ......@@...... - ......@@...... - .....@@....... - .....@@....... - ....@@........ - ....@@..@@@@.. - ...@@..@@@@@@. - ...@@..@@..@@. - ..@@...@@@@@@. - ..@@....@@@@.. - .............. - .............. - .............. - .............. - .............. - .............. - -u+0026: -ampersand: - .............. - .............. - .............. - .............. - ....@@@@...... - ...@@@@@@..... - ..@@@..@@@.... - ..@@....@@.... - ..@@....@@.... - ..@@@..@@@.... - ...@@@@@@..... - ....@@@@...... - ....@@@....... - ...@@@@@...@@. - ..@@@.@@@.@@@. - .@@@...@@@@@.. - .@@.....@@@... - .@@.....@@@... - .@@.....@@@... - .@@@...@@@@@.. - ..@@@@@@@.@@@. - ...@@@@@...@@. - .............. - .............. - .............. - .............. - .............. - .............. - -u+0027: -quotesingle: - .............. - .............. - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - -u+0028: -parenleft: - .............. - .............. - .............. - .............. - .......@@..... - ......@@...... - .....@@....... - .....@@....... - ....@@........ - ....@@........ - ....@@........ - ....@@........ - ....@@........ - ....@@........ - ....@@........ - ....@@........ - ....@@........ - ....@@........ - .....@@....... - .....@@....... - ......@@...... - .......@@..... - .............. - .............. - .............. - .............. - .............. - .............. - -u+0029: -parenright: - .............. - .............. - .............. - .............. - ....@@........ - .....@@....... - ......@@...... - ......@@...... - .......@@..... - .......@@..... - .......@@..... - .......@@..... - .......@@..... - .......@@..... - .......@@..... - .......@@..... - .......@@..... - .......@@..... - ......@@...... - ......@@...... - .....@@....... - ....@@........ - .............. - .............. - .............. - .............. - .............. - .............. - -u+002a: -asterisk: - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - ..@@@...@@@... - ...@@@.@@@.... - ....@@@@@..... - .....@@@...... - .@@@@@@@@@@@.. - .@@@@@@@@@@@.. - .....@@@...... - ....@@@@@..... - ...@@@.@@@.... - ..@@@...@@@... - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - -u+002b: -plus: - .............. - .............. - .............. - .............. - .............. - .............. - .............. - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - .@@@@@@@@@@@@. - .@@@@@@@@@@@@. - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - -u+002c: -comma: - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - ......@@...... - ......@@...... - ......@@...... - ......@@...... - .....@@....... - ....@@........ - .............. - .............. - .............. - .............. - -u+002d: -hyphen: - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .@@@@@@@@@@@.. - .@@@@@@@@@@@.. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - -u+002e: -period: - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - ......@@...... - ......@@...... - ......@@...... - ......@@...... - .............. - .............. - .............. - .............. - .............. - .............. - -u+002f: -slash: - .............. - .............. - .............. - .............. - ..........@@.. - ..........@@.. - .........@@... - .........@@... - ........@@.... - ........@@.... - .......@@..... - .......@@..... - ......@@...... - ......@@...... - .....@@....... - .....@@....... - ....@@........ - ....@@........ - ...@@......... - ...@@......... - ..@@.......... - ..@@.......... - .............. - .............. - .............. - .............. - .............. - .............. - -u+0030: -zero: - .............. - .............. - .............. - .............. - ...@@@@@@@.... - ..@@@@@@@@@... - .@@@.....@@@.. - .@@.......@@.. - .@@......@@@.. - .@@.....@@@@.. - .@@....@@@@@.. - .@@...@@@.@@.. - .@@..@@@..@@.. - .@@.@@@...@@.. - .@@@@@....@@.. - .@@@@.....@@.. - .@@@......@@.. - .@@.......@@.. - .@@.......@@.. - .@@@.....@@@.. - ..@@@@@@@@@... - ...@@@@@@@.... - .............. - .............. - .............. - .............. - .............. - .............. - -u+0031: -one: - .............. - .............. - .............. - .............. - ......@@...... - .....@@@...... - ....@@@@...... - ...@@@@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ...@@@@@@@@... - ...@@@@@@@@... - .............. - .............. - .............. - .............. - .............. - .............. - -u+0032: -two: - .............. - .............. - .............. - .............. - ...@@@@@@@.... - ..@@@@@@@@@... - .@@@.....@@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - ..........@@.. - .........@@@.. - ........@@@... - .......@@@.... - ......@@@..... - .....@@@...... - ....@@@....... - ...@@@........ - ..@@@......... - .@@@.......... - .@@@@@@@@@@@.. - .@@@@@@@@@@@.. - .............. - .............. - .............. - .............. - .............. - .............. - -u+0033: -three: - .............. - .............. - .............. - .............. - ...@@@@@@@.... - ..@@@@@@@@@... - .@@@.....@@@.. - .@@.......@@.. - ..........@@.. - ..........@@.. - ..........@@.. - .........@@@.. - ....@@@@@@@... - ....@@@@@@@... - .........@@@.. - ..........@@.. - ..........@@.. - ..........@@.. - .@@.......@@.. - .@@@.....@@@.. - ..@@@@@@@@@... - ...@@@@@@@.... - .............. - .............. - .............. - .............. - .............. - .............. - -u+0034: -four: - .............. - .............. - .............. - .............. - ..........@@.. - .........@@@.. - ........@@@@.. - .......@@@@@.. - ......@@@.@@.. - .....@@@..@@.. - ....@@@...@@.. - ...@@@....@@.. - ..@@@.....@@.. - .@@@......@@.. - .@@.......@@.. - .@@.......@@.. - .@@@@@@@@@@@.. - .@@@@@@@@@@@.. - ..........@@.. - ..........@@.. - ..........@@.. - ..........@@.. - .............. - .............. - .............. - .............. - .............. - .............. - -u+0035: -five: - .............. - .............. - .............. - .............. - .@@@@@@@@@@@.. - .@@@@@@@@@@@.. - .@@........... - .@@........... - .@@........... - .@@........... - .@@........... - .@@@@@@@@@.... - .@@@@@@@@@@... - .........@@@.. - ..........@@.. - ..........@@.. - ..........@@.. - ..........@@.. - .@@.......@@.. - .@@@.....@@@.. - ..@@@@@@@@@... - ...@@@@@@@.... - .............. - .............. - .............. - .............. - .............. - .............. - -u+0036: -six: - .............. - .............. - .............. - .............. - ...@@@@@@@@... - ..@@@@@@@@@... - .@@@.......... - .@@........... - .@@........... - .@@........... - .@@........... - .@@@@@@@@@.... - .@@@@@@@@@@... - .@@......@@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@@.....@@@.. - ..@@@@@@@@@... - ...@@@@@@@.... - .............. - .............. - .............. - .............. - .............. - .............. - -u+0037: -seven: - .............. - .............. - .............. - .............. - .@@@@@@@@@@@.. - .@@@@@@@@@@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .........@@... - .........@@... - ........@@.... - ........@@.... - .......@@..... - .......@@..... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - .............. - .............. - .............. - .............. - .............. - .............. - -u+0038: -eight: - .............. - .............. - .............. - .............. - ...@@@@@@@.... - ..@@@@@@@@@... - .@@@.....@@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@@.....@@@.. - ..@@@@@@@@@... - ..@@@@@@@@@... - .@@@.....@@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@@.....@@@.. - ..@@@@@@@@@... - ...@@@@@@@.... - .............. - .............. - .............. - .............. - .............. - .............. - -u+0039: -nine: - .............. - .............. - .............. - .............. - ...@@@@@@@.... - ..@@@@@@@@@... - .@@@.....@@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@@......@@.. - ..@@@@@@@@@@.. - ...@@@@@@@@@.. - ..........@@.. - ..........@@.. - ..........@@.. - ..........@@.. - .........@@@.. - ..@@@@@@@@@... - ..@@@@@@@@.... - .............. - .............. - .............. - .............. - .............. - .............. - -u+003a: -colon: - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - ......@@...... - ......@@...... - ......@@...... - ......@@...... - .............. - .............. - .............. - .............. - .............. - ......@@...... - ......@@...... - ......@@...... - ......@@...... - .............. - .............. - .............. - .............. - .............. - .............. - -u+003b: -semicolon: - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - ......@@...... - ......@@...... - ......@@...... - ......@@...... - .............. - .............. - .............. - .............. - .............. - ......@@...... - ......@@...... - ......@@...... - ......@@...... - .....@@....... - ....@@........ - .............. - .............. - .............. - .............. - -u+003c: -less: - .............. - .............. - .............. - .............. - .............. - .........@@@.. - ........@@@... - .......@@@.... - ......@@@..... - .....@@@...... - ....@@@....... - ...@@@........ - ..@@@......... - .@@@.......... - ..@@@......... - ...@@@........ - ....@@@....... - .....@@@...... - ......@@@..... - .......@@@.... - ........@@@... - .........@@@.. - .............. - .............. - .............. - .............. - .............. - .............. - -u+003d: -equal: - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .@@@@@@@@@@@.. - .@@@@@@@@@@@.. - .............. - .............. - .............. - .............. - .@@@@@@@@@@@.. - .@@@@@@@@@@@.. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - -u+003e: -greater: - .............. - .............. - .............. - .............. - .............. - .@@@.......... - ..@@@......... - ...@@@........ - ....@@@....... - .....@@@...... - ......@@@..... - .......@@@.... - ........@@@... - .........@@@.. - ........@@@... - .......@@@.... - ......@@@..... - .....@@@...... - ....@@@....... - ...@@@........ - ..@@@......... - .@@@.......... - .............. - .............. - .............. - .............. - .............. - .............. - -u+003f: -question: - .............. - .............. - .............. - .............. - ...@@@@@@@.... - ..@@@@@@@@@... - .@@@.....@@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .........@@@.. - ........@@@... - .......@@@.... - ......@@@..... - ......@@...... - ......@@...... - .............. - .............. - ......@@...... - ......@@...... - ......@@...... - ......@@...... - .............. - .............. - .............. - .............. - .............. - .............. - -u+0040: -at: - .............. - .............. - .............. - .............. - ...@@@@@@@@... - ..@@@@@@@@@@.. - .@@@......@@@. - .@@........@@. - .@@....@@@@@@. - .@@...@@@@@@@. - .@@..@@@...@@. - .@@..@@....@@. - .@@..@@....@@. - .@@..@@....@@. - .@@..@@....@@. - .@@..@@@...@@. - .@@...@@@@@@@. - .@@....@@@@.@. - .@@........... - .@@@.......... - ..@@@@@@@@@@@. - ...@@@@@@@@@@. - .............. - .............. - .............. - .............. - .............. - .............. - -u+0041: -"A": - .............. - .............. - .............. - .............. - ...@@@@@@@.... - ..@@@@@@@@@... - .@@@.....@@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@@@@@@@@@@.. - .@@@@@@@@@@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .............. - .............. - .............. - .............. - .............. - .............. - -u+0042: -"B": - .............. - .............. - .............. - .............. - .@@@@@@@@@.... - .@@@@@@@@@@... - .@@......@@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@......@@... - .@@@@@@@@@.... - .@@@@@@@@@.... - .@@......@@... - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@......@@@.. - .@@@@@@@@@@... - .@@@@@@@@@.... - .............. - .............. - .............. - .............. - .............. - .............. - -u+0043: -"C": - .............. - .............. - .............. - .............. - ...@@@@@@@.... - ..@@@@@@@@@... - .@@@.....@@@.. - .@@.......@@.. - .@@.......@@.. - .@@........... - .@@........... - .@@........... - .@@........... - .@@........... - .@@........... - .@@........... - .@@........... - .@@.......@@.. - .@@.......@@.. - .@@@.....@@@.. - ..@@@@@@@@@... - ...@@@@@@@.... - .............. - .............. - .............. - .............. - .............. - .............. - -u+0044: -"D": - .............. - .............. - .............. - .............. - .@@@@@@@...... - .@@@@@@@@@.... - .@@.....@@@... - .@@......@@... - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@......@@... - .@@.....@@@... - .@@@@@@@@@.... - .@@@@@@@...... - .............. - .............. - .............. - .............. - .............. - .............. - -u+0045: -"E": - .............. - .............. - .............. - .............. - .@@@@@@@@@@@.. - .@@@@@@@@@@@.. - .@@........... - .@@........... - .@@........... - .@@........... - .@@........... - .@@........... - .@@@@@@@@..... - .@@@@@@@@..... - .@@........... - .@@........... - .@@........... - .@@........... - .@@........... - .@@........... - .@@@@@@@@@@@.. - .@@@@@@@@@@@.. - .............. - .............. - .............. - .............. - .............. - .............. - -u+0046: -"F": - .............. - .............. - .............. - .............. - .@@@@@@@@@@@.. - .@@@@@@@@@@@.. - .@@........... - .@@........... - .@@........... - .@@........... - .@@........... - .@@........... - .@@@@@@@@..... - .@@@@@@@@..... - .@@........... - .@@........... - .@@........... - .@@........... - .@@........... - .@@........... - .@@........... - .@@........... - .............. - .............. - .............. - .............. - .............. - .............. - -u+0047: -"G": - .............. - .............. - .............. - .............. - ...@@@@@@@.... - ..@@@@@@@@@... - .@@@.....@@@.. - .@@.......@@.. - .@@.......@@.. - .@@........... - .@@........... - .@@........... - .@@...@@@@@@.. - .@@...@@@@@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@@.....@@@.. - ..@@@@@@@@@... - ...@@@@@@@.... - .............. - .............. - .............. - .............. - .............. - .............. - -u+0048: -"H": - .............. - .............. - .............. - .............. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@@@@@@@@@@.. - .@@@@@@@@@@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .............. - .............. - .............. - .............. - .............. - .............. - -u+0049: -"I": - .............. - .............. - .............. - .............. - ....@@@@@@.... - ....@@@@@@.... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ....@@@@@@.... - ....@@@@@@.... - .............. - .............. - .............. - .............. - .............. - .............. - -u+004a: -"J": - .............. - .............. - .............. - .............. - .......@@@@@@. - .......@@@@@@. - .........@@... - .........@@... - .........@@... - .........@@... - .........@@... - .........@@... - .........@@... - .........@@... - .........@@... - .........@@... - .@@......@@... - .@@......@@... - .@@......@@... - .@@@....@@@... - ..@@@@@@@@.... - ...@@@@@@..... - .............. - .............. - .............. - .............. - .............. - .............. - -u+004b: -"K": - .............. - .............. - .............. - .............. - .@@.......@@.. - .@@......@@@.. - .@@.....@@@... - .@@....@@@.... - .@@...@@@..... - .@@..@@@...... - .@@.@@@....... - .@@@@@........ - .@@@@......... - .@@@@......... - .@@@@@........ - .@@.@@@....... - .@@..@@@...... - .@@...@@@..... - .@@....@@@.... - .@@.....@@@... - .@@......@@@.. - .@@.......@@.. - .............. - .............. - .............. - .............. - .............. - .............. - -u+004c: -"L": - .............. - .............. - .............. - .............. - .@@........... - .@@........... - .@@........... - .@@........... - .@@........... - .@@........... - .@@........... - .@@........... - .@@........... - .@@........... - .@@........... - .@@........... - .@@........... - .@@........... - .@@........... - .@@........... - .@@@@@@@@@@@.. - .@@@@@@@@@@@.. - .............. - .............. - .............. - .............. - .............. - .............. - -u+004d: -"M": - .............. - .............. - .............. - .............. - .@@........@@. - .@@........@@. - .@@@......@@@. - .@@@@....@@@@. - .@@@@@..@@@@@. - .@@.@@@@@@.@@. - .@@..@@@@..@@. - .@@...@@...@@. - .@@........@@. - .@@........@@. - .@@........@@. - .@@........@@. - .@@........@@. - .@@........@@. - .@@........@@. - .@@........@@. - .@@........@@. - .@@........@@. - .............. - .............. - .............. - .............. - .............. - .............. - -u+004e: -"N": - .............. - .............. - .............. - .............. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@@......@@.. - .@@@@.....@@.. - .@@@@@....@@.. - .@@.@@@...@@.. - .@@..@@@..@@.. - .@@...@@@.@@.. - .@@....@@@@@.. - .@@.....@@@@.. - .@@......@@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .............. - .............. - .............. - .............. - .............. - .............. - -u+004f: -"O": - .............. - .............. - .............. - .............. - ...@@@@@@@.... - ..@@@@@@@@@... - .@@@.....@@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@@.....@@@.. - ..@@@@@@@@@... - ...@@@@@@@.... - .............. - .............. - .............. - .............. - .............. - .............. - -u+0050: -"P": - .............. - .............. - .............. - .............. - .@@@@@@@@@.... - .@@@@@@@@@@... - .@@......@@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@......@@@.. - .@@@@@@@@@@... - .@@@@@@@@@.... - .@@........... - .@@........... - .@@........... - .@@........... - .@@........... - .@@........... - .@@........... - .............. - .............. - .............. - .............. - .............. - .............. - -u+0051: -"Q": - .............. - .............. - .............. - .............. - ...@@@@@@@.... - ..@@@@@@@@@... - .@@@.....@@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@...@@@.@@.. - .@@@...@@@@@.. - ..@@@@@@@@@... - ...@@@@@@@@... - .........@@@.. - ..........@@@. - .............. - .............. - .............. - .............. - -u+0052: -"R": - .............. - .............. - .............. - .............. - .@@@@@@@@@.... - .@@@@@@@@@@... - .@@......@@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@......@@@.. - .@@@@@@@@@@... - .@@@@@@@@@.... - .@@@@@........ - .@@.@@@....... - .@@..@@@...... - .@@...@@@..... - .@@....@@@.... - .@@.....@@@... - .@@......@@@.. - .............. - .............. - .............. - .............. - .............. - .............. - -u+0053: -"S": - .............. - .............. - .............. - .............. - ...@@@@@@@.... - ..@@@@@@@@@... - .@@@.....@@@.. - .@@.......@@.. - .@@........... - .@@........... - .@@........... - .@@@.......... - ..@@@@@@@@.... - ...@@@@@@@@... - .........@@@.. - ..........@@.. - ..........@@.. - ..........@@.. - .@@.......@@.. - .@@@.....@@@.. - ..@@@@@@@@@... - ...@@@@@@@.... - .............. - .............. - .............. - .............. - .............. - .............. - -u+0054: -"T": - .............. - .............. - .............. - .............. - .@@@@@@@@@@@@. - .@@@@@@@@@@@@. - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - .............. - .............. - .............. - .............. - .............. - .............. - -u+0055: -"U": - .............. - .............. - .............. - .............. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@@.....@@@.. - ..@@@@@@@@@... - ...@@@@@@@.... - .............. - .............. - .............. - .............. - .............. - .............. - -u+0056: -"V": - .............. - .............. - .............. - .............. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - ..@@.....@@... - ..@@.....@@... - ..@@.....@@... - ..@@.....@@... - ...@@...@@.... - ...@@...@@.... - ...@@...@@.... - ...@@...@@.... - ....@@.@@..... - ....@@.@@..... - ....@@.@@..... - .....@@@...... - .....@@@...... - .....@@@...... - .............. - .............. - .............. - .............. - .............. - .............. - -u+0057: -"W": - .............. - .............. - .............. - .............. - .@@........@@. - .@@........@@. - .@@........@@. - .@@........@@. - .@@........@@. - .@@........@@. - .@@........@@. - .@@........@@. - .@@........@@. - .@@........@@. - .@@...@@...@@. - .@@..@@@@..@@. - .@@.@@@@@@.@@. - .@@@@@..@@@@@. - .@@@@....@@@@. - .@@@......@@@. - .@@........@@. - .@@........@@. - .............. - .............. - .............. - .............. - .............. - .............. - -u+0058: -"X": - .............. - .............. - .............. - .............. - .@@.......@@.. - .@@.......@@.. - ..@@.....@@... - ..@@.....@@... - ...@@...@@.... - ...@@...@@.... - ....@@.@@..... - ....@@.@@..... - .....@@@...... - .....@@@...... - ....@@.@@..... - ....@@.@@..... - ...@@...@@.... - ...@@...@@.... - ..@@.....@@... - ..@@.....@@... - .@@.......@@.. - .@@.......@@.. - .............. - .............. - .............. - .............. - .............. - .............. - -u+0059: -"Y": - .............. - .............. - .............. - .............. - .@@........@@. - .@@........@@. - ..@@......@@.. - ..@@......@@.. - ...@@....@@... - ...@@....@@... - ....@@..@@.... - ....@@..@@.... - .....@@@@..... - .....@@@@..... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - .............. - .............. - .............. - .............. - .............. - .............. - -u+005a: -"Z": - .............. - .............. - .............. - .............. - .@@@@@@@@@@@.. - .@@@@@@@@@@@.. - ..........@@.. - ..........@@.. - .........@@@.. - ........@@@... - .......@@@.... - ......@@@..... - .....@@@...... - ....@@@....... - ...@@@........ - ..@@@......... - .@@@.......... - .@@........... - .@@........... - .@@........... - .@@@@@@@@@@@.. - .@@@@@@@@@@@.. - .............. - .............. - .............. - .............. - .............. - .............. - -u+005b: -bracketleft: - .............. - .............. - .............. - .............. - ....@@@@@@.... - ....@@@@@@.... - ....@@........ - ....@@........ - ....@@........ - ....@@........ - ....@@........ - ....@@........ - ....@@........ - ....@@........ - ....@@........ - ....@@........ - ....@@........ - ....@@........ - ....@@........ - ....@@........ - ....@@@@@@.... - ....@@@@@@.... - .............. - .............. - .............. - .............. - .............. - .............. - -u+005c: -backslash: - .............. - .............. - .............. - .............. - ..@@.......... - ..@@.......... - ...@@......... - ...@@......... - ....@@........ - ....@@........ - .....@@....... - .....@@....... - ......@@...... - ......@@...... - .......@@..... - .......@@..... - ........@@.... - ........@@.... - .........@@... - .........@@... - ..........@@.. - ..........@@.. - .............. - .............. - .............. - .............. - .............. - .............. - -u+005d: -bracketright: - .............. - .............. - .............. - .............. - ....@@@@@@.... - ....@@@@@@.... - ........@@.... - ........@@.... - ........@@.... - ........@@.... - ........@@.... - ........@@.... - ........@@.... - ........@@.... - ........@@.... - ........@@.... - ........@@.... - ........@@.... - ........@@.... - ........@@.... - ....@@@@@@.... - ....@@@@@@.... - .............. - .............. - .............. - .............. - .............. - .............. - -u+005e: -asciicircum: - .............. - .............. - ......@....... - .....@@@...... - ....@@@@@..... - ...@@@.@@@.... - ..@@@...@@@... - .@@@.....@@@.. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - -u+005f: -underscore: - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .@@@@@@@@@@@.. - .@@@@@@@@@@@.. - .............. - .............. - .............. - -u+0060: -grave: - ..@@@......... - ...@@@........ - ....@@@....... - .....@@@...... - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - -u+0061: -"a": - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - ...@@@@@@@.... - ...@@@@@@@@... - .........@@@.. - ..........@@.. - ...@@@@@@@@@.. - ..@@@@@@@@@@.. - .@@@......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@@......@@.. - ..@@@@@@@@@@.. - ...@@@@@@@@@.. - .............. - .............. - .............. - .............. - .............. - .............. - -u+0062: -"b": - .............. - .............. - .............. - .............. - .@@........... - .@@........... - .@@........... - .@@........... - .@@........... - .@@@@@@@@@.... - .@@@@@@@@@@... - .@@......@@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@......@@@.. - .@@@@@@@@@@... - .@@@@@@@@@.... - .............. - .............. - .............. - .............. - .............. - .............. - -u+0063: -"c": - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - ...@@@@@@@.... - ..@@@@@@@@@... - .@@@.....@@@.. - .@@........... - .@@........... - .@@........... - .@@........... - .@@........... - .@@........... - .@@........... - .@@@.....@@@.. - ..@@@@@@@@@... - ...@@@@@@@.... - .............. - .............. - .............. - .............. - .............. - .............. - -u+0064: -"d": - .............. - .............. - .............. - .............. - ..........@@.. - ..........@@.. - ..........@@.. - ..........@@.. - ..........@@.. - ...@@@@@@@@@.. - ..@@@@@@@@@@.. - .@@@......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@@......@@.. - ..@@@@@@@@@@.. - ...@@@@@@@@@.. - .............. - .............. - .............. - .............. - .............. - .............. - -u+0065: -"e": - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - ...@@@@@@@.... - ..@@@@@@@@@... - .@@@.....@@@.. - .@@.......@@.. - .@@.......@@.. - .@@@@@@@@@@@.. - .@@@@@@@@@@@.. - .@@........... - .@@........... - .@@........... - .@@@.....@@@.. - ..@@@@@@@@@... - ...@@@@@@@.... - .............. - .............. - .............. - .............. - .............. - .............. - -u+0066: -"f": - .............. - .............. - .............. - .............. - ......@@@@@@.. - .....@@@@@@@.. - .....@@....... - .....@@....... - .....@@....... - ..@@@@@@@@.... - ..@@@@@@@@.... - .....@@....... - .....@@....... - .....@@....... - .....@@....... - .....@@....... - .....@@....... - .....@@....... - .....@@....... - .....@@....... - .....@@....... - .....@@....... - .............. - .............. - .............. - .............. - .............. - .............. - -u+0067: -"g": - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - ...@@@@@@@@@.. - ..@@@@@@@@@@.. - .@@@......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@@......@@.. - ..@@@@@@@@@@.. - ...@@@@@@@@@.. - ..........@@.. - ..........@@.. - .........@@@.. - ..@@@@@@@@@... - ..@@@@@@@@.... - .............. - -u+0068: -"h": - .............. - .............. - .............. - .............. - .@@........... - .@@........... - .@@........... - .@@........... - .@@........... - .@@@@@@@@@.... - .@@@@@@@@@@... - .@@......@@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .............. - .............. - .............. - .............. - .............. - .............. - -u+0069: -"i": - .............. - .............. - .............. - .............. - ......@@...... - ......@@...... - ......@@...... - .............. - .............. - ....@@@@...... - ....@@@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ....@@@@@@.... - ....@@@@@@.... - .............. - .............. - .............. - .............. - .............. - .............. - -u+006a: -"j": - .............. - .............. - .............. - .............. - .........@@... - .........@@... - .........@@... - .............. - .............. - .......@@@@... - .......@@@@... - .........@@... - .........@@... - .........@@... - .........@@... - .........@@... - .........@@... - .........@@... - .........@@... - .........@@... - .........@@... - .........@@... - ..@@.....@@... - ..@@.....@@... - ..@@@...@@@... - ...@@@@@@@.... - ....@@@@@..... - .............. - -u+006b: -"k": - .............. - .............. - .............. - .............. - ..@@.......... - ..@@.......... - ..@@.......... - ..@@.......... - ..@@.......... - ..@@.....@@@.. - ..@@....@@@... - ..@@...@@@.... - ..@@..@@@..... - ..@@.@@@...... - ..@@@@@....... - ..@@@@........ - ..@@@@@....... - ..@@.@@@...... - ..@@..@@@..... - ..@@...@@@.... - ..@@....@@@... - ..@@.....@@@.. - .............. - .............. - .............. - .............. - .............. - .............. - -u+006c: -"l": - .............. - .............. - .............. - .............. - ....@@@@...... - ....@@@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ....@@@@@@.... - ....@@@@@@.... - .............. - .............. - .............. - .............. - .............. - .............. - -u+006d: -"m": - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .@@@@@@@@@@... - .@@@@@@@@@@@.. - .@@...@@..@@@. - .@@...@@...@@. - .@@...@@...@@. - .@@...@@...@@. - .@@...@@...@@. - .@@...@@...@@. - .@@...@@...@@. - .@@...@@...@@. - .@@...@@...@@. - .@@...@@...@@. - .@@...@@...@@. - .............. - .............. - .............. - .............. - .............. - .............. - -u+006e: -"n": - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .@@@@@@@@@.... - .@@@@@@@@@@... - .@@......@@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .............. - .............. - .............. - .............. - .............. - .............. - -u+006f: -"o": - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - ...@@@@@@@.... - ..@@@@@@@@@... - .@@@.....@@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@@.....@@@.. - ..@@@@@@@@@... - ...@@@@@@@.... - .............. - .............. - .............. - .............. - .............. - .............. - -u+0070: -"p": - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .@@@@@@@@@.... - .@@@@@@@@@@... - .@@......@@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@......@@@.. - .@@@@@@@@@@... - .@@@@@@@@@.... - .@@........... - .@@........... - .@@........... - .@@........... - .@@........... - .............. - -u+0071: -"q": - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - ...@@@@@@@@@.. - ..@@@@@@@@@@.. - .@@@......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@@......@@.. - ..@@@@@@@@@@.. - ...@@@@@@@@@.. - ..........@@.. - ..........@@.. - ..........@@.. - ..........@@.. - ..........@@.. - .............. - -u+0072: -"r": - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .@@..@@@@@@@.. - .@@.@@@@@@@@.. - .@@@@@........ - .@@@@......... - .@@@.......... - .@@........... - .@@........... - .@@........... - .@@........... - .@@........... - .@@........... - .@@........... - .@@........... - .............. - .............. - .............. - .............. - .............. - .............. - -u+0073: -"s": - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - ...@@@@@@@.... - ..@@@@@@@@@... - .@@@.....@@@.. - .@@........... - .@@@.......... - ..@@@@@@@@.... - ...@@@@@@@@... - .........@@@.. - ..........@@.. - ..........@@.. - .@@@.....@@@.. - ..@@@@@@@@@... - ...@@@@@@@.... - .............. - .............. - .............. - .............. - .............. - .............. - -u+0074: -"t": - .............. - .............. - .............. - .............. - .....@@....... - .....@@....... - .....@@....... - .....@@....... - .....@@....... - ..@@@@@@@@.... - ..@@@@@@@@.... - .....@@....... - .....@@....... - .....@@....... - .....@@....... - .....@@....... - .....@@....... - .....@@....... - .....@@....... - .....@@....... - .....@@@@@@... - ......@@@@@... - .............. - .............. - .............. - .............. - .............. - .............. - -u+0075: -"u": - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@@......@@.. - ..@@@@@@@@@@.. - ...@@@@@@@@@.. - .............. - .............. - .............. - .............. - .............. - .............. - -u+0076: -"v": - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - ..@@.....@@... - ..@@.....@@... - ..@@.....@@... - ...@@...@@.... - ...@@...@@.... - ...@@...@@.... - ....@@.@@..... - ....@@.@@..... - .....@@@...... - .....@@@...... - .............. - .............. - .............. - .............. - .............. - .............. - -u+0077: -"w": - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .@@........@@. - .@@........@@. - .@@........@@. - .@@........@@. - .@@...@@...@@. - .@@...@@...@@. - .@@...@@...@@. - .@@...@@...@@. - .@@...@@...@@. - .@@...@@...@@. - .@@@..@@..@@@. - ..@@@@@@@@@@.. - ...@@@@@@@@... - .............. - .............. - .............. - .............. - .............. - .............. - -u+0078: -"x": - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .@@.......@@.. - .@@.......@@.. - .@@@.....@@@.. - ..@@@...@@@... - ...@@@.@@@.... - ....@@@@@..... - .....@@@...... - ....@@@@@..... - ...@@@.@@@.... - ..@@@...@@@... - .@@@.....@@@.. - .@@.......@@.. - .@@.......@@.. - .............. - .............. - .............. - .............. - .............. - .............. - -u+0079: -"y": - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@.......@@.. - .@@@......@@.. - ..@@@@@@@@@@.. - ...@@@@@@@@@.. - ..........@@.. - ..........@@.. - .........@@@.. - ..@@@@@@@@@... - ..@@@@@@@@.... - .............. - -u+007a: -"z": - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .@@@@@@@@@@@.. - .@@@@@@@@@@@.. - .........@@@.. - ........@@@... - .......@@@.... - ......@@@..... - .....@@@...... - ....@@@....... - ...@@@........ - ..@@@......... - .@@@.......... - .@@@@@@@@@@@.. - .@@@@@@@@@@@.. - .............. - .............. - .............. - .............. - .............. - .............. - -u+007b: -braceleft: - .............. - .............. - .............. - .............. - .......@@@.... - ......@@@@.... - .....@@@...... - .....@@....... - .....@@....... - .....@@....... - .....@@....... - .....@@....... - ...@@@........ - ...@@@........ - .....@@....... - .....@@....... - .....@@....... - .....@@....... - .....@@....... - .....@@@...... - ......@@@@.... - .......@@@.... - .............. - .............. - .............. - .............. - .............. - .............. - -u+007c: -bar: - .............. - .............. - .............. - .............. - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - .............. - .............. - .............. - .............. - .............. - .............. - -u+007d: -braceright: - .............. - .............. - .............. - .............. - ...@@@........ - ...@@@@....... - .....@@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - .......@@@.... - .......@@@.... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - ......@@...... - .....@@@...... - ...@@@@....... - ...@@@........ - .............. - .............. - .............. - .............. - .............. - .............. - -u+007e: -asciitilde: - .............. - .............. - ..@@@@....@@.. - .@@@@@@...@@.. - .@@..@@@..@@.. - .@@...@@@@@@.. - .@@....@@@@... - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - .............. - +name: Terminus Bold 14x28 +spacing: character-cell +cell-size: 14 28 +family: Terminus +foundry: xos4 +copyright: Copyright (C) 2020 Dimitar Toshkov Zhekov +notice: Licensed under the SIL Open Font License, Version 1.1 +point-size: 28 +weight: bold +slant: roman +setwidth: normal +dpi: 72 72 +average-width: 14 +ascent: 22 +descent: 6 +shift-up: -6 +encoding: iso10646-1 +default-char: u+fffd +min-word-space: 14 +converter: monobit v0.32 +source-name: ter-u28b.bdf +source-format: BDF v2.1 +history: load --format=bdf + +u+0000: +char0: + .............. + .............. + .............. + .............. + .@@@@...@@@@.. + .@@@@...@@@@.. + .@@.......@@.. + .@@.......@@.. + .............. + .............. + .............. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .............. + .............. + .............. + .@@.......@@.. + .@@.......@@.. + .@@@@...@@@@.. + .@@@@...@@@@.. + .............. + .............. + .............. + .............. + .............. + .............. + +u+0020: +space: + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + +u+0021: +exclam: + .............. + .............. + .............. + .............. + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + .............. + .............. + ......@@...... + ......@@...... + ......@@...... + ......@@...... + .............. + .............. + .............. + .............. + .............. + .............. + +u+0022: +quotedbl: + .............. + .............. + ...@@...@@.... + ...@@...@@.... + ...@@...@@.... + ...@@...@@.... + ...@@...@@.... + ...@@...@@.... + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + +u+0023: +numbersign: + .............. + .............. + .............. + .............. + ...@@...@@.... + ...@@...@@.... + ...@@...@@.... + ...@@...@@.... + .@@@@@@@@@@@.. + .@@@@@@@@@@@.. + ...@@...@@.... + ...@@...@@.... + ...@@...@@.... + ...@@...@@.... + ...@@...@@.... + ...@@...@@.... + .@@@@@@@@@@@.. + .@@@@@@@@@@@.. + ...@@...@@.... + ...@@...@@.... + ...@@...@@.... + ...@@...@@.... + .............. + .............. + .............. + .............. + .............. + .............. + +u+0024: +dollar: + .............. + .............. + ......@@...... + ......@@...... + ......@@...... + ...@@@@@@@@... + ..@@@@@@@@@@.. + .@@@..@@..@@@. + .@@...@@...@@. + .@@...@@...... + .@@...@@...... + .@@@..@@...... + ..@@@@@@@@@... + ...@@@@@@@@@.. + ......@@..@@@. + ......@@...@@. + ......@@...@@. + .@@...@@...@@. + .@@@..@@..@@@. + ..@@@@@@@@@@.. + ...@@@@@@@@... + ......@@...... + ......@@...... + ......@@...... + .............. + .............. + .............. + .............. + +u+0025: +percent: + .............. + .............. + .............. + .............. + ..@@@@....@@.. + .@@@@@@...@@.. + .@@..@@..@@... + .@@@@@@..@@... + ..@@@@..@@.... + ........@@.... + .......@@..... + .......@@..... + ......@@...... + ......@@...... + .....@@....... + .....@@....... + ....@@........ + ....@@..@@@@.. + ...@@..@@@@@@. + ...@@..@@..@@. + ..@@...@@@@@@. + ..@@....@@@@.. + .............. + .............. + .............. + .............. + .............. + .............. + +u+0026: +ampersand: + .............. + .............. + .............. + .............. + ....@@@@...... + ...@@@@@@..... + ..@@@..@@@.... + ..@@....@@.... + ..@@....@@.... + ..@@@..@@@.... + ...@@@@@@..... + ....@@@@...... + ....@@@....... + ...@@@@@...@@. + ..@@@.@@@.@@@. + .@@@...@@@@@.. + .@@.....@@@... + .@@.....@@@... + .@@.....@@@... + .@@@...@@@@@.. + ..@@@@@@@.@@@. + ...@@@@@...@@. + .............. + .............. + .............. + .............. + .............. + .............. + +u+0027: +quotesingle: + .............. + .............. + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + +u+0028: +parenleft: + .............. + .............. + .............. + .............. + .......@@..... + ......@@...... + .....@@....... + .....@@....... + ....@@........ + ....@@........ + ....@@........ + ....@@........ + ....@@........ + ....@@........ + ....@@........ + ....@@........ + ....@@........ + ....@@........ + .....@@....... + .....@@....... + ......@@...... + .......@@..... + .............. + .............. + .............. + .............. + .............. + .............. + +u+0029: +parenright: + .............. + .............. + .............. + .............. + ....@@........ + .....@@....... + ......@@...... + ......@@...... + .......@@..... + .......@@..... + .......@@..... + .......@@..... + .......@@..... + .......@@..... + .......@@..... + .......@@..... + .......@@..... + .......@@..... + ......@@...... + ......@@...... + .....@@....... + ....@@........ + .............. + .............. + .............. + .............. + .............. + .............. + +u+002a: +asterisk: + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + ..@@@...@@@... + ...@@@.@@@.... + ....@@@@@..... + .....@@@...... + .@@@@@@@@@@@.. + .@@@@@@@@@@@.. + .....@@@...... + ....@@@@@..... + ...@@@.@@@.... + ..@@@...@@@... + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + +u+002b: +plus: + .............. + .............. + .............. + .............. + .............. + .............. + .............. + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + .@@@@@@@@@@@@. + .@@@@@@@@@@@@. + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + +u+002c: +comma: + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + ......@@...... + ......@@...... + ......@@...... + ......@@...... + .....@@....... + ....@@........ + .............. + .............. + .............. + .............. + +u+002d: +hyphen: + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .@@@@@@@@@@@.. + .@@@@@@@@@@@.. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + +u+002e: +period: + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + ......@@...... + ......@@...... + ......@@...... + ......@@...... + .............. + .............. + .............. + .............. + .............. + .............. + +u+002f: +slash: + .............. + .............. + .............. + .............. + ..........@@.. + ..........@@.. + .........@@... + .........@@... + ........@@.... + ........@@.... + .......@@..... + .......@@..... + ......@@...... + ......@@...... + .....@@....... + .....@@....... + ....@@........ + ....@@........ + ...@@......... + ...@@......... + ..@@.......... + ..@@.......... + .............. + .............. + .............. + .............. + .............. + .............. + +u+0030: +zero: + .............. + .............. + .............. + .............. + ...@@@@@@@.... + ..@@@@@@@@@... + .@@@.....@@@.. + .@@.......@@.. + .@@......@@@.. + .@@.....@@@@.. + .@@....@@@@@.. + .@@...@@@.@@.. + .@@..@@@..@@.. + .@@.@@@...@@.. + .@@@@@....@@.. + .@@@@.....@@.. + .@@@......@@.. + .@@.......@@.. + .@@.......@@.. + .@@@.....@@@.. + ..@@@@@@@@@... + ...@@@@@@@.... + .............. + .............. + .............. + .............. + .............. + .............. + +u+0031: +one: + .............. + .............. + .............. + .............. + ......@@...... + .....@@@...... + ....@@@@...... + ...@@@@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ...@@@@@@@@... + ...@@@@@@@@... + .............. + .............. + .............. + .............. + .............. + .............. + +u+0032: +two: + .............. + .............. + .............. + .............. + ...@@@@@@@.... + ..@@@@@@@@@... + .@@@.....@@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + ..........@@.. + .........@@@.. + ........@@@... + .......@@@.... + ......@@@..... + .....@@@...... + ....@@@....... + ...@@@........ + ..@@@......... + .@@@.......... + .@@@@@@@@@@@.. + .@@@@@@@@@@@.. + .............. + .............. + .............. + .............. + .............. + .............. + +u+0033: +three: + .............. + .............. + .............. + .............. + ...@@@@@@@.... + ..@@@@@@@@@... + .@@@.....@@@.. + .@@.......@@.. + ..........@@.. + ..........@@.. + ..........@@.. + .........@@@.. + ....@@@@@@@... + ....@@@@@@@... + .........@@@.. + ..........@@.. + ..........@@.. + ..........@@.. + .@@.......@@.. + .@@@.....@@@.. + ..@@@@@@@@@... + ...@@@@@@@.... + .............. + .............. + .............. + .............. + .............. + .............. + +u+0034: +four: + .............. + .............. + .............. + .............. + ..........@@.. + .........@@@.. + ........@@@@.. + .......@@@@@.. + ......@@@.@@.. + .....@@@..@@.. + ....@@@...@@.. + ...@@@....@@.. + ..@@@.....@@.. + .@@@......@@.. + .@@.......@@.. + .@@.......@@.. + .@@@@@@@@@@@.. + .@@@@@@@@@@@.. + ..........@@.. + ..........@@.. + ..........@@.. + ..........@@.. + .............. + .............. + .............. + .............. + .............. + .............. + +u+0035: +five: + .............. + .............. + .............. + .............. + .@@@@@@@@@@@.. + .@@@@@@@@@@@.. + .@@........... + .@@........... + .@@........... + .@@........... + .@@........... + .@@@@@@@@@.... + .@@@@@@@@@@... + .........@@@.. + ..........@@.. + ..........@@.. + ..........@@.. + ..........@@.. + .@@.......@@.. + .@@@.....@@@.. + ..@@@@@@@@@... + ...@@@@@@@.... + .............. + .............. + .............. + .............. + .............. + .............. + +u+0036: +six: + .............. + .............. + .............. + .............. + ...@@@@@@@@... + ..@@@@@@@@@... + .@@@.......... + .@@........... + .@@........... + .@@........... + .@@........... + .@@@@@@@@@.... + .@@@@@@@@@@... + .@@......@@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@@.....@@@.. + ..@@@@@@@@@... + ...@@@@@@@.... + .............. + .............. + .............. + .............. + .............. + .............. + +u+0037: +seven: + .............. + .............. + .............. + .............. + .@@@@@@@@@@@.. + .@@@@@@@@@@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .........@@... + .........@@... + ........@@.... + ........@@.... + .......@@..... + .......@@..... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + .............. + .............. + .............. + .............. + .............. + .............. + +u+0038: +eight: + .............. + .............. + .............. + .............. + ...@@@@@@@.... + ..@@@@@@@@@... + .@@@.....@@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@@.....@@@.. + ..@@@@@@@@@... + ..@@@@@@@@@... + .@@@.....@@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@@.....@@@.. + ..@@@@@@@@@... + ...@@@@@@@.... + .............. + .............. + .............. + .............. + .............. + .............. + +u+0039: +nine: + .............. + .............. + .............. + .............. + ...@@@@@@@.... + ..@@@@@@@@@... + .@@@.....@@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@@......@@.. + ..@@@@@@@@@@.. + ...@@@@@@@@@.. + ..........@@.. + ..........@@.. + ..........@@.. + ..........@@.. + .........@@@.. + ..@@@@@@@@@... + ..@@@@@@@@.... + .............. + .............. + .............. + .............. + .............. + .............. + +u+003a: +colon: + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + ......@@...... + ......@@...... + ......@@...... + ......@@...... + .............. + .............. + .............. + .............. + .............. + ......@@...... + ......@@...... + ......@@...... + ......@@...... + .............. + .............. + .............. + .............. + .............. + .............. + +u+003b: +semicolon: + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + ......@@...... + ......@@...... + ......@@...... + ......@@...... + .............. + .............. + .............. + .............. + .............. + ......@@...... + ......@@...... + ......@@...... + ......@@...... + .....@@....... + ....@@........ + .............. + .............. + .............. + .............. + +u+003c: +less: + .............. + .............. + .............. + .............. + .............. + .........@@@.. + ........@@@... + .......@@@.... + ......@@@..... + .....@@@...... + ....@@@....... + ...@@@........ + ..@@@......... + .@@@.......... + ..@@@......... + ...@@@........ + ....@@@....... + .....@@@...... + ......@@@..... + .......@@@.... + ........@@@... + .........@@@.. + .............. + .............. + .............. + .............. + .............. + .............. + +u+003d: +equal: + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .@@@@@@@@@@@.. + .@@@@@@@@@@@.. + .............. + .............. + .............. + .............. + .@@@@@@@@@@@.. + .@@@@@@@@@@@.. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + +u+003e: +greater: + .............. + .............. + .............. + .............. + .............. + .@@@.......... + ..@@@......... + ...@@@........ + ....@@@....... + .....@@@...... + ......@@@..... + .......@@@.... + ........@@@... + .........@@@.. + ........@@@... + .......@@@.... + ......@@@..... + .....@@@...... + ....@@@....... + ...@@@........ + ..@@@......... + .@@@.......... + .............. + .............. + .............. + .............. + .............. + .............. + +u+003f: +question: + .............. + .............. + .............. + .............. + ...@@@@@@@.... + ..@@@@@@@@@... + .@@@.....@@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .........@@@.. + ........@@@... + .......@@@.... + ......@@@..... + ......@@...... + ......@@...... + .............. + .............. + ......@@...... + ......@@...... + ......@@...... + ......@@...... + .............. + .............. + .............. + .............. + .............. + .............. + +u+0040: +at: + .............. + .............. + .............. + .............. + ...@@@@@@@@... + ..@@@@@@@@@@.. + .@@@......@@@. + .@@........@@. + .@@....@@@@@@. + .@@...@@@@@@@. + .@@..@@@...@@. + .@@..@@....@@. + .@@..@@....@@. + .@@..@@....@@. + .@@..@@....@@. + .@@..@@@...@@. + .@@...@@@@@@@. + .@@....@@@@.@. + .@@........... + .@@@.......... + ..@@@@@@@@@@@. + ...@@@@@@@@@@. + .............. + .............. + .............. + .............. + .............. + .............. + +u+0041: +"A": + .............. + .............. + .............. + .............. + ...@@@@@@@.... + ..@@@@@@@@@... + .@@@.....@@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@@@@@@@@@@.. + .@@@@@@@@@@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .............. + .............. + .............. + .............. + .............. + .............. + +u+0042: +"B": + .............. + .............. + .............. + .............. + .@@@@@@@@@.... + .@@@@@@@@@@... + .@@......@@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@......@@... + .@@@@@@@@@.... + .@@@@@@@@@.... + .@@......@@... + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@......@@@.. + .@@@@@@@@@@... + .@@@@@@@@@.... + .............. + .............. + .............. + .............. + .............. + .............. + +u+0043: +"C": + .............. + .............. + .............. + .............. + ...@@@@@@@.... + ..@@@@@@@@@... + .@@@.....@@@.. + .@@.......@@.. + .@@.......@@.. + .@@........... + .@@........... + .@@........... + .@@........... + .@@........... + .@@........... + .@@........... + .@@........... + .@@.......@@.. + .@@.......@@.. + .@@@.....@@@.. + ..@@@@@@@@@... + ...@@@@@@@.... + .............. + .............. + .............. + .............. + .............. + .............. + +u+0044: +"D": + .............. + .............. + .............. + .............. + .@@@@@@@...... + .@@@@@@@@@.... + .@@.....@@@... + .@@......@@... + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@......@@... + .@@.....@@@... + .@@@@@@@@@.... + .@@@@@@@...... + .............. + .............. + .............. + .............. + .............. + .............. + +u+0045: +"E": + .............. + .............. + .............. + .............. + .@@@@@@@@@@@.. + .@@@@@@@@@@@.. + .@@........... + .@@........... + .@@........... + .@@........... + .@@........... + .@@........... + .@@@@@@@@..... + .@@@@@@@@..... + .@@........... + .@@........... + .@@........... + .@@........... + .@@........... + .@@........... + .@@@@@@@@@@@.. + .@@@@@@@@@@@.. + .............. + .............. + .............. + .............. + .............. + .............. + +u+0046: +"F": + .............. + .............. + .............. + .............. + .@@@@@@@@@@@.. + .@@@@@@@@@@@.. + .@@........... + .@@........... + .@@........... + .@@........... + .@@........... + .@@........... + .@@@@@@@@..... + .@@@@@@@@..... + .@@........... + .@@........... + .@@........... + .@@........... + .@@........... + .@@........... + .@@........... + .@@........... + .............. + .............. + .............. + .............. + .............. + .............. + +u+0047: +"G": + .............. + .............. + .............. + .............. + ...@@@@@@@.... + ..@@@@@@@@@... + .@@@.....@@@.. + .@@.......@@.. + .@@.......@@.. + .@@........... + .@@........... + .@@........... + .@@...@@@@@@.. + .@@...@@@@@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@@.....@@@.. + ..@@@@@@@@@... + ...@@@@@@@.... + .............. + .............. + .............. + .............. + .............. + .............. + +u+0048: +"H": + .............. + .............. + .............. + .............. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@@@@@@@@@@.. + .@@@@@@@@@@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .............. + .............. + .............. + .............. + .............. + .............. + +u+0049: +"I": + .............. + .............. + .............. + .............. + ....@@@@@@.... + ....@@@@@@.... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ....@@@@@@.... + ....@@@@@@.... + .............. + .............. + .............. + .............. + .............. + .............. + +u+004a: +"J": + .............. + .............. + .............. + .............. + .......@@@@@@. + .......@@@@@@. + .........@@... + .........@@... + .........@@... + .........@@... + .........@@... + .........@@... + .........@@... + .........@@... + .........@@... + .........@@... + .@@......@@... + .@@......@@... + .@@......@@... + .@@@....@@@... + ..@@@@@@@@.... + ...@@@@@@..... + .............. + .............. + .............. + .............. + .............. + .............. + +u+004b: +"K": + .............. + .............. + .............. + .............. + .@@.......@@.. + .@@......@@@.. + .@@.....@@@... + .@@....@@@.... + .@@...@@@..... + .@@..@@@...... + .@@.@@@....... + .@@@@@........ + .@@@@......... + .@@@@......... + .@@@@@........ + .@@.@@@....... + .@@..@@@...... + .@@...@@@..... + .@@....@@@.... + .@@.....@@@... + .@@......@@@.. + .@@.......@@.. + .............. + .............. + .............. + .............. + .............. + .............. + +u+004c: +"L": + .............. + .............. + .............. + .............. + .@@........... + .@@........... + .@@........... + .@@........... + .@@........... + .@@........... + .@@........... + .@@........... + .@@........... + .@@........... + .@@........... + .@@........... + .@@........... + .@@........... + .@@........... + .@@........... + .@@@@@@@@@@@.. + .@@@@@@@@@@@.. + .............. + .............. + .............. + .............. + .............. + .............. + +u+004d: +"M": + .............. + .............. + .............. + .............. + .@@........@@. + .@@........@@. + .@@@......@@@. + .@@@@....@@@@. + .@@@@@..@@@@@. + .@@.@@@@@@.@@. + .@@..@@@@..@@. + .@@...@@...@@. + .@@........@@. + .@@........@@. + .@@........@@. + .@@........@@. + .@@........@@. + .@@........@@. + .@@........@@. + .@@........@@. + .@@........@@. + .@@........@@. + .............. + .............. + .............. + .............. + .............. + .............. + +u+004e: +"N": + .............. + .............. + .............. + .............. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@@......@@.. + .@@@@.....@@.. + .@@@@@....@@.. + .@@.@@@...@@.. + .@@..@@@..@@.. + .@@...@@@.@@.. + .@@....@@@@@.. + .@@.....@@@@.. + .@@......@@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .............. + .............. + .............. + .............. + .............. + .............. + +u+004f: +"O": + .............. + .............. + .............. + .............. + ...@@@@@@@.... + ..@@@@@@@@@... + .@@@.....@@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@@.....@@@.. + ..@@@@@@@@@... + ...@@@@@@@.... + .............. + .............. + .............. + .............. + .............. + .............. + +u+0050: +"P": + .............. + .............. + .............. + .............. + .@@@@@@@@@.... + .@@@@@@@@@@... + .@@......@@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@......@@@.. + .@@@@@@@@@@... + .@@@@@@@@@.... + .@@........... + .@@........... + .@@........... + .@@........... + .@@........... + .@@........... + .@@........... + .............. + .............. + .............. + .............. + .............. + .............. + +u+0051: +"Q": + .............. + .............. + .............. + .............. + ...@@@@@@@.... + ..@@@@@@@@@... + .@@@.....@@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@...@@@.@@.. + .@@@...@@@@@.. + ..@@@@@@@@@... + ...@@@@@@@@... + .........@@@.. + ..........@@@. + .............. + .............. + .............. + .............. + +u+0052: +"R": + .............. + .............. + .............. + .............. + .@@@@@@@@@.... + .@@@@@@@@@@... + .@@......@@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@......@@@.. + .@@@@@@@@@@... + .@@@@@@@@@.... + .@@@@@........ + .@@.@@@....... + .@@..@@@...... + .@@...@@@..... + .@@....@@@.... + .@@.....@@@... + .@@......@@@.. + .............. + .............. + .............. + .............. + .............. + .............. + +u+0053: +"S": + .............. + .............. + .............. + .............. + ...@@@@@@@.... + ..@@@@@@@@@... + .@@@.....@@@.. + .@@.......@@.. + .@@........... + .@@........... + .@@........... + .@@@.......... + ..@@@@@@@@.... + ...@@@@@@@@... + .........@@@.. + ..........@@.. + ..........@@.. + ..........@@.. + .@@.......@@.. + .@@@.....@@@.. + ..@@@@@@@@@... + ...@@@@@@@.... + .............. + .............. + .............. + .............. + .............. + .............. + +u+0054: +"T": + .............. + .............. + .............. + .............. + .@@@@@@@@@@@@. + .@@@@@@@@@@@@. + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + .............. + .............. + .............. + .............. + .............. + .............. + +u+0055: +"U": + .............. + .............. + .............. + .............. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@@.....@@@.. + ..@@@@@@@@@... + ...@@@@@@@.... + .............. + .............. + .............. + .............. + .............. + .............. + +u+0056: +"V": + .............. + .............. + .............. + .............. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + ..@@.....@@... + ..@@.....@@... + ..@@.....@@... + ..@@.....@@... + ...@@...@@.... + ...@@...@@.... + ...@@...@@.... + ...@@...@@.... + ....@@.@@..... + ....@@.@@..... + ....@@.@@..... + .....@@@...... + .....@@@...... + .....@@@...... + .............. + .............. + .............. + .............. + .............. + .............. + +u+0057: +"W": + .............. + .............. + .............. + .............. + .@@........@@. + .@@........@@. + .@@........@@. + .@@........@@. + .@@........@@. + .@@........@@. + .@@........@@. + .@@........@@. + .@@........@@. + .@@........@@. + .@@...@@...@@. + .@@..@@@@..@@. + .@@.@@@@@@.@@. + .@@@@@..@@@@@. + .@@@@....@@@@. + .@@@......@@@. + .@@........@@. + .@@........@@. + .............. + .............. + .............. + .............. + .............. + .............. + +u+0058: +"X": + .............. + .............. + .............. + .............. + .@@.......@@.. + .@@.......@@.. + ..@@.....@@... + ..@@.....@@... + ...@@...@@.... + ...@@...@@.... + ....@@.@@..... + ....@@.@@..... + .....@@@...... + .....@@@...... + ....@@.@@..... + ....@@.@@..... + ...@@...@@.... + ...@@...@@.... + ..@@.....@@... + ..@@.....@@... + .@@.......@@.. + .@@.......@@.. + .............. + .............. + .............. + .............. + .............. + .............. + +u+0059: +"Y": + .............. + .............. + .............. + .............. + .@@........@@. + .@@........@@. + ..@@......@@.. + ..@@......@@.. + ...@@....@@... + ...@@....@@... + ....@@..@@.... + ....@@..@@.... + .....@@@@..... + .....@@@@..... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + .............. + .............. + .............. + .............. + .............. + .............. + +u+005a: +"Z": + .............. + .............. + .............. + .............. + .@@@@@@@@@@@.. + .@@@@@@@@@@@.. + ..........@@.. + ..........@@.. + .........@@@.. + ........@@@... + .......@@@.... + ......@@@..... + .....@@@...... + ....@@@....... + ...@@@........ + ..@@@......... + .@@@.......... + .@@........... + .@@........... + .@@........... + .@@@@@@@@@@@.. + .@@@@@@@@@@@.. + .............. + .............. + .............. + .............. + .............. + .............. + +u+005b: +bracketleft: + .............. + .............. + .............. + .............. + ....@@@@@@.... + ....@@@@@@.... + ....@@........ + ....@@........ + ....@@........ + ....@@........ + ....@@........ + ....@@........ + ....@@........ + ....@@........ + ....@@........ + ....@@........ + ....@@........ + ....@@........ + ....@@........ + ....@@........ + ....@@@@@@.... + ....@@@@@@.... + .............. + .............. + .............. + .............. + .............. + .............. + +u+005c: +backslash: + .............. + .............. + .............. + .............. + ..@@.......... + ..@@.......... + ...@@......... + ...@@......... + ....@@........ + ....@@........ + .....@@....... + .....@@....... + ......@@...... + ......@@...... + .......@@..... + .......@@..... + ........@@.... + ........@@.... + .........@@... + .........@@... + ..........@@.. + ..........@@.. + .............. + .............. + .............. + .............. + .............. + .............. + +u+005d: +bracketright: + .............. + .............. + .............. + .............. + ....@@@@@@.... + ....@@@@@@.... + ........@@.... + ........@@.... + ........@@.... + ........@@.... + ........@@.... + ........@@.... + ........@@.... + ........@@.... + ........@@.... + ........@@.... + ........@@.... + ........@@.... + ........@@.... + ........@@.... + ....@@@@@@.... + ....@@@@@@.... + .............. + .............. + .............. + .............. + .............. + .............. + +u+005e: +asciicircum: + .............. + .............. + ......@....... + .....@@@...... + ....@@@@@..... + ...@@@.@@@.... + ..@@@...@@@... + .@@@.....@@@.. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + +u+005f: +underscore: + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .@@@@@@@@@@@.. + .@@@@@@@@@@@.. + .............. + .............. + .............. + +u+0060: +grave: + ..@@@......... + ...@@@........ + ....@@@....... + .....@@@...... + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + +u+0061: +"a": + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + ...@@@@@@@.... + ...@@@@@@@@... + .........@@@.. + ..........@@.. + ...@@@@@@@@@.. + ..@@@@@@@@@@.. + .@@@......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@@......@@.. + ..@@@@@@@@@@.. + ...@@@@@@@@@.. + .............. + .............. + .............. + .............. + .............. + .............. + +u+0062: +"b": + .............. + .............. + .............. + .............. + .@@........... + .@@........... + .@@........... + .@@........... + .@@........... + .@@@@@@@@@.... + .@@@@@@@@@@... + .@@......@@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@......@@@.. + .@@@@@@@@@@... + .@@@@@@@@@.... + .............. + .............. + .............. + .............. + .............. + .............. + +u+0063: +"c": + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + ...@@@@@@@.... + ..@@@@@@@@@... + .@@@.....@@@.. + .@@........... + .@@........... + .@@........... + .@@........... + .@@........... + .@@........... + .@@........... + .@@@.....@@@.. + ..@@@@@@@@@... + ...@@@@@@@.... + .............. + .............. + .............. + .............. + .............. + .............. + +u+0064: +"d": + .............. + .............. + .............. + .............. + ..........@@.. + ..........@@.. + ..........@@.. + ..........@@.. + ..........@@.. + ...@@@@@@@@@.. + ..@@@@@@@@@@.. + .@@@......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@@......@@.. + ..@@@@@@@@@@.. + ...@@@@@@@@@.. + .............. + .............. + .............. + .............. + .............. + .............. + +u+0065: +"e": + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + ...@@@@@@@.... + ..@@@@@@@@@... + .@@@.....@@@.. + .@@.......@@.. + .@@.......@@.. + .@@@@@@@@@@@.. + .@@@@@@@@@@@.. + .@@........... + .@@........... + .@@........... + .@@@.....@@@.. + ..@@@@@@@@@... + ...@@@@@@@.... + .............. + .............. + .............. + .............. + .............. + .............. + +u+0066: +"f": + .............. + .............. + .............. + .............. + ......@@@@@@.. + .....@@@@@@@.. + .....@@....... + .....@@....... + .....@@....... + ..@@@@@@@@.... + ..@@@@@@@@.... + .....@@....... + .....@@....... + .....@@....... + .....@@....... + .....@@....... + .....@@....... + .....@@....... + .....@@....... + .....@@....... + .....@@....... + .....@@....... + .............. + .............. + .............. + .............. + .............. + .............. + +u+0067: +"g": + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + ...@@@@@@@@@.. + ..@@@@@@@@@@.. + .@@@......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@@......@@.. + ..@@@@@@@@@@.. + ...@@@@@@@@@.. + ..........@@.. + ..........@@.. + .........@@@.. + ..@@@@@@@@@... + ..@@@@@@@@.... + .............. + +u+0068: +"h": + .............. + .............. + .............. + .............. + .@@........... + .@@........... + .@@........... + .@@........... + .@@........... + .@@@@@@@@@.... + .@@@@@@@@@@... + .@@......@@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .............. + .............. + .............. + .............. + .............. + .............. + +u+0069: +"i": + .............. + .............. + .............. + .............. + ......@@...... + ......@@...... + ......@@...... + .............. + .............. + ....@@@@...... + ....@@@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ....@@@@@@.... + ....@@@@@@.... + .............. + .............. + .............. + .............. + .............. + .............. + +u+006a: +"j": + .............. + .............. + .............. + .............. + .........@@... + .........@@... + .........@@... + .............. + .............. + .......@@@@... + .......@@@@... + .........@@... + .........@@... + .........@@... + .........@@... + .........@@... + .........@@... + .........@@... + .........@@... + .........@@... + .........@@... + .........@@... + ..@@.....@@... + ..@@.....@@... + ..@@@...@@@... + ...@@@@@@@.... + ....@@@@@..... + .............. + +u+006b: +"k": + .............. + .............. + .............. + .............. + ..@@.......... + ..@@.......... + ..@@.......... + ..@@.......... + ..@@.......... + ..@@.....@@@.. + ..@@....@@@... + ..@@...@@@.... + ..@@..@@@..... + ..@@.@@@...... + ..@@@@@....... + ..@@@@........ + ..@@@@@....... + ..@@.@@@...... + ..@@..@@@..... + ..@@...@@@.... + ..@@....@@@... + ..@@.....@@@.. + .............. + .............. + .............. + .............. + .............. + .............. + +u+006c: +"l": + .............. + .............. + .............. + .............. + ....@@@@...... + ....@@@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ....@@@@@@.... + ....@@@@@@.... + .............. + .............. + .............. + .............. + .............. + .............. + +u+006d: +"m": + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .@@@@@@@@@@... + .@@@@@@@@@@@.. + .@@...@@..@@@. + .@@...@@...@@. + .@@...@@...@@. + .@@...@@...@@. + .@@...@@...@@. + .@@...@@...@@. + .@@...@@...@@. + .@@...@@...@@. + .@@...@@...@@. + .@@...@@...@@. + .@@...@@...@@. + .............. + .............. + .............. + .............. + .............. + .............. + +u+006e: +"n": + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .@@@@@@@@@.... + .@@@@@@@@@@... + .@@......@@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .............. + .............. + .............. + .............. + .............. + .............. + +u+006f: +"o": + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + ...@@@@@@@.... + ..@@@@@@@@@... + .@@@.....@@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@@.....@@@.. + ..@@@@@@@@@... + ...@@@@@@@.... + .............. + .............. + .............. + .............. + .............. + .............. + +u+0070: +"p": + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .@@@@@@@@@.... + .@@@@@@@@@@... + .@@......@@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@......@@@.. + .@@@@@@@@@@... + .@@@@@@@@@.... + .@@........... + .@@........... + .@@........... + .@@........... + .@@........... + .............. + +u+0071: +"q": + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + ...@@@@@@@@@.. + ..@@@@@@@@@@.. + .@@@......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@@......@@.. + ..@@@@@@@@@@.. + ...@@@@@@@@@.. + ..........@@.. + ..........@@.. + ..........@@.. + ..........@@.. + ..........@@.. + .............. + +u+0072: +"r": + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .@@..@@@@@@@.. + .@@.@@@@@@@@.. + .@@@@@........ + .@@@@......... + .@@@.......... + .@@........... + .@@........... + .@@........... + .@@........... + .@@........... + .@@........... + .@@........... + .@@........... + .............. + .............. + .............. + .............. + .............. + .............. + +u+0073: +"s": + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + ...@@@@@@@.... + ..@@@@@@@@@... + .@@@.....@@@.. + .@@........... + .@@@.......... + ..@@@@@@@@.... + ...@@@@@@@@... + .........@@@.. + ..........@@.. + ..........@@.. + .@@@.....@@@.. + ..@@@@@@@@@... + ...@@@@@@@.... + .............. + .............. + .............. + .............. + .............. + .............. + +u+0074: +"t": + .............. + .............. + .............. + .............. + .....@@....... + .....@@....... + .....@@....... + .....@@....... + .....@@....... + ..@@@@@@@@.... + ..@@@@@@@@.... + .....@@....... + .....@@....... + .....@@....... + .....@@....... + .....@@....... + .....@@....... + .....@@....... + .....@@....... + .....@@....... + .....@@@@@@... + ......@@@@@... + .............. + .............. + .............. + .............. + .............. + .............. + +u+0075: +"u": + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@@......@@.. + ..@@@@@@@@@@.. + ...@@@@@@@@@.. + .............. + .............. + .............. + .............. + .............. + .............. + +u+0076: +"v": + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + ..@@.....@@... + ..@@.....@@... + ..@@.....@@... + ...@@...@@.... + ...@@...@@.... + ...@@...@@.... + ....@@.@@..... + ....@@.@@..... + .....@@@...... + .....@@@...... + .............. + .............. + .............. + .............. + .............. + .............. + +u+0077: +"w": + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .@@........@@. + .@@........@@. + .@@........@@. + .@@........@@. + .@@...@@...@@. + .@@...@@...@@. + .@@...@@...@@. + .@@...@@...@@. + .@@...@@...@@. + .@@...@@...@@. + .@@@..@@..@@@. + ..@@@@@@@@@@.. + ...@@@@@@@@... + .............. + .............. + .............. + .............. + .............. + .............. + +u+0078: +"x": + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .@@.......@@.. + .@@.......@@.. + .@@@.....@@@.. + ..@@@...@@@... + ...@@@.@@@.... + ....@@@@@..... + .....@@@...... + ....@@@@@..... + ...@@@.@@@.... + ..@@@...@@@... + .@@@.....@@@.. + .@@.......@@.. + .@@.......@@.. + .............. + .............. + .............. + .............. + .............. + .............. + +u+0079: +"y": + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@.......@@.. + .@@@......@@.. + ..@@@@@@@@@@.. + ...@@@@@@@@@.. + ..........@@.. + ..........@@.. + .........@@@.. + ..@@@@@@@@@... + ..@@@@@@@@.... + .............. + +u+007a: +"z": + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .@@@@@@@@@@@.. + .@@@@@@@@@@@.. + .........@@@.. + ........@@@... + .......@@@.... + ......@@@..... + .....@@@...... + ....@@@....... + ...@@@........ + ..@@@......... + .@@@.......... + .@@@@@@@@@@@.. + .@@@@@@@@@@@.. + .............. + .............. + .............. + .............. + .............. + .............. + +u+007b: +braceleft: + .............. + .............. + .............. + .............. + .......@@@.... + ......@@@@.... + .....@@@...... + .....@@....... + .....@@....... + .....@@....... + .....@@....... + .....@@....... + ...@@@........ + ...@@@........ + .....@@....... + .....@@....... + .....@@....... + .....@@....... + .....@@....... + .....@@@...... + ......@@@@.... + .......@@@.... + .............. + .............. + .............. + .............. + .............. + .............. + +u+007c: +bar: + .............. + .............. + .............. + .............. + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + .............. + .............. + .............. + .............. + .............. + .............. + +u+007d: +braceright: + .............. + .............. + .............. + .............. + ...@@@........ + ...@@@@....... + .....@@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + .......@@@.... + .......@@@.... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + ......@@...... + .....@@@...... + ...@@@@....... + ...@@@........ + .............. + .............. + .............. + .............. + .............. + .............. + +u+007e: +asciitilde: + .............. + .............. + ..@@@@....@@.. + .@@@@@@...@@.. + .@@..@@@..@@.. + .@@...@@@@@@.. + .@@....@@@@... + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + +u+00a0: +nbspace: + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. + .............. diff --git a/containers/test/fonts/terminus-ascii-bold-32px.yaff b/containers/test/fonts/terminus-32px.yaff similarity index 95% rename from containers/test/fonts/terminus-ascii-bold-32px.yaff rename to containers/test/fonts/terminus-32px.yaff index ed83209a8..685a7df71 100644 --- a/containers/test/fonts/terminus-ascii-bold-32px.yaff +++ b/containers/test/fonts/terminus-32px.yaff @@ -1,3383 +1,3418 @@ -name: Terminus Bold 16x32 -spacing: character-cell -cell-size: 16 32 -family: Terminus -foundry: xos4 -copyright: Copyright (C) 2020 Dimitar Toshkov Zhekov -notice: Licensed under the SIL Open Font License, Version 1.1 -point-size: 32 -weight: bold -slant: roman -setwidth: normal -dpi: 72 72 -average-width: 16 -ascent: 26 -descent: 6 -shift-up: -6 -encoding: iso10646-1 -default-char: u+fffd -min-word-space: 16 -converter: monobit v0.32 -source-name: ter-u32b.bdf -source-format: BDF v2.1 -history: load --format=bdf - -u+0000: -char0: - ................ - ................ - ................ - ................ - ................ - ................ - .@@@@@...@@@@@.. - .@@@@@...@@@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - ................ - ................ - ................ - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - ................ - ................ - ................ - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@@@...@@@@@.. - .@@@@@...@@@@@.. - ................ - ................ - ................ - ................ - ................ - ................ - -u+0020: -space: - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - -u+0021: -exclam: - ................ - ................ - ................ - ................ - ................ - ................ - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ................ - ................ - ................ - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ................ - ................ - ................ - ................ - ................ - ................ - -u+0022: -quotedbl: - ................ - ................ - ................ - ................ - ...@@@...@@@.... - ...@@@...@@@.... - ...@@@...@@@.... - ...@@@...@@@.... - ...@@@...@@@.... - ...@@@...@@@.... - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - -u+0023: -numbersign: - ................ - ................ - ................ - ................ - ................ - ................ - ...@@@...@@@.... - ...@@@...@@@.... - ...@@@...@@@.... - ...@@@...@@@.... - ...@@@...@@@.... - .@@@@@@@@@@@@@.. - .@@@@@@@@@@@@@.. - ...@@@...@@@.... - ...@@@...@@@.... - ...@@@...@@@.... - ...@@@...@@@.... - ...@@@...@@@.... - ...@@@...@@@.... - .@@@@@@@@@@@@@.. - .@@@@@@@@@@@@@.. - ...@@@...@@@.... - ...@@@...@@@.... - ...@@@...@@@.... - ...@@@...@@@.... - ...@@@...@@@.... - ................ - ................ - ................ - ................ - ................ - ................ - -u+0024: -dollar: - ................ - ................ - ................ - ................ - ......@@@....... - ......@@@....... - ......@@@....... - ...@@@@@@@@@.... - ..@@@@@@@@@@@... - .@@@@.@@@.@@@@.. - .@@@..@@@..@@@.. - .@@@..@@@....... - .@@@..@@@....... - .@@@..@@@....... - .@@@@.@@@....... - ..@@@@@@@@@@.... - ...@@@@@@@@@@... - ......@@@.@@@@.. - ......@@@..@@@.. - ......@@@..@@@.. - ......@@@..@@@.. - .@@@..@@@..@@@.. - .@@@@.@@@.@@@@.. - ..@@@@@@@@@@@... - ...@@@@@@@@@.... - ......@@@....... - ......@@@....... - ......@@@....... - ................ - ................ - ................ - ................ - -u+0025: -percent: - ................ - ................ - ................ - ................ - ................ - ................ - ...@@@@@...@@@.. - ..@@@@@@@..@@@.. - ..@@@.@@@.@@@... - ..@@@.@@@.@@@... - ..@@@@@@@@@@.... - ...@@@@@.@@@.... - ........@@@..... - ........@@@..... - .......@@@...... - .......@@@...... - ......@@@....... - ......@@@....... - .....@@@........ - .....@@@........ - ....@@@.@@@@@... - ....@@@@@@@@@@.. - ...@@@.@@@.@@@.. - ...@@@.@@@.@@@.. - ..@@@..@@@@@@@.. - ..@@@...@@@@@... - ................ - ................ - ................ - ................ - ................ - ................ - -u+0026: -ampersand: - ................ - ................ - ................ - ................ - ................ - ................ - ....@@@@@@...... - ...@@@@@@@@..... - ..@@@....@@@.... - ..@@@....@@@.... - ..@@@....@@@.... - ..@@@....@@@.... - ..@@@....@@@.... - ...@@@..@@@..... - ....@@@@@@...... - ....@@@@@....... - ...@@@@@@@..@@@. - ..@@@...@@@.@@@. - .@@@.....@@@@@.. - .@@@......@@@... - .@@@......@@@... - .@@@......@@@... - .@@@......@@@... - .@@@@....@@@@@.. - ..@@@@@@@@@.@@@. - ...@@@@@@@..@@@. - ................ - ................ - ................ - ................ - ................ - ................ - -u+0027: -quotesingle: - ................ - ................ - ................ - ................ - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - -u+0028: -parenleft: - ................ - ................ - ................ - ................ - ................ - ................ - ........@@@..... - .......@@@...... - ......@@@....... - .....@@@........ - .....@@@........ - ....@@@......... - ....@@@......... - ....@@@......... - ....@@@......... - ....@@@......... - ....@@@......... - ....@@@......... - ....@@@......... - ....@@@......... - ....@@@......... - .....@@@........ - .....@@@........ - ......@@@....... - .......@@@...... - ........@@@..... - ................ - ................ - ................ - ................ - ................ - ................ - -u+0029: -parenright: - ................ - ................ - ................ - ................ - ................ - ................ - ....@@@......... - .....@@@........ - ......@@@....... - .......@@@...... - .......@@@...... - ........@@@..... - ........@@@..... - ........@@@..... - ........@@@..... - ........@@@..... - ........@@@..... - ........@@@..... - ........@@@..... - ........@@@..... - ........@@@..... - .......@@@...... - .......@@@...... - ......@@@....... - .....@@@........ - ....@@@......... - ................ - ................ - ................ - ................ - ................ - ................ - -u+002a: -asterisk: - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ..@@@.....@@@... - ...@@@...@@@.... - ....@@@.@@@..... - .....@@@@@...... - ......@@@....... - .@@@@@@@@@@@@@.. - .@@@@@@@@@@@@@.. - ......@@@....... - .....@@@@@...... - ....@@@.@@@..... - ...@@@...@@@.... - ..@@@.....@@@... - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - -u+002b: -plus: - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - .@@@@@@@@@@@@@.. - .@@@@@@@@@@@@@.. - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - -u+002c: -comma: - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - .....@@@........ - ....@@@......... - ................ - ................ - ................ - ................ - -u+002d: -hyphen: - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - .@@@@@@@@@@@@@.. - .@@@@@@@@@@@@@.. - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - -u+002e: -period: - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ................ - ................ - ................ - ................ - ................ - ................ - -u+002f: -slash: - ................ - ................ - ................ - ................ - ................ - ................ - ...........@@@.. - ...........@@@.. - ..........@@@... - ..........@@@... - .........@@@.... - .........@@@.... - ........@@@..... - ........@@@..... - .......@@@...... - .......@@@...... - ......@@@....... - ......@@@....... - .....@@@........ - .....@@@........ - ....@@@......... - ....@@@......... - ...@@@.......... - ...@@@.......... - ..@@@........... - ..@@@........... - ................ - ................ - ................ - ................ - ................ - ................ - -u+0030: -zero: - ................ - ................ - ................ - ................ - ................ - ................ - ...@@@@@@@@@.... - ..@@@@@@@@@@@... - .@@@@.....@@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@......@@@@.. - .@@@.....@@@@@.. - .@@@....@@@@@@.. - .@@@...@@@.@@@.. - .@@@..@@@..@@@.. - .@@@.@@@...@@@.. - .@@@@@@....@@@.. - .@@@@@.....@@@.. - .@@@@......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@@.....@@@@.. - ..@@@@@@@@@@@... - ...@@@@@@@@@.... - ................ - ................ - ................ - ................ - ................ - ................ - -u+0031: -one: - ................ - ................ - ................ - ................ - ................ - ................ - ......@@@....... - .....@@@@....... - ....@@@@@....... - ...@@@@@@....... - ...@@@@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ...@@@@@@@@@.... - ...@@@@@@@@@.... - ................ - ................ - ................ - ................ - ................ - ................ - -u+0032: -two: - ................ - ................ - ................ - ................ - ................ - ................ - ...@@@@@@@@@.... - ..@@@@@@@@@@@... - .@@@@.....@@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - ...........@@@.. - ..........@@@... - .........@@@.... - ........@@@..... - .......@@@...... - ......@@@....... - .....@@@........ - ....@@@......... - ...@@@.......... - ..@@@........... - .@@@............ - .@@@@@@@@@@@@@.. - .@@@@@@@@@@@@@.. - ................ - ................ - ................ - ................ - ................ - ................ - -u+0033: -three: - ................ - ................ - ................ - ................ - ................ - ................ - ...@@@@@@@@@.... - ..@@@@@@@@@@@... - .@@@@.....@@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - ...........@@@.. - ...........@@@.. - ...........@@@.. - ..........@@@@.. - ....@@@@@@@@@... - ....@@@@@@@@@... - ..........@@@@.. - ...........@@@.. - ...........@@@.. - ...........@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@@.....@@@@.. - ..@@@@@@@@@@@... - ...@@@@@@@@@.... - ................ - ................ - ................ - ................ - ................ - ................ - -u+0034: -four: - ................ - ................ - ................ - ................ - ................ - ................ - ...........@@@.. - ..........@@@@.. - .........@@@@@.. - ........@@@@@@.. - .......@@@.@@@.. - ......@@@..@@@.. - .....@@@...@@@.. - ....@@@....@@@.. - ...@@@.....@@@.. - ..@@@......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@@@@@@@@@@@.. - .@@@@@@@@@@@@@.. - ...........@@@.. - ...........@@@.. - ...........@@@.. - ...........@@@.. - ...........@@@.. - ................ - ................ - ................ - ................ - ................ - ................ - -u+0035: -five: - ................ - ................ - ................ - ................ - ................ - ................ - .@@@@@@@@@@@@@.. - .@@@@@@@@@@@@@.. - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@@@@@@@@@.... - .@@@@@@@@@@@@... - ..........@@@@.. - ...........@@@.. - ...........@@@.. - ...........@@@.. - ...........@@@.. - ...........@@@.. - .@@@.......@@@.. - .@@@@......@@@.. - ..@@@@@@@@@@@... - ...@@@@@@@@@.... - ................ - ................ - ................ - ................ - ................ - ................ - -u+0036: -six: - ................ - ................ - ................ - ................ - ................ - ................ - ...@@@@@@@@@@... - ..@@@@@@@@@@@... - .@@@@........... - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@@@@@@@@@.... - .@@@@@@@@@@@@... - .@@@......@@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@@.....@@@@.. - ..@@@@@@@@@@@... - ...@@@@@@@@@.... - ................ - ................ - ................ - ................ - ................ - ................ - -u+0037: -seven: - ................ - ................ - ................ - ................ - ................ - ................ - .@@@@@@@@@@@@@.. - .@@@@@@@@@@@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@......@@@... - ..........@@@... - .........@@@.... - .........@@@.... - ........@@@..... - ........@@@..... - .......@@@...... - .......@@@...... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ................ - ................ - ................ - ................ - ................ - ................ - -u+0038: -eight: - ................ - ................ - ................ - ................ - ................ - ................ - ...@@@@@@@@@.... - ..@@@@@@@@@@@... - .@@@@.....@@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@@.....@@@@.. - ..@@@@@@@@@@@... - ..@@@@@@@@@@@... - .@@@@.....@@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@@.....@@@@.. - ..@@@@@@@@@@@... - ...@@@@@@@@@.... - ................ - ................ - ................ - ................ - ................ - ................ - -u+0039: -nine: - ................ - ................ - ................ - ................ - ................ - ................ - ...@@@@@@@@@.... - ..@@@@@@@@@@@... - .@@@@.....@@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@@......@@@.. - ..@@@@@@@@@@@@.. - ...@@@@@@@@@@@.. - ...........@@@.. - ...........@@@.. - ...........@@@.. - ...........@@@.. - ...........@@@.. - ..........@@@@.. - ..@@@@@@@@@@@... - ..@@@@@@@@@@.... - ................ - ................ - ................ - ................ - ................ - ................ - -u+003a: -colon: - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ................ - ................ - ................ - ................ - ................ - ................ - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ................ - ................ - ................ - ................ - ................ - ................ - -u+003b: -semicolon: - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ................ - ................ - ................ - ................ - ................ - ................ - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - .....@@@........ - ....@@@......... - ................ - ................ - ................ - ................ - -u+003c: -less: - ................ - ................ - ................ - ................ - ................ - ................ - ...........@@@.. - ..........@@@... - .........@@@.... - ........@@@..... - .......@@@...... - ......@@@....... - .....@@@........ - ....@@@......... - ...@@@.......... - ..@@@........... - ..@@@........... - ...@@@.......... - ....@@@......... - .....@@@........ - ......@@@....... - .......@@@...... - ........@@@..... - .........@@@.... - ..........@@@... - ...........@@@.. - ................ - ................ - ................ - ................ - ................ - ................ - -u+003d: -equal: - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - .@@@@@@@@@@@@@.. - .@@@@@@@@@@@@@.. - ................ - ................ - ................ - ................ - .@@@@@@@@@@@@@.. - .@@@@@@@@@@@@@.. - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - -u+003e: -greater: - ................ - ................ - ................ - ................ - ................ - ................ - ..@@@........... - ...@@@.......... - ....@@@......... - .....@@@........ - ......@@@....... - .......@@@...... - ........@@@..... - .........@@@.... - ..........@@@... - ...........@@@.. - ...........@@@.. - ..........@@@... - .........@@@.... - ........@@@..... - .......@@@...... - ......@@@....... - .....@@@........ - ....@@@......... - ...@@@.......... - ..@@@........... - ................ - ................ - ................ - ................ - ................ - ................ - -u+003f: -question: - ................ - ................ - ................ - ................ - ................ - ................ - ...@@@@@@@@@.... - ..@@@@@@@@@@@... - .@@@@.....@@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - ..........@@@... - .........@@@.... - ........@@@..... - .......@@@...... - ......@@@....... - ......@@@....... - ......@@@....... - ................ - ................ - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ................ - ................ - ................ - ................ - ................ - ................ - -u+0040: -at: - ................ - ................ - ................ - ................ - ................ - ................ - ...@@@@@@@@@@... - ..@@@@@@@@@@@@.. - .@@@@.......@@@. - .@@@.........@@. - .@@@...@@@@@@@@. - .@@@..@@@@@@@@@. - .@@@.@@@@...@@@. - .@@@.@@@....@@@. - .@@@.@@@....@@@. - .@@@.@@@....@@@. - .@@@.@@@....@@@. - .@@@.@@@....@@@. - .@@@.@@@....@@@. - .@@@.@@@@..@@@@. - .@@@..@@@@@@@@@. - .@@@...@@@@@.@@. - .@@@............ - .@@@@........... - ..@@@@@@@@@@@@@. - ...@@@@@@@@@@@@. - ................ - ................ - ................ - ................ - ................ - ................ - -u+0041: -"A": - ................ - ................ - ................ - ................ - ................ - ................ - ...@@@@@@@@@.... - ..@@@@@@@@@@@... - .@@@@.....@@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@@@@@@@@@@@.. - .@@@@@@@@@@@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - ................ - ................ - ................ - ................ - ................ - ................ - -u+0042: -"B": - ................ - ................ - ................ - ................ - ................ - ................ - .@@@@@@@@@@@.... - .@@@@@@@@@@@@... - .@@@......@@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@......@@@... - .@@@@@@@@@@@.... - .@@@@@@@@@@@.... - .@@@......@@@... - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@......@@@@.. - .@@@@@@@@@@@@... - .@@@@@@@@@@@.... - ................ - ................ - ................ - ................ - ................ - ................ - -u+0043: -"C": - ................ - ................ - ................ - ................ - ................ - ................ - ...@@@@@@@@@.... - ..@@@@@@@@@@@... - .@@@@.....@@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@.......@@@.. - .@@@.......@@@.. - .@@@@.....@@@@.. - ..@@@@@@@@@@@... - ...@@@@@@@@@.... - ................ - ................ - ................ - ................ - ................ - ................ - -u+0044: -"D": - ................ - ................ - ................ - ................ - ................ - ................ - .@@@@@@@@@...... - .@@@@@@@@@@@.... - .@@@.....@@@@... - .@@@......@@@... - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@......@@@... - .@@@.....@@@@... - .@@@@@@@@@@@.... - .@@@@@@@@@...... - ................ - ................ - ................ - ................ - ................ - ................ - -u+0045: -"E": - ................ - ................ - ................ - ................ - ................ - ................ - .@@@@@@@@@@@@@.. - .@@@@@@@@@@@@@.. - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@@@@@@@@..... - .@@@@@@@@@@..... - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@@@@@@@@@@@.. - .@@@@@@@@@@@@@.. - ................ - ................ - ................ - ................ - ................ - ................ - -u+0046: -"F": - ................ - ................ - ................ - ................ - ................ - ................ - .@@@@@@@@@@@@@.. - .@@@@@@@@@@@@@.. - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@@@@@@@@..... - .@@@@@@@@@@..... - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - ................ - ................ - ................ - ................ - ................ - ................ - -u+0047: -"G": - ................ - ................ - ................ - ................ - ................ - ................ - ...@@@@@@@@@.... - ..@@@@@@@@@@@... - .@@@@.....@@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@...@@@@@@@.. - .@@@...@@@@@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@@.....@@@@.. - ..@@@@@@@@@@@... - ...@@@@@@@@@.... - ................ - ................ - ................ - ................ - ................ - ................ - -u+0048: -"H": - ................ - ................ - ................ - ................ - ................ - ................ - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@@@@@@@@@@@.. - .@@@@@@@@@@@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - ................ - ................ - ................ - ................ - ................ - ................ - -u+0049: -"I": - ................ - ................ - ................ - ................ - ................ - ................ - ....@@@@@@@..... - ....@@@@@@@..... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ....@@@@@@@..... - ....@@@@@@@..... - ................ - ................ - ................ - ................ - ................ - ................ - -u+004a: -"J": - ................ - ................ - ................ - ................ - ................ - ................ - ........@@@@@@@. - ........@@@@@@@. - ..........@@@... - ..........@@@... - ..........@@@... - ..........@@@... - ..........@@@... - ..........@@@... - ..........@@@... - ..........@@@... - ..........@@@... - ..........@@@... - ..........@@@... - ..........@@@... - .@@@......@@@... - .@@@......@@@... - .@@@......@@@... - .@@@@....@@@@... - ..@@@@@@@@@@.... - ...@@@@@@@@..... - ................ - ................ - ................ - ................ - ................ - ................ - -u+004b: -"K": - ................ - ................ - ................ - ................ - ................ - ................ - .@@@........@@.. - .@@@.......@@@.. - .@@@......@@@... - .@@@.....@@@.... - .@@@....@@@..... - .@@@...@@@...... - .@@@..@@@....... - .@@@.@@@........ - .@@@@@@......... - .@@@@@.......... - .@@@@@.......... - .@@@@@@......... - .@@@.@@@........ - .@@@..@@@....... - .@@@...@@@...... - .@@@....@@@..... - .@@@.....@@@.... - .@@@......@@@... - .@@@.......@@@.. - .@@@........@@.. - ................ - ................ - ................ - ................ - ................ - ................ - -u+004c: -"L": - ................ - ................ - ................ - ................ - ................ - ................ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@@@@@@@@@@@.. - .@@@@@@@@@@@@@.. - ................ - ................ - ................ - ................ - ................ - ................ - -u+004d: -"M": - ................ - ................ - ................ - ................ - ................ - ................ - .@@@........@@@. - .@@@........@@@. - .@@@@......@@@@. - .@@@@@....@@@@@. - .@@@@@@..@@@@@@. - .@@@@@@..@@@@@@. - .@@@.@@@@@@.@@@. - .@@@..@@@@..@@@. - .@@@..@@@@..@@@. - .@@@...@@...@@@. - .@@@........@@@. - .@@@........@@@. - .@@@........@@@. - .@@@........@@@. - .@@@........@@@. - .@@@........@@@. - .@@@........@@@. - .@@@........@@@. - .@@@........@@@. - .@@@........@@@. - ................ - ................ - ................ - ................ - ................ - ................ - -u+004e: -"N": - ................ - ................ - ................ - ................ - ................ - ................ - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@@......@@@.. - .@@@@@.....@@@.. - .@@@@@@....@@@.. - .@@@.@@@...@@@.. - .@@@..@@@..@@@.. - .@@@...@@@.@@@.. - .@@@....@@@@@@.. - .@@@.....@@@@@.. - .@@@......@@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - ................ - ................ - ................ - ................ - ................ - ................ - -u+004f: -"O": - ................ - ................ - ................ - ................ - ................ - ................ - ...@@@@@@@@@.... - ..@@@@@@@@@@@... - .@@@@.....@@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@@.....@@@@.. - ..@@@@@@@@@@@... - ...@@@@@@@@@.... - ................ - ................ - ................ - ................ - ................ - ................ - -u+0050: -"P": - ................ - ................ - ................ - ................ - ................ - ................ - .@@@@@@@@@@@.... - .@@@@@@@@@@@@... - .@@@......@@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@......@@@@.. - .@@@@@@@@@@@@... - .@@@@@@@@@@@.... - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - ................ - ................ - ................ - ................ - ................ - ................ - -u+0051: -"Q": - ................ - ................ - ................ - ................ - ................ - ................ - ...@@@@@@@@@.... - ..@@@@@@@@@@@... - .@@@@.....@@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@..@@@..@@@.. - .@@@@..@@@@@@@.. - ..@@@@@@@@@@@... - ...@@@@@@@@@.... - ..........@@@... - ...........@@@.. - ................ - ................ - ................ - ................ - -u+0052: -"R": - ................ - ................ - ................ - ................ - ................ - ................ - .@@@@@@@@@@@.... - .@@@@@@@@@@@@... - .@@@......@@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@......@@@@.. - .@@@@@@@@@@@@... - .@@@@@@@@@@@.... - .@@@@@@......... - .@@@.@@@........ - .@@@..@@@....... - .@@@...@@@...... - .@@@....@@@..... - .@@@.....@@@.... - .@@@......@@@... - .@@@.......@@@.. - ................ - ................ - ................ - ................ - ................ - ................ - -u+0053: -"S": - ................ - ................ - ................ - ................ - ................ - ................ - ...@@@@@@@@@.... - ..@@@@@@@@@@@... - .@@@@.....@@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@............ - .@@@............ - .@@@............ - .@@@@........... - ..@@@@@@@@@@.... - ...@@@@@@@@@@... - ..........@@@@.. - ...........@@@.. - ...........@@@.. - ...........@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@@.....@@@@.. - ..@@@@@@@@@@@... - ...@@@@@@@@@.... - ................ - ................ - ................ - ................ - ................ - ................ - -u+0054: -"T": - ................ - ................ - ................ - ................ - ................ - ................ - .@@@@@@@@@@@@@.. - .@@@@@@@@@@@@@.. - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ................ - ................ - ................ - ................ - ................ - ................ - -u+0055: -"U": - ................ - ................ - ................ - ................ - ................ - ................ - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@@.....@@@@.. - ..@@@@@@@@@@@... - ...@@@@@@@@@.... - ................ - ................ - ................ - ................ - ................ - ................ - -u+0056: -"V": - ................ - ................ - ................ - ................ - ................ - ................ - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - ..@@@.....@@@... - ..@@@.....@@@... - ..@@@.....@@@... - ..@@@.....@@@... - ..@@@.....@@@... - ...@@@...@@@.... - ...@@@...@@@.... - ...@@@...@@@.... - ...@@@...@@@.... - ....@@@.@@@..... - ....@@@.@@@..... - ....@@@.@@@..... - .....@@@@@...... - .....@@@@@...... - .....@@@@@...... - ................ - ................ - ................ - ................ - ................ - ................ - -u+0057: -"W": - ................ - ................ - ................ - ................ - ................ - ................ - .@@@........@@@. - .@@@........@@@. - .@@@........@@@. - .@@@........@@@. - .@@@........@@@. - .@@@........@@@. - .@@@........@@@. - .@@@........@@@. - .@@@........@@@. - .@@@........@@@. - .@@@...@@...@@@. - .@@@..@@@@..@@@. - .@@@..@@@@..@@@. - .@@@.@@@@@@.@@@. - .@@@@@@..@@@@@@. - .@@@@@@..@@@@@@. - .@@@@@....@@@@@. - .@@@@......@@@@. - .@@@........@@@. - .@@@........@@@. - ................ - ................ - ................ - ................ - ................ - ................ - -u+0058: -"X": - ................ - ................ - ................ - ................ - ................ - ................ - .@@@.......@@@.. - .@@@.......@@@.. - ..@@@.....@@@... - ..@@@.....@@@... - ...@@@...@@@.... - ...@@@...@@@.... - ....@@@.@@@..... - ....@@@.@@@..... - .....@@@@@...... - .....@@@@@...... - .....@@@@@...... - .....@@@@@...... - ....@@@.@@@..... - ....@@@.@@@..... - ...@@@...@@@.... - ...@@@...@@@.... - ..@@@.....@@@... - ..@@@.....@@@... - .@@@.......@@@.. - .@@@.......@@@.. - ................ - ................ - ................ - ................ - ................ - ................ - -u+0059: -"Y": - ................ - ................ - ................ - ................ - ................ - ................ - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - ..@@@.....@@@... - ..@@@.....@@@... - ...@@@...@@@.... - ...@@@...@@@.... - ....@@@.@@@..... - ....@@@.@@@..... - .....@@@@@...... - .....@@@@@...... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ................ - ................ - ................ - ................ - ................ - ................ - -u+005a: -"Z": - ................ - ................ - ................ - ................ - ................ - ................ - .@@@@@@@@@@@@@.. - .@@@@@@@@@@@@@.. - ...........@@@.. - ...........@@@.. - ...........@@@.. - ..........@@@... - .........@@@.... - ........@@@..... - .......@@@...... - ......@@@....... - .....@@@........ - ....@@@......... - ...@@@.......... - ..@@@........... - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@@@@@@@@@@@.. - .@@@@@@@@@@@@@.. - ................ - ................ - ................ - ................ - ................ - ................ - -u+005b: -bracketleft: - ................ - ................ - ................ - ................ - ................ - ................ - ....@@@@@@@@.... - ....@@@@@@@@.... - ....@@@......... - ....@@@......... - ....@@@......... - ....@@@......... - ....@@@......... - ....@@@......... - ....@@@......... - ....@@@......... - ....@@@......... - ....@@@......... - ....@@@......... - ....@@@......... - ....@@@......... - ....@@@......... - ....@@@......... - ....@@@......... - ....@@@@@@@@.... - ....@@@@@@@@.... - ................ - ................ - ................ - ................ - ................ - ................ - -u+005c: -backslash: - ................ - ................ - ................ - ................ - ................ - ................ - ..@@@........... - ..@@@........... - ...@@@.......... - ...@@@.......... - ....@@@......... - ....@@@......... - .....@@@........ - .....@@@........ - ......@@@....... - ......@@@....... - .......@@@...... - .......@@@...... - ........@@@..... - ........@@@..... - .........@@@.... - .........@@@.... - ..........@@@... - ..........@@@... - ...........@@@.. - ...........@@@.. - ................ - ................ - ................ - ................ - ................ - ................ - -u+005d: -bracketright: - ................ - ................ - ................ - ................ - ................ - ................ - ....@@@@@@@@.... - ....@@@@@@@@.... - .........@@@.... - .........@@@.... - .........@@@.... - .........@@@.... - .........@@@.... - .........@@@.... - .........@@@.... - .........@@@.... - .........@@@.... - .........@@@.... - .........@@@.... - .........@@@.... - .........@@@.... - .........@@@.... - .........@@@.... - .........@@@.... - ....@@@@@@@@.... - ....@@@@@@@@.... - ................ - ................ - ................ - ................ - ................ - ................ - -u+005e: -asciicircum: - ................ - ................ - ................ - ................ - ......@@@....... - .....@@@@@...... - ....@@@.@@@..... - ...@@@...@@@.... - ..@@@.....@@@... - .@@@.......@@@.. - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - -u+005f: -underscore: - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - .@@@@@@@@@@@@@.. - .@@@@@@@@@@@@@.. - ................ - ................ - ................ - -u+0060: -grave: - ................ - ................ - ...@@@.......... - ....@@@......... - .....@@@........ - ......@@@....... - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - -u+0061: -"a": - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ..@@@@@@@@@@.... - ..@@@@@@@@@@@... - ..........@@@@.. - ...........@@@.. - ...........@@@.. - ...@@@@@@@@@@@.. - ..@@@@@@@@@@@@.. - .@@@@......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@@......@@@.. - ..@@@@@@@@@@@@.. - ...@@@@@@@@@@@.. - ................ - ................ - ................ - ................ - ................ - ................ - -u+0062: -"b": - ................ - ................ - ................ - ................ - ................ - ................ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@@@@@@@@@.... - .@@@@@@@@@@@@... - .@@@......@@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@......@@@@.. - .@@@@@@@@@@@@... - .@@@@@@@@@@@.... - ................ - ................ - ................ - ................ - ................ - ................ - -u+0063: -"c": - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ...@@@@@@@@@.... - ..@@@@@@@@@@@... - .@@@@.....@@@@.. - .@@@.......@@@.. - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@.......@@@.. - .@@@@.....@@@@.. - ..@@@@@@@@@@@... - ...@@@@@@@@@.... - ................ - ................ - ................ - ................ - ................ - ................ - -u+0064: -"d": - ................ - ................ - ................ - ................ - ................ - ................ - ...........@@@.. - ...........@@@.. - ...........@@@.. - ...........@@@.. - ...........@@@.. - ...........@@@.. - ...@@@@@@@@@@@.. - ..@@@@@@@@@@@@.. - .@@@@......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@@......@@@.. - ..@@@@@@@@@@@@.. - ...@@@@@@@@@@@.. - ................ - ................ - ................ - ................ - ................ - ................ - -u+0065: -"e": - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ...@@@@@@@@@.... - ..@@@@@@@@@@@... - .@@@@.....@@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@@@@@@@@@@@.. - .@@@@@@@@@@@@@.. - .@@@............ - .@@@............ - .@@@............ - .@@@@......@@@.. - ..@@@@@@@@@@@@.. - ...@@@@@@@@@@... - ................ - ................ - ................ - ................ - ................ - ................ - -u+0066: -"f": - ................ - ................ - ................ - ................ - ................ - ................ - ........@@@@@@@. - .......@@@@@@@@. - ......@@@@...... - ......@@@....... - ......@@@....... - ......@@@....... - ..@@@@@@@@@@@... - ..@@@@@@@@@@@... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ................ - ................ - ................ - ................ - ................ - ................ - -u+0067: -"g": - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ...@@@@@@@@@@@.. - ..@@@@@@@@@@@@.. - .@@@@......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@@......@@@.. - ..@@@@@@@@@@@@.. - ...@@@@@@@@@@@.. - ...........@@@.. - ...........@@@.. - ..........@@@@.. - ..@@@@@@@@@@@... - ..@@@@@@@@@@.... - ................ - -u+0068: -"h": - ................ - ................ - ................ - ................ - ................ - ................ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@@@@@@@@@.... - .@@@@@@@@@@@@... - .@@@......@@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - ................ - ................ - ................ - ................ - ................ - ................ - -u+0069: -"i": - ................ - ................ - ................ - ................ - ................ - ................ - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ................ - ................ - ....@@@@@....... - ....@@@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ....@@@@@@@..... - ....@@@@@@@..... - ................ - ................ - ................ - ................ - ................ - ................ - -u+006a: -"j": - ................ - ................ - ................ - ................ - ................ - ................ - ..........@@@... - ..........@@@... - ..........@@@... - ..........@@@... - ................ - ................ - ........@@@@@... - ........@@@@@... - ..........@@@... - ..........@@@... - ..........@@@... - ..........@@@... - ..........@@@... - ..........@@@... - ..........@@@... - ..........@@@... - ..........@@@... - ..........@@@... - ..........@@@... - ..........@@@... - ..@@@.....@@@... - ..@@@.....@@@... - ..@@@@...@@@@... - ...@@@@@@@@@.... - ....@@@@@@@..... - ................ - -u+006b: -"k": - ................ - ................ - ................ - ................ - ................ - ................ - ..@@@........... - ..@@@........... - ..@@@........... - ..@@@........... - ..@@@........... - ..@@@........... - ..@@@......@@@.. - ..@@@.....@@@... - ..@@@....@@@.... - ..@@@...@@@..... - ..@@@..@@@...... - ..@@@.@@@....... - ..@@@@@@........ - ..@@@@@@........ - ..@@@.@@@....... - ..@@@..@@@...... - ..@@@...@@@..... - ..@@@....@@@.... - ..@@@.....@@@... - ..@@@......@@@.. - ................ - ................ - ................ - ................ - ................ - ................ - -u+006c: -"l": - ................ - ................ - ................ - ................ - ................ - ................ - ....@@@@@....... - ....@@@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ....@@@@@@@..... - ....@@@@@@@..... - ................ - ................ - ................ - ................ - ................ - ................ - -u+006d: -"m": - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - .@@@@@@@@@@@.... - .@@@@@@@@@@@@... - .@@@..@@@.@@@@.. - .@@@..@@@..@@@.. - .@@@..@@@..@@@.. - .@@@..@@@..@@@.. - .@@@..@@@..@@@.. - .@@@..@@@..@@@.. - .@@@..@@@..@@@.. - .@@@..@@@..@@@.. - .@@@..@@@..@@@.. - .@@@..@@@..@@@.. - .@@@..@@@..@@@.. - .@@@..@@@..@@@.. - ................ - ................ - ................ - ................ - ................ - ................ - -u+006e: -"n": - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - .@@@@@@@@@@@.... - .@@@@@@@@@@@@... - .@@@......@@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - ................ - ................ - ................ - ................ - ................ - ................ - -u+006f: -"o": - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ...@@@@@@@@@.... - ..@@@@@@@@@@@... - .@@@@.....@@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@@.....@@@@.. - ..@@@@@@@@@@@... - ...@@@@@@@@@.... - ................ - ................ - ................ - ................ - ................ - ................ - -u+0070: -"p": - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - .@@@@@@@@@@@.... - .@@@@@@@@@@@@... - .@@@......@@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@......@@@@.. - .@@@@@@@@@@@@... - .@@@@@@@@@@@.... - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - ................ - -u+0071: -"q": - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ...@@@@@@@@@@@.. - ..@@@@@@@@@@@@.. - .@@@@......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@@......@@@.. - ..@@@@@@@@@@@@.. - ...@@@@@@@@@@@.. - ...........@@@.. - ...........@@@.. - ...........@@@.. - ...........@@@.. - ...........@@@.. - ................ - -u+0072: -"r": - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - .@@@..@@@@@@@@.. - .@@@.@@@@@@@@@.. - .@@@@@@......... - .@@@@@.......... - .@@@@........... - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - .@@@............ - ................ - ................ - ................ - ................ - ................ - ................ - -u+0073: -"s": - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ...@@@@@@@@@.... - ..@@@@@@@@@@@... - .@@@@.....@@@@.. - .@@@............ - .@@@............ - .@@@@........... - ..@@@@@@@@@@.... - ...@@@@@@@@@@... - ..........@@@@.. - ...........@@@.. - ...........@@@.. - .@@@@.....@@@@.. - ..@@@@@@@@@@@... - ...@@@@@@@@@.... - ................ - ................ - ................ - ................ - ................ - ................ - -u+0074: -"t": - ................ - ................ - ................ - ................ - ................ - ................ - .....@@@........ - .....@@@........ - .....@@@........ - .....@@@........ - .....@@@........ - .....@@@........ - .@@@@@@@@@@@.... - .@@@@@@@@@@@.... - .....@@@........ - .....@@@........ - .....@@@........ - .....@@@........ - .....@@@........ - .....@@@........ - .....@@@........ - .....@@@........ - .....@@@........ - .....@@@@....... - ......@@@@@@@@.. - .......@@@@@@@.. - ................ - ................ - ................ - ................ - ................ - ................ - -u+0075: -"u": - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@@......@@@.. - ..@@@@@@@@@@@@.. - ...@@@@@@@@@@@.. - ................ - ................ - ................ - ................ - ................ - ................ - -u+0076: -"v": - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - ..@@@.....@@@... - ..@@@.....@@@... - ..@@@.....@@@... - ...@@@...@@@.... - ...@@@...@@@.... - ...@@@...@@@.... - ....@@@.@@@..... - ....@@@.@@@..... - .....@@@@@...... - .....@@@@@...... - .....@@@@@...... - ................ - ................ - ................ - ................ - ................ - ................ - -u+0077: -"w": - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@..@@@..@@@.. - .@@@..@@@..@@@.. - .@@@..@@@..@@@.. - .@@@..@@@..@@@.. - .@@@..@@@..@@@.. - .@@@..@@@..@@@.. - .@@@..@@@..@@@.. - .@@@@.@@@.@@@@.. - ..@@@@@@@@@@@... - ...@@@@@@@@@.... - ................ - ................ - ................ - ................ - ................ - ................ - -u+0078: -"x": - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - ..@@@.....@@@... - ...@@@...@@@.... - ....@@@.@@@..... - .....@@@@@...... - .....@@@@@...... - ....@@@.@@@..... - ...@@@...@@@.... - ..@@@.....@@@... - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - ................ - ................ - ................ - ................ - ................ - ................ - -u+0079: -"y": - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@.......@@@.. - .@@@@......@@@.. - ..@@@@@@@@@@@@.. - ...@@@@@@@@@@@.. - ...........@@@.. - ...........@@@.. - ..........@@@@.. - ..@@@@@@@@@@@... - ..@@@@@@@@@@.... - ................ - -u+007a: -"z": - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - .@@@@@@@@@@@@@.. - .@@@@@@@@@@@@@.. - ..........@@@... - .........@@@.... - ........@@@..... - .......@@@...... - ......@@@....... - .....@@@........ - ....@@@......... - ...@@@.......... - ..@@@........... - .@@@............ - .@@@@@@@@@@@@@.. - .@@@@@@@@@@@@@.. - ................ - ................ - ................ - ................ - ................ - ................ - -u+007b: -braceleft: - ................ - ................ - ................ - ................ - ................ - ................ - .......@@@@@.... - ......@@@@@@.... - .....@@@@....... - .....@@@........ - .....@@@........ - .....@@@........ - .....@@@........ - .....@@@........ - .....@@@........ - ..@@@@@......... - ..@@@@@......... - .....@@@........ - .....@@@........ - .....@@@........ - .....@@@........ - .....@@@........ - .....@@@........ - .....@@@@....... - ......@@@@@@.... - .......@@@@@.... - ................ - ................ - ................ - ................ - ................ - ................ - -u+007c: -bar: - ................ - ................ - ................ - ................ - ................ - ................ - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ................ - ................ - ................ - ................ - ................ - ................ - -u+007d: -braceright: - ................ - ................ - ................ - ................ - ................ - ................ - ..@@@@@......... - ..@@@@@@........ - .....@@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - .......@@@@@.... - .......@@@@@.... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - ......@@@....... - .....@@@@....... - ..@@@@@@........ - ..@@@@@......... - ................ - ................ - ................ - ................ - ................ - ................ - -u+007e: -asciitilde: - ................ - ................ - ................ - ................ - ...@@@@....@@@.. - ..@@@@@@...@@@.. - .@@@.@@@@..@@@.. - .@@@..@@@@.@@@.. - .@@@...@@@@@@... - .@@@....@@@@.... - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ - ................ +name: Terminus Bold 16x32 +spacing: character-cell +cell-size: 16 32 +family: Terminus +foundry: xos4 +copyright: Copyright (C) 2020 Dimitar Toshkov Zhekov +notice: Licensed under the SIL Open Font License, Version 1.1 +point-size: 32 +weight: bold +slant: roman +setwidth: normal +dpi: 72 72 +average-width: 16 +ascent: 26 +descent: 6 +shift-up: -6 +encoding: iso10646-1 +default-char: u+fffd +min-word-space: 16 +converter: monobit v0.32 +source-name: ter-u32b.bdf +source-format: BDF v2.1 +history: load --format=bdf + +u+0000: +char0: + ................ + ................ + ................ + ................ + ................ + ................ + .@@@@@...@@@@@.. + .@@@@@...@@@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + ................ + ................ + ................ + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + ................ + ................ + ................ + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@@@...@@@@@.. + .@@@@@...@@@@@.. + ................ + ................ + ................ + ................ + ................ + ................ + +u+0020: +space: + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + +u+0021: +exclam: + ................ + ................ + ................ + ................ + ................ + ................ + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ................ + ................ + ................ + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ................ + ................ + ................ + ................ + ................ + ................ + +u+0022: +quotedbl: + ................ + ................ + ................ + ................ + ...@@@...@@@.... + ...@@@...@@@.... + ...@@@...@@@.... + ...@@@...@@@.... + ...@@@...@@@.... + ...@@@...@@@.... + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + +u+0023: +numbersign: + ................ + ................ + ................ + ................ + ................ + ................ + ...@@@...@@@.... + ...@@@...@@@.... + ...@@@...@@@.... + ...@@@...@@@.... + ...@@@...@@@.... + .@@@@@@@@@@@@@.. + .@@@@@@@@@@@@@.. + ...@@@...@@@.... + ...@@@...@@@.... + ...@@@...@@@.... + ...@@@...@@@.... + ...@@@...@@@.... + ...@@@...@@@.... + .@@@@@@@@@@@@@.. + .@@@@@@@@@@@@@.. + ...@@@...@@@.... + ...@@@...@@@.... + ...@@@...@@@.... + ...@@@...@@@.... + ...@@@...@@@.... + ................ + ................ + ................ + ................ + ................ + ................ + +u+0024: +dollar: + ................ + ................ + ................ + ................ + ......@@@....... + ......@@@....... + ......@@@....... + ...@@@@@@@@@.... + ..@@@@@@@@@@@... + .@@@@.@@@.@@@@.. + .@@@..@@@..@@@.. + .@@@..@@@....... + .@@@..@@@....... + .@@@..@@@....... + .@@@@.@@@....... + ..@@@@@@@@@@.... + ...@@@@@@@@@@... + ......@@@.@@@@.. + ......@@@..@@@.. + ......@@@..@@@.. + ......@@@..@@@.. + .@@@..@@@..@@@.. + .@@@@.@@@.@@@@.. + ..@@@@@@@@@@@... + ...@@@@@@@@@.... + ......@@@....... + ......@@@....... + ......@@@....... + ................ + ................ + ................ + ................ + +u+0025: +percent: + ................ + ................ + ................ + ................ + ................ + ................ + ...@@@@@...@@@.. + ..@@@@@@@..@@@.. + ..@@@.@@@.@@@... + ..@@@.@@@.@@@... + ..@@@@@@@@@@.... + ...@@@@@.@@@.... + ........@@@..... + ........@@@..... + .......@@@...... + .......@@@...... + ......@@@....... + ......@@@....... + .....@@@........ + .....@@@........ + ....@@@.@@@@@... + ....@@@@@@@@@@.. + ...@@@.@@@.@@@.. + ...@@@.@@@.@@@.. + ..@@@..@@@@@@@.. + ..@@@...@@@@@... + ................ + ................ + ................ + ................ + ................ + ................ + +u+0026: +ampersand: + ................ + ................ + ................ + ................ + ................ + ................ + ....@@@@@@...... + ...@@@@@@@@..... + ..@@@....@@@.... + ..@@@....@@@.... + ..@@@....@@@.... + ..@@@....@@@.... + ..@@@....@@@.... + ...@@@..@@@..... + ....@@@@@@...... + ....@@@@@....... + ...@@@@@@@..@@@. + ..@@@...@@@.@@@. + .@@@.....@@@@@.. + .@@@......@@@... + .@@@......@@@... + .@@@......@@@... + .@@@......@@@... + .@@@@....@@@@@.. + ..@@@@@@@@@.@@@. + ...@@@@@@@..@@@. + ................ + ................ + ................ + ................ + ................ + ................ + +u+0027: +quotesingle: + ................ + ................ + ................ + ................ + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + +u+0028: +parenleft: + ................ + ................ + ................ + ................ + ................ + ................ + ........@@@..... + .......@@@...... + ......@@@....... + .....@@@........ + .....@@@........ + ....@@@......... + ....@@@......... + ....@@@......... + ....@@@......... + ....@@@......... + ....@@@......... + ....@@@......... + ....@@@......... + ....@@@......... + ....@@@......... + .....@@@........ + .....@@@........ + ......@@@....... + .......@@@...... + ........@@@..... + ................ + ................ + ................ + ................ + ................ + ................ + +u+0029: +parenright: + ................ + ................ + ................ + ................ + ................ + ................ + ....@@@......... + .....@@@........ + ......@@@....... + .......@@@...... + .......@@@...... + ........@@@..... + ........@@@..... + ........@@@..... + ........@@@..... + ........@@@..... + ........@@@..... + ........@@@..... + ........@@@..... + ........@@@..... + ........@@@..... + .......@@@...... + .......@@@...... + ......@@@....... + .....@@@........ + ....@@@......... + ................ + ................ + ................ + ................ + ................ + ................ + +u+002a: +asterisk: + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ..@@@.....@@@... + ...@@@...@@@.... + ....@@@.@@@..... + .....@@@@@...... + ......@@@....... + .@@@@@@@@@@@@@.. + .@@@@@@@@@@@@@.. + ......@@@....... + .....@@@@@...... + ....@@@.@@@..... + ...@@@...@@@.... + ..@@@.....@@@... + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + +u+002b: +plus: + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + .@@@@@@@@@@@@@.. + .@@@@@@@@@@@@@.. + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + +u+002c: +comma: + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + .....@@@........ + ....@@@......... + ................ + ................ + ................ + ................ + +u+002d: +hyphen: + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + .@@@@@@@@@@@@@.. + .@@@@@@@@@@@@@.. + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + +u+002e: +period: + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ................ + ................ + ................ + ................ + ................ + ................ + +u+002f: +slash: + ................ + ................ + ................ + ................ + ................ + ................ + ...........@@@.. + ...........@@@.. + ..........@@@... + ..........@@@... + .........@@@.... + .........@@@.... + ........@@@..... + ........@@@..... + .......@@@...... + .......@@@...... + ......@@@....... + ......@@@....... + .....@@@........ + .....@@@........ + ....@@@......... + ....@@@......... + ...@@@.......... + ...@@@.......... + ..@@@........... + ..@@@........... + ................ + ................ + ................ + ................ + ................ + ................ + +u+0030: +zero: + ................ + ................ + ................ + ................ + ................ + ................ + ...@@@@@@@@@.... + ..@@@@@@@@@@@... + .@@@@.....@@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@......@@@@.. + .@@@.....@@@@@.. + .@@@....@@@@@@.. + .@@@...@@@.@@@.. + .@@@..@@@..@@@.. + .@@@.@@@...@@@.. + .@@@@@@....@@@.. + .@@@@@.....@@@.. + .@@@@......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@@.....@@@@.. + ..@@@@@@@@@@@... + ...@@@@@@@@@.... + ................ + ................ + ................ + ................ + ................ + ................ + +u+0031: +one: + ................ + ................ + ................ + ................ + ................ + ................ + ......@@@....... + .....@@@@....... + ....@@@@@....... + ...@@@@@@....... + ...@@@@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ...@@@@@@@@@.... + ...@@@@@@@@@.... + ................ + ................ + ................ + ................ + ................ + ................ + +u+0032: +two: + ................ + ................ + ................ + ................ + ................ + ................ + ...@@@@@@@@@.... + ..@@@@@@@@@@@... + .@@@@.....@@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + ...........@@@.. + ..........@@@... + .........@@@.... + ........@@@..... + .......@@@...... + ......@@@....... + .....@@@........ + ....@@@......... + ...@@@.......... + ..@@@........... + .@@@............ + .@@@@@@@@@@@@@.. + .@@@@@@@@@@@@@.. + ................ + ................ + ................ + ................ + ................ + ................ + +u+0033: +three: + ................ + ................ + ................ + ................ + ................ + ................ + ...@@@@@@@@@.... + ..@@@@@@@@@@@... + .@@@@.....@@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + ...........@@@.. + ...........@@@.. + ...........@@@.. + ..........@@@@.. + ....@@@@@@@@@... + ....@@@@@@@@@... + ..........@@@@.. + ...........@@@.. + ...........@@@.. + ...........@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@@.....@@@@.. + ..@@@@@@@@@@@... + ...@@@@@@@@@.... + ................ + ................ + ................ + ................ + ................ + ................ + +u+0034: +four: + ................ + ................ + ................ + ................ + ................ + ................ + ...........@@@.. + ..........@@@@.. + .........@@@@@.. + ........@@@@@@.. + .......@@@.@@@.. + ......@@@..@@@.. + .....@@@...@@@.. + ....@@@....@@@.. + ...@@@.....@@@.. + ..@@@......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@@@@@@@@@@@.. + .@@@@@@@@@@@@@.. + ...........@@@.. + ...........@@@.. + ...........@@@.. + ...........@@@.. + ...........@@@.. + ................ + ................ + ................ + ................ + ................ + ................ + +u+0035: +five: + ................ + ................ + ................ + ................ + ................ + ................ + .@@@@@@@@@@@@@.. + .@@@@@@@@@@@@@.. + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@@@@@@@@@.... + .@@@@@@@@@@@@... + ..........@@@@.. + ...........@@@.. + ...........@@@.. + ...........@@@.. + ...........@@@.. + ...........@@@.. + .@@@.......@@@.. + .@@@@......@@@.. + ..@@@@@@@@@@@... + ...@@@@@@@@@.... + ................ + ................ + ................ + ................ + ................ + ................ + +u+0036: +six: + ................ + ................ + ................ + ................ + ................ + ................ + ...@@@@@@@@@@... + ..@@@@@@@@@@@... + .@@@@........... + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@@@@@@@@@.... + .@@@@@@@@@@@@... + .@@@......@@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@@.....@@@@.. + ..@@@@@@@@@@@... + ...@@@@@@@@@.... + ................ + ................ + ................ + ................ + ................ + ................ + +u+0037: +seven: + ................ + ................ + ................ + ................ + ................ + ................ + .@@@@@@@@@@@@@.. + .@@@@@@@@@@@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@......@@@... + ..........@@@... + .........@@@.... + .........@@@.... + ........@@@..... + ........@@@..... + .......@@@...... + .......@@@...... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ................ + ................ + ................ + ................ + ................ + ................ + +u+0038: +eight: + ................ + ................ + ................ + ................ + ................ + ................ + ...@@@@@@@@@.... + ..@@@@@@@@@@@... + .@@@@.....@@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@@.....@@@@.. + ..@@@@@@@@@@@... + ..@@@@@@@@@@@... + .@@@@.....@@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@@.....@@@@.. + ..@@@@@@@@@@@... + ...@@@@@@@@@.... + ................ + ................ + ................ + ................ + ................ + ................ + +u+0039: +nine: + ................ + ................ + ................ + ................ + ................ + ................ + ...@@@@@@@@@.... + ..@@@@@@@@@@@... + .@@@@.....@@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@@......@@@.. + ..@@@@@@@@@@@@.. + ...@@@@@@@@@@@.. + ...........@@@.. + ...........@@@.. + ...........@@@.. + ...........@@@.. + ...........@@@.. + ..........@@@@.. + ..@@@@@@@@@@@... + ..@@@@@@@@@@.... + ................ + ................ + ................ + ................ + ................ + ................ + +u+003a: +colon: + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ................ + ................ + ................ + ................ + ................ + ................ + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ................ + ................ + ................ + ................ + ................ + ................ + +u+003b: +semicolon: + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ................ + ................ + ................ + ................ + ................ + ................ + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + .....@@@........ + ....@@@......... + ................ + ................ + ................ + ................ + +u+003c: +less: + ................ + ................ + ................ + ................ + ................ + ................ + ...........@@@.. + ..........@@@... + .........@@@.... + ........@@@..... + .......@@@...... + ......@@@....... + .....@@@........ + ....@@@......... + ...@@@.......... + ..@@@........... + ..@@@........... + ...@@@.......... + ....@@@......... + .....@@@........ + ......@@@....... + .......@@@...... + ........@@@..... + .........@@@.... + ..........@@@... + ...........@@@.. + ................ + ................ + ................ + ................ + ................ + ................ + +u+003d: +equal: + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + .@@@@@@@@@@@@@.. + .@@@@@@@@@@@@@.. + ................ + ................ + ................ + ................ + .@@@@@@@@@@@@@.. + .@@@@@@@@@@@@@.. + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + +u+003e: +greater: + ................ + ................ + ................ + ................ + ................ + ................ + ..@@@........... + ...@@@.......... + ....@@@......... + .....@@@........ + ......@@@....... + .......@@@...... + ........@@@..... + .........@@@.... + ..........@@@... + ...........@@@.. + ...........@@@.. + ..........@@@... + .........@@@.... + ........@@@..... + .......@@@...... + ......@@@....... + .....@@@........ + ....@@@......... + ...@@@.......... + ..@@@........... + ................ + ................ + ................ + ................ + ................ + ................ + +u+003f: +question: + ................ + ................ + ................ + ................ + ................ + ................ + ...@@@@@@@@@.... + ..@@@@@@@@@@@... + .@@@@.....@@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + ..........@@@... + .........@@@.... + ........@@@..... + .......@@@...... + ......@@@....... + ......@@@....... + ......@@@....... + ................ + ................ + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ................ + ................ + ................ + ................ + ................ + ................ + +u+0040: +at: + ................ + ................ + ................ + ................ + ................ + ................ + ...@@@@@@@@@@... + ..@@@@@@@@@@@@.. + .@@@@.......@@@. + .@@@.........@@. + .@@@...@@@@@@@@. + .@@@..@@@@@@@@@. + .@@@.@@@@...@@@. + .@@@.@@@....@@@. + .@@@.@@@....@@@. + .@@@.@@@....@@@. + .@@@.@@@....@@@. + .@@@.@@@....@@@. + .@@@.@@@....@@@. + .@@@.@@@@..@@@@. + .@@@..@@@@@@@@@. + .@@@...@@@@@.@@. + .@@@............ + .@@@@........... + ..@@@@@@@@@@@@@. + ...@@@@@@@@@@@@. + ................ + ................ + ................ + ................ + ................ + ................ + +u+0041: +"A": + ................ + ................ + ................ + ................ + ................ + ................ + ...@@@@@@@@@.... + ..@@@@@@@@@@@... + .@@@@.....@@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@@@@@@@@@@@.. + .@@@@@@@@@@@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + ................ + ................ + ................ + ................ + ................ + ................ + +u+0042: +"B": + ................ + ................ + ................ + ................ + ................ + ................ + .@@@@@@@@@@@.... + .@@@@@@@@@@@@... + .@@@......@@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@......@@@... + .@@@@@@@@@@@.... + .@@@@@@@@@@@.... + .@@@......@@@... + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@......@@@@.. + .@@@@@@@@@@@@... + .@@@@@@@@@@@.... + ................ + ................ + ................ + ................ + ................ + ................ + +u+0043: +"C": + ................ + ................ + ................ + ................ + ................ + ................ + ...@@@@@@@@@.... + ..@@@@@@@@@@@... + .@@@@.....@@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@.......@@@.. + .@@@.......@@@.. + .@@@@.....@@@@.. + ..@@@@@@@@@@@... + ...@@@@@@@@@.... + ................ + ................ + ................ + ................ + ................ + ................ + +u+0044: +"D": + ................ + ................ + ................ + ................ + ................ + ................ + .@@@@@@@@@...... + .@@@@@@@@@@@.... + .@@@.....@@@@... + .@@@......@@@... + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@......@@@... + .@@@.....@@@@... + .@@@@@@@@@@@.... + .@@@@@@@@@...... + ................ + ................ + ................ + ................ + ................ + ................ + +u+0045: +"E": + ................ + ................ + ................ + ................ + ................ + ................ + .@@@@@@@@@@@@@.. + .@@@@@@@@@@@@@.. + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@@@@@@@@..... + .@@@@@@@@@@..... + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@@@@@@@@@@@.. + .@@@@@@@@@@@@@.. + ................ + ................ + ................ + ................ + ................ + ................ + +u+0046: +"F": + ................ + ................ + ................ + ................ + ................ + ................ + .@@@@@@@@@@@@@.. + .@@@@@@@@@@@@@.. + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@@@@@@@@..... + .@@@@@@@@@@..... + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + ................ + ................ + ................ + ................ + ................ + ................ + +u+0047: +"G": + ................ + ................ + ................ + ................ + ................ + ................ + ...@@@@@@@@@.... + ..@@@@@@@@@@@... + .@@@@.....@@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@...@@@@@@@.. + .@@@...@@@@@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@@.....@@@@.. + ..@@@@@@@@@@@... + ...@@@@@@@@@.... + ................ + ................ + ................ + ................ + ................ + ................ + +u+0048: +"H": + ................ + ................ + ................ + ................ + ................ + ................ + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@@@@@@@@@@@.. + .@@@@@@@@@@@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + ................ + ................ + ................ + ................ + ................ + ................ + +u+0049: +"I": + ................ + ................ + ................ + ................ + ................ + ................ + ....@@@@@@@..... + ....@@@@@@@..... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ....@@@@@@@..... + ....@@@@@@@..... + ................ + ................ + ................ + ................ + ................ + ................ + +u+004a: +"J": + ................ + ................ + ................ + ................ + ................ + ................ + ........@@@@@@@. + ........@@@@@@@. + ..........@@@... + ..........@@@... + ..........@@@... + ..........@@@... + ..........@@@... + ..........@@@... + ..........@@@... + ..........@@@... + ..........@@@... + ..........@@@... + ..........@@@... + ..........@@@... + .@@@......@@@... + .@@@......@@@... + .@@@......@@@... + .@@@@....@@@@... + ..@@@@@@@@@@.... + ...@@@@@@@@..... + ................ + ................ + ................ + ................ + ................ + ................ + +u+004b: +"K": + ................ + ................ + ................ + ................ + ................ + ................ + .@@@........@@.. + .@@@.......@@@.. + .@@@......@@@... + .@@@.....@@@.... + .@@@....@@@..... + .@@@...@@@...... + .@@@..@@@....... + .@@@.@@@........ + .@@@@@@......... + .@@@@@.......... + .@@@@@.......... + .@@@@@@......... + .@@@.@@@........ + .@@@..@@@....... + .@@@...@@@...... + .@@@....@@@..... + .@@@.....@@@.... + .@@@......@@@... + .@@@.......@@@.. + .@@@........@@.. + ................ + ................ + ................ + ................ + ................ + ................ + +u+004c: +"L": + ................ + ................ + ................ + ................ + ................ + ................ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@@@@@@@@@@@.. + .@@@@@@@@@@@@@.. + ................ + ................ + ................ + ................ + ................ + ................ + +u+004d: +"M": + ................ + ................ + ................ + ................ + ................ + ................ + .@@@........@@@. + .@@@........@@@. + .@@@@......@@@@. + .@@@@@....@@@@@. + .@@@@@@..@@@@@@. + .@@@@@@..@@@@@@. + .@@@.@@@@@@.@@@. + .@@@..@@@@..@@@. + .@@@..@@@@..@@@. + .@@@...@@...@@@. + .@@@........@@@. + .@@@........@@@. + .@@@........@@@. + .@@@........@@@. + .@@@........@@@. + .@@@........@@@. + .@@@........@@@. + .@@@........@@@. + .@@@........@@@. + .@@@........@@@. + ................ + ................ + ................ + ................ + ................ + ................ + +u+004e: +"N": + ................ + ................ + ................ + ................ + ................ + ................ + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@@......@@@.. + .@@@@@.....@@@.. + .@@@@@@....@@@.. + .@@@.@@@...@@@.. + .@@@..@@@..@@@.. + .@@@...@@@.@@@.. + .@@@....@@@@@@.. + .@@@.....@@@@@.. + .@@@......@@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + ................ + ................ + ................ + ................ + ................ + ................ + +u+004f: +"O": + ................ + ................ + ................ + ................ + ................ + ................ + ...@@@@@@@@@.... + ..@@@@@@@@@@@... + .@@@@.....@@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@@.....@@@@.. + ..@@@@@@@@@@@... + ...@@@@@@@@@.... + ................ + ................ + ................ + ................ + ................ + ................ + +u+0050: +"P": + ................ + ................ + ................ + ................ + ................ + ................ + .@@@@@@@@@@@.... + .@@@@@@@@@@@@... + .@@@......@@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@......@@@@.. + .@@@@@@@@@@@@... + .@@@@@@@@@@@.... + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + ................ + ................ + ................ + ................ + ................ + ................ + +u+0051: +"Q": + ................ + ................ + ................ + ................ + ................ + ................ + ...@@@@@@@@@.... + ..@@@@@@@@@@@... + .@@@@.....@@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@..@@@..@@@.. + .@@@@..@@@@@@@.. + ..@@@@@@@@@@@... + ...@@@@@@@@@.... + ..........@@@... + ...........@@@.. + ................ + ................ + ................ + ................ + +u+0052: +"R": + ................ + ................ + ................ + ................ + ................ + ................ + .@@@@@@@@@@@.... + .@@@@@@@@@@@@... + .@@@......@@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@......@@@@.. + .@@@@@@@@@@@@... + .@@@@@@@@@@@.... + .@@@@@@......... + .@@@.@@@........ + .@@@..@@@....... + .@@@...@@@...... + .@@@....@@@..... + .@@@.....@@@.... + .@@@......@@@... + .@@@.......@@@.. + ................ + ................ + ................ + ................ + ................ + ................ + +u+0053: +"S": + ................ + ................ + ................ + ................ + ................ + ................ + ...@@@@@@@@@.... + ..@@@@@@@@@@@... + .@@@@.....@@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@............ + .@@@............ + .@@@............ + .@@@@........... + ..@@@@@@@@@@.... + ...@@@@@@@@@@... + ..........@@@@.. + ...........@@@.. + ...........@@@.. + ...........@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@@.....@@@@.. + ..@@@@@@@@@@@... + ...@@@@@@@@@.... + ................ + ................ + ................ + ................ + ................ + ................ + +u+0054: +"T": + ................ + ................ + ................ + ................ + ................ + ................ + .@@@@@@@@@@@@@.. + .@@@@@@@@@@@@@.. + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ................ + ................ + ................ + ................ + ................ + ................ + +u+0055: +"U": + ................ + ................ + ................ + ................ + ................ + ................ + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@@.....@@@@.. + ..@@@@@@@@@@@... + ...@@@@@@@@@.... + ................ + ................ + ................ + ................ + ................ + ................ + +u+0056: +"V": + ................ + ................ + ................ + ................ + ................ + ................ + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + ..@@@.....@@@... + ..@@@.....@@@... + ..@@@.....@@@... + ..@@@.....@@@... + ..@@@.....@@@... + ...@@@...@@@.... + ...@@@...@@@.... + ...@@@...@@@.... + ...@@@...@@@.... + ....@@@.@@@..... + ....@@@.@@@..... + ....@@@.@@@..... + .....@@@@@...... + .....@@@@@...... + .....@@@@@...... + ................ + ................ + ................ + ................ + ................ + ................ + +u+0057: +"W": + ................ + ................ + ................ + ................ + ................ + ................ + .@@@........@@@. + .@@@........@@@. + .@@@........@@@. + .@@@........@@@. + .@@@........@@@. + .@@@........@@@. + .@@@........@@@. + .@@@........@@@. + .@@@........@@@. + .@@@........@@@. + .@@@...@@...@@@. + .@@@..@@@@..@@@. + .@@@..@@@@..@@@. + .@@@.@@@@@@.@@@. + .@@@@@@..@@@@@@. + .@@@@@@..@@@@@@. + .@@@@@....@@@@@. + .@@@@......@@@@. + .@@@........@@@. + .@@@........@@@. + ................ + ................ + ................ + ................ + ................ + ................ + +u+0058: +"X": + ................ + ................ + ................ + ................ + ................ + ................ + .@@@.......@@@.. + .@@@.......@@@.. + ..@@@.....@@@... + ..@@@.....@@@... + ...@@@...@@@.... + ...@@@...@@@.... + ....@@@.@@@..... + ....@@@.@@@..... + .....@@@@@...... + .....@@@@@...... + .....@@@@@...... + .....@@@@@...... + ....@@@.@@@..... + ....@@@.@@@..... + ...@@@...@@@.... + ...@@@...@@@.... + ..@@@.....@@@... + ..@@@.....@@@... + .@@@.......@@@.. + .@@@.......@@@.. + ................ + ................ + ................ + ................ + ................ + ................ + +u+0059: +"Y": + ................ + ................ + ................ + ................ + ................ + ................ + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + ..@@@.....@@@... + ..@@@.....@@@... + ...@@@...@@@.... + ...@@@...@@@.... + ....@@@.@@@..... + ....@@@.@@@..... + .....@@@@@...... + .....@@@@@...... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ................ + ................ + ................ + ................ + ................ + ................ + +u+005a: +"Z": + ................ + ................ + ................ + ................ + ................ + ................ + .@@@@@@@@@@@@@.. + .@@@@@@@@@@@@@.. + ...........@@@.. + ...........@@@.. + ...........@@@.. + ..........@@@... + .........@@@.... + ........@@@..... + .......@@@...... + ......@@@....... + .....@@@........ + ....@@@......... + ...@@@.......... + ..@@@........... + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@@@@@@@@@@@.. + .@@@@@@@@@@@@@.. + ................ + ................ + ................ + ................ + ................ + ................ + +u+005b: +bracketleft: + ................ + ................ + ................ + ................ + ................ + ................ + ....@@@@@@@@.... + ....@@@@@@@@.... + ....@@@......... + ....@@@......... + ....@@@......... + ....@@@......... + ....@@@......... + ....@@@......... + ....@@@......... + ....@@@......... + ....@@@......... + ....@@@......... + ....@@@......... + ....@@@......... + ....@@@......... + ....@@@......... + ....@@@......... + ....@@@......... + ....@@@@@@@@.... + ....@@@@@@@@.... + ................ + ................ + ................ + ................ + ................ + ................ + +u+005c: +backslash: + ................ + ................ + ................ + ................ + ................ + ................ + ..@@@........... + ..@@@........... + ...@@@.......... + ...@@@.......... + ....@@@......... + ....@@@......... + .....@@@........ + .....@@@........ + ......@@@....... + ......@@@....... + .......@@@...... + .......@@@...... + ........@@@..... + ........@@@..... + .........@@@.... + .........@@@.... + ..........@@@... + ..........@@@... + ...........@@@.. + ...........@@@.. + ................ + ................ + ................ + ................ + ................ + ................ + +u+005d: +bracketright: + ................ + ................ + ................ + ................ + ................ + ................ + ....@@@@@@@@.... + ....@@@@@@@@.... + .........@@@.... + .........@@@.... + .........@@@.... + .........@@@.... + .........@@@.... + .........@@@.... + .........@@@.... + .........@@@.... + .........@@@.... + .........@@@.... + .........@@@.... + .........@@@.... + .........@@@.... + .........@@@.... + .........@@@.... + .........@@@.... + ....@@@@@@@@.... + ....@@@@@@@@.... + ................ + ................ + ................ + ................ + ................ + ................ + +u+005e: +asciicircum: + ................ + ................ + ................ + ................ + ......@@@....... + .....@@@@@...... + ....@@@.@@@..... + ...@@@...@@@.... + ..@@@.....@@@... + .@@@.......@@@.. + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + +u+005f: +underscore: + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + .@@@@@@@@@@@@@.. + .@@@@@@@@@@@@@.. + ................ + ................ + ................ + +u+0060: +grave: + ................ + ................ + ...@@@.......... + ....@@@......... + .....@@@........ + ......@@@....... + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + +u+0061: +"a": + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ..@@@@@@@@@@.... + ..@@@@@@@@@@@... + ..........@@@@.. + ...........@@@.. + ...........@@@.. + ...@@@@@@@@@@@.. + ..@@@@@@@@@@@@.. + .@@@@......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@@......@@@.. + ..@@@@@@@@@@@@.. + ...@@@@@@@@@@@.. + ................ + ................ + ................ + ................ + ................ + ................ + +u+0062: +"b": + ................ + ................ + ................ + ................ + ................ + ................ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@@@@@@@@@.... + .@@@@@@@@@@@@... + .@@@......@@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@......@@@@.. + .@@@@@@@@@@@@... + .@@@@@@@@@@@.... + ................ + ................ + ................ + ................ + ................ + ................ + +u+0063: +"c": + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ...@@@@@@@@@.... + ..@@@@@@@@@@@... + .@@@@.....@@@@.. + .@@@.......@@@.. + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@.......@@@.. + .@@@@.....@@@@.. + ..@@@@@@@@@@@... + ...@@@@@@@@@.... + ................ + ................ + ................ + ................ + ................ + ................ + +u+0064: +"d": + ................ + ................ + ................ + ................ + ................ + ................ + ...........@@@.. + ...........@@@.. + ...........@@@.. + ...........@@@.. + ...........@@@.. + ...........@@@.. + ...@@@@@@@@@@@.. + ..@@@@@@@@@@@@.. + .@@@@......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@@......@@@.. + ..@@@@@@@@@@@@.. + ...@@@@@@@@@@@.. + ................ + ................ + ................ + ................ + ................ + ................ + +u+0065: +"e": + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ...@@@@@@@@@.... + ..@@@@@@@@@@@... + .@@@@.....@@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@@@@@@@@@@@.. + .@@@@@@@@@@@@@.. + .@@@............ + .@@@............ + .@@@............ + .@@@@......@@@.. + ..@@@@@@@@@@@@.. + ...@@@@@@@@@@... + ................ + ................ + ................ + ................ + ................ + ................ + +u+0066: +"f": + ................ + ................ + ................ + ................ + ................ + ................ + ........@@@@@@@. + .......@@@@@@@@. + ......@@@@...... + ......@@@....... + ......@@@....... + ......@@@....... + ..@@@@@@@@@@@... + ..@@@@@@@@@@@... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ................ + ................ + ................ + ................ + ................ + ................ + +u+0067: +"g": + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ...@@@@@@@@@@@.. + ..@@@@@@@@@@@@.. + .@@@@......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@@......@@@.. + ..@@@@@@@@@@@@.. + ...@@@@@@@@@@@.. + ...........@@@.. + ...........@@@.. + ..........@@@@.. + ..@@@@@@@@@@@... + ..@@@@@@@@@@.... + ................ + +u+0068: +"h": + ................ + ................ + ................ + ................ + ................ + ................ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@@@@@@@@@.... + .@@@@@@@@@@@@... + .@@@......@@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + ................ + ................ + ................ + ................ + ................ + ................ + +u+0069: +"i": + ................ + ................ + ................ + ................ + ................ + ................ + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ................ + ................ + ....@@@@@....... + ....@@@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ....@@@@@@@..... + ....@@@@@@@..... + ................ + ................ + ................ + ................ + ................ + ................ + +u+006a: +"j": + ................ + ................ + ................ + ................ + ................ + ................ + ..........@@@... + ..........@@@... + ..........@@@... + ..........@@@... + ................ + ................ + ........@@@@@... + ........@@@@@... + ..........@@@... + ..........@@@... + ..........@@@... + ..........@@@... + ..........@@@... + ..........@@@... + ..........@@@... + ..........@@@... + ..........@@@... + ..........@@@... + ..........@@@... + ..........@@@... + ..@@@.....@@@... + ..@@@.....@@@... + ..@@@@...@@@@... + ...@@@@@@@@@.... + ....@@@@@@@..... + ................ + +u+006b: +"k": + ................ + ................ + ................ + ................ + ................ + ................ + ..@@@........... + ..@@@........... + ..@@@........... + ..@@@........... + ..@@@........... + ..@@@........... + ..@@@......@@@.. + ..@@@.....@@@... + ..@@@....@@@.... + ..@@@...@@@..... + ..@@@..@@@...... + ..@@@.@@@....... + ..@@@@@@........ + ..@@@@@@........ + ..@@@.@@@....... + ..@@@..@@@...... + ..@@@...@@@..... + ..@@@....@@@.... + ..@@@.....@@@... + ..@@@......@@@.. + ................ + ................ + ................ + ................ + ................ + ................ + +u+006c: +"l": + ................ + ................ + ................ + ................ + ................ + ................ + ....@@@@@....... + ....@@@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ....@@@@@@@..... + ....@@@@@@@..... + ................ + ................ + ................ + ................ + ................ + ................ + +u+006d: +"m": + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + .@@@@@@@@@@@.... + .@@@@@@@@@@@@... + .@@@..@@@.@@@@.. + .@@@..@@@..@@@.. + .@@@..@@@..@@@.. + .@@@..@@@..@@@.. + .@@@..@@@..@@@.. + .@@@..@@@..@@@.. + .@@@..@@@..@@@.. + .@@@..@@@..@@@.. + .@@@..@@@..@@@.. + .@@@..@@@..@@@.. + .@@@..@@@..@@@.. + .@@@..@@@..@@@.. + ................ + ................ + ................ + ................ + ................ + ................ + +u+006e: +"n": + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + .@@@@@@@@@@@.... + .@@@@@@@@@@@@... + .@@@......@@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + ................ + ................ + ................ + ................ + ................ + ................ + +u+006f: +"o": + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ...@@@@@@@@@.... + ..@@@@@@@@@@@... + .@@@@.....@@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@@.....@@@@.. + ..@@@@@@@@@@@... + ...@@@@@@@@@.... + ................ + ................ + ................ + ................ + ................ + ................ + +u+0070: +"p": + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + .@@@@@@@@@@@.... + .@@@@@@@@@@@@... + .@@@......@@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@......@@@@.. + .@@@@@@@@@@@@... + .@@@@@@@@@@@.... + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + ................ + +u+0071: +"q": + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ...@@@@@@@@@@@.. + ..@@@@@@@@@@@@.. + .@@@@......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@@......@@@.. + ..@@@@@@@@@@@@.. + ...@@@@@@@@@@@.. + ...........@@@.. + ...........@@@.. + ...........@@@.. + ...........@@@.. + ...........@@@.. + ................ + +u+0072: +"r": + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + .@@@..@@@@@@@@.. + .@@@.@@@@@@@@@.. + .@@@@@@......... + .@@@@@.......... + .@@@@........... + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + .@@@............ + ................ + ................ + ................ + ................ + ................ + ................ + +u+0073: +"s": + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ...@@@@@@@@@.... + ..@@@@@@@@@@@... + .@@@@.....@@@@.. + .@@@............ + .@@@............ + .@@@@........... + ..@@@@@@@@@@.... + ...@@@@@@@@@@... + ..........@@@@.. + ...........@@@.. + ...........@@@.. + .@@@@.....@@@@.. + ..@@@@@@@@@@@... + ...@@@@@@@@@.... + ................ + ................ + ................ + ................ + ................ + ................ + +u+0074: +"t": + ................ + ................ + ................ + ................ + ................ + ................ + .....@@@........ + .....@@@........ + .....@@@........ + .....@@@........ + .....@@@........ + .....@@@........ + .@@@@@@@@@@@.... + .@@@@@@@@@@@.... + .....@@@........ + .....@@@........ + .....@@@........ + .....@@@........ + .....@@@........ + .....@@@........ + .....@@@........ + .....@@@........ + .....@@@........ + .....@@@@....... + ......@@@@@@@@.. + .......@@@@@@@.. + ................ + ................ + ................ + ................ + ................ + ................ + +u+0075: +"u": + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@@......@@@.. + ..@@@@@@@@@@@@.. + ...@@@@@@@@@@@.. + ................ + ................ + ................ + ................ + ................ + ................ + +u+0076: +"v": + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + ..@@@.....@@@... + ..@@@.....@@@... + ..@@@.....@@@... + ...@@@...@@@.... + ...@@@...@@@.... + ...@@@...@@@.... + ....@@@.@@@..... + ....@@@.@@@..... + .....@@@@@...... + .....@@@@@...... + .....@@@@@...... + ................ + ................ + ................ + ................ + ................ + ................ + +u+0077: +"w": + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@..@@@..@@@.. + .@@@..@@@..@@@.. + .@@@..@@@..@@@.. + .@@@..@@@..@@@.. + .@@@..@@@..@@@.. + .@@@..@@@..@@@.. + .@@@..@@@..@@@.. + .@@@@.@@@.@@@@.. + ..@@@@@@@@@@@... + ...@@@@@@@@@.... + ................ + ................ + ................ + ................ + ................ + ................ + +u+0078: +"x": + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + ..@@@.....@@@... + ...@@@...@@@.... + ....@@@.@@@..... + .....@@@@@...... + .....@@@@@...... + ....@@@.@@@..... + ...@@@...@@@.... + ..@@@.....@@@... + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + ................ + ................ + ................ + ................ + ................ + ................ + +u+0079: +"y": + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@.......@@@.. + .@@@@......@@@.. + ..@@@@@@@@@@@@.. + ...@@@@@@@@@@@.. + ...........@@@.. + ...........@@@.. + ..........@@@@.. + ..@@@@@@@@@@@... + ..@@@@@@@@@@.... + ................ + +u+007a: +"z": + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + .@@@@@@@@@@@@@.. + .@@@@@@@@@@@@@.. + ..........@@@... + .........@@@.... + ........@@@..... + .......@@@...... + ......@@@....... + .....@@@........ + ....@@@......... + ...@@@.......... + ..@@@........... + .@@@............ + .@@@@@@@@@@@@@.. + .@@@@@@@@@@@@@.. + ................ + ................ + ................ + ................ + ................ + ................ + +u+007b: +braceleft: + ................ + ................ + ................ + ................ + ................ + ................ + .......@@@@@.... + ......@@@@@@.... + .....@@@@....... + .....@@@........ + .....@@@........ + .....@@@........ + .....@@@........ + .....@@@........ + .....@@@........ + ..@@@@@......... + ..@@@@@......... + .....@@@........ + .....@@@........ + .....@@@........ + .....@@@........ + .....@@@........ + .....@@@........ + .....@@@@....... + ......@@@@@@.... + .......@@@@@.... + ................ + ................ + ................ + ................ + ................ + ................ + +u+007c: +bar: + ................ + ................ + ................ + ................ + ................ + ................ + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ................ + ................ + ................ + ................ + ................ + ................ + +u+007d: +braceright: + ................ + ................ + ................ + ................ + ................ + ................ + ..@@@@@......... + ..@@@@@@........ + .....@@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + .......@@@@@.... + .......@@@@@.... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + ......@@@....... + .....@@@@....... + ..@@@@@@........ + ..@@@@@......... + ................ + ................ + ................ + ................ + ................ + ................ + +u+007e: +asciitilde: + ................ + ................ + ................ + ................ + ...@@@@....@@@.. + ..@@@@@@...@@@.. + .@@@.@@@@..@@@.. + .@@@..@@@@.@@@.. + .@@@...@@@@@@... + .@@@....@@@@.... + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + +u+00a0: +nbspace: + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ diff --git a/containers/test/test_container.cpp b/containers/test/test_container.cpp index c3e2a8b61..2f9e67bf8 100644 --- a/containers/test/test_container.cpp +++ b/containers/test/test_container.cpp @@ -1,94 +1,363 @@ #include "test_container.h" #include "Font.h" +#define CANVAS_ITY_IMPLEMENTATION +#include "canvas_ity.hpp" string readfile(string filename); -// note: font is selected only by size, name and style are not used -uint_ptr test_container::create_font(const char* faceName, int size, int weight, font_style italic, unsigned int decoration, font_metrics* fm) +// +// canvas_ity adapters +// + +void set_color(canvas& cvs, brush_type type, color c) +{ + cvs.set_color(type, c.r / 255.f, c.g / 255.f, c.b / 255.f, c.a / 255.f); +} + +void fill_rect(canvas& cvs, rect r) +{ + cvs.fill_rectangle((float)r.x, (float)r.y, (float)r.width, (float)r.height); +} + +void fill_rect(canvas& cvs, rect r, color color) +{ + set_color(cvs, fill_style, color); + fill_rect(cvs, r); +} + +void fill_circle(canvas& cvs, rect rc, color color) +{ + float r = min(rc.width, rc.height) / 2.f; + float x = rc.x + rc.width / 2.f; + float y = rc.y + rc.height / 2.f; + set_color(cvs, fill_style, color); + cvs.begin_path(); + cvs.arc(x, y, r, 0, 2*pi); + cvs.fill(); +} + +void draw_circle(canvas& cvs, rect rc, color color) { - Font* font = new Font(size); + float r = min(rc.width, rc.height) / 2.f - .5f; + float x = rc.x + rc.width / 2.f; + float y = rc.y + rc.height / 2.f; + set_color(cvs, stroke_style, color); + cvs.begin_path(); + cvs.arc(x, y, r, 0, 2*pi); + cvs.stroke(); +} + +void clip_rect(canvas& cvs, rect r) +{ + cvs.begin_path(); + cvs.rectangle((float)r.x, (float)r.y, (float)r.width, (float)r.height); + cvs.clip(); +} + +// without scaling +void draw_image(canvas& cvs, int x, int y, const Bitmap& bmp) +{ + cvs.draw_image((byte*)bmp.data.data(), bmp.width, bmp.height, bmp.width * 4, (float)x, (float)y, (float)bmp.width, (float)bmp.height); +} + +// with scaling +void draw_image(canvas& cvs, rect rc, const Bitmap& bmp) +{ + cvs.draw_image((byte*)bmp.data.data(), bmp.width, bmp.height, bmp.width * 4, (float)rc.x, (float)rc.y, (float)rc.width, (float)rc.height); +} + +void add_color_stop(canvas& cvs, brush_type type, float offset, color c, optional hint) +{ + cvs.add_color_stop(type, offset, c.r / 255.f, c.g / 255.f, c.b / 255.f, c.a / 255.f, hint); +} - if (fm) +bool set_font(canvas& cvs, const string& raw_font_data, int pixel_size) +{ + return cvs.set_font((byte*)raw_font_data.data(), (int)raw_font_data.size(), (float)pixel_size); +} + +void fill_polygon(canvas& cvs, vector points, color color) +{ + cvs.begin_path(); + cvs.polygon(points); + set_color(cvs, fill_style, color); + cvs.fill(); +} + +// +// test_container implementation +// + +uint_ptr test_container::create_font(const char* font_families, int size, int weight, font_style /*italic*/, unsigned int /*decoration*/, font_metrics* fm) +{ + Font* font = 0; + string_vector fonts = split_string(font_families, ",", "", ""); + for (auto name : fonts) { - fm->ascent = font->ascent; - fm->descent = font->descent; - fm->height = font->height; - fm->x_height = font->x_height; + font = Font::create(name, size, weight); + if (font) break; } + if (!font) + font = Font::create(get_default_font_name(), size, weight); + + if (fm) *fm = *font; + return (uint_ptr)font; } int test_container::text_width(const char* text, uint_ptr hFont) { Font* font = (Font*)hFont; - return (int)strlen(text) * font->width; + return font->text_width(text); } void test_container::draw_text(uint_ptr hdc, const char* text, uint_ptr hFont, web_color color, const position& pos) { - Bitmap* bmp = (Bitmap*)hdc; Font* font = (Font*)hFont; - - int x = pos.x; - for (auto p = text; *p; p++) - { - Bitmap glyph = font->get_glyph(*p, color); - bmp->draw_bitmap(x, pos.y, glyph); - x += glyph.width; - } + font->draw_text(*(canvas*)hdc, text, color, pos.x, pos.y); } int test_container::pt_to_px(int pt) const { return pt * 96 / 72; } int test_container::get_default_font_size() const { return 16; } -const char* test_container::get_default_font_name() const { return ""; } +const char* test_container::get_default_font_name() const { return "Terminus"; } -void test_container::draw_background(uint_ptr hdc, const std::vector& bg) +void test_container::draw_solid_fill(uint_ptr hdc, const background_layer& layer, const web_color& color) { - Bitmap* bmp = (Bitmap*)hdc; - bmp->fill_rect(bg.back().border_box, bg.back().color); + auto cvs = (canvas*)hdc; + fill_rect(*cvs, layer.border_box, color); } -void test_container::draw_borders(uint_ptr hdc, const borders& borders, const position& pos, bool root) +void test_container::draw_borders(uint_ptr hdc, const borders& borders, const position& pos, bool /*root*/) { - Bitmap* bmp = (Bitmap*)hdc; + canvas img(pos.width, pos.height); + img.global_composite_operation = lighter; - // left border - for (int x = 0; x < borders.left.width; x++) - bmp->draw_line( - pos.left() + x, pos.top(), - pos.left() + x, pos.bottom(), borders.left.color); +/* + A_________________B + |\ /| + | \ / | + | \_________/ | + | |a b| | + | | | | + | | | | + | |d_______c| | + | / \ | + | / \ | + |/_____________\| + D C +*/ + float width_ = (float)pos.width; + float height_ = (float)pos.height; - // right border - for (int x = 0; x < borders.right.width; x++) - bmp->draw_line( - pos.right() - x - 1, pos.top(), - pos.right() - x - 1, pos.bottom(), borders.right.color); + float left_width = (float)borders.left.width; + float right_width = (float)borders.right.width; + float top_width = (float)borders.top.width; + float bottom_width = (float)borders.bottom.width; - // top border - for (int y = 0; y < borders.top.width; y++) - bmp->draw_line( - pos.left(), pos.top() + y, - pos.right(), pos.top() + y, borders.top.color); + xy A = {0, 0}; + xy B = {width_, 0}; + xy C = {width_, height_}; + xy D = {0, height_}; - // bottom border - for (int y = 0; y < borders.bottom.width; y++) - bmp->draw_line( - pos.left(), pos.bottom() - y - 1, - pos.right(), pos.bottom() - y - 1, borders.bottom.color); + xy a = A + xy(left_width, top_width); + xy b = B + xy(-right_width, top_width); + xy c = C + xy(-right_width, -bottom_width); + xy d = D + xy(left_width, -bottom_width); + + fill_polygon(img, {A, B, b, a}, borders.top.color); + fill_polygon(img, {B, C, c, b}, borders.right.color); + fill_polygon(img, {C, D, d, c}, borders.bottom.color); + fill_polygon(img, {D, A, a, d}, borders.left.color); + + ::draw_image(*(canvas*)hdc, pos.x, pos.y, img); } void test_container::draw_list_marker(uint_ptr hdc, const list_marker& marker) { - Bitmap* bmp = (Bitmap*)hdc; - bmp->fill_rect(marker.pos, marker.color); + auto& cvs = *(canvas*)hdc; + + if (marker.image != "") + { + string url = make_url(marker.image.c_str(), marker.baseurl); + auto& img = images[url]; + if (img) + { + ::draw_image(cvs, marker.pos, img); + return; + } + } + + switch (marker.marker_type) + { + case list_style_type_circle: + draw_circle(cvs, marker.pos, marker.color); + break; + + case list_style_type_disc: + fill_circle(cvs, marker.pos, marker.color); + break; + + case list_style_type_square: + fill_rect(cvs, marker.pos, marker.color); + break; + + default: + // do nothing + break; + } +} + +string getdir(string filename) +{ + auto i = filename.find_last_of("\\/"); + return filename.substr(0, i); +} + +string test_container::make_url(const char* src, const char* baseurl) +{ + return (baseurl && *baseurl ? getdir(baseurl) : basedir) + "/" + src; } void test_container::import_css(string& text, const string& url, string& baseurl) { - baseurl = basedir + "/" + url; + baseurl = make_url(url.c_str(), baseurl.c_str()); text = readfile(baseurl); } void test_container::get_client_rect(position& client) const { - client = position(0, 0, width, height); + client = {0, 0, width, height}; +} + +void test_container::get_media_features(media_features& media) const +{ + position client; + get_client_rect(client); + media.type = media_type_screen; + media.width = client.width; + media.height = client.height; + media.color = 8; // same as Chrome/Firefox + media.monochrome = 0; // same as Chrome/Firefox + media.color_index = 0; // same as Chrome/Firefox + media.resolution = 96; // same as Chrome/Firefox +} + +void test_container::load_image(const char* src, const char* baseurl, bool /*redraw_on_ready*/) +{ + string url = make_url(src, baseurl); + images[url] = Bitmap(url); +} + +void test_container::get_image_size(const char* src, const char* baseurl, size& sz) +{ + string url = make_url(src, baseurl); + auto& img = images[url]; + sz = {img.width, img.height}; +} + +void draw_image_pattern(canvas& cvs, const background_layer& bg, const Bitmap& img) +{ + cvs.save(); + clip_rect(cvs, bg.clip_box); + + int x = bg.origin_box.x; + int y = bg.origin_box.y; + int w = bg.origin_box.width; + int h = bg.origin_box.height; + + switch (bg.repeat) + { + case background_repeat_no_repeat: + draw_image(cvs, {x, y, w, h}, img); + break; + + case background_repeat_repeat_x: + while (x > bg.clip_box.left()) x -= w; + for (; x < bg.clip_box.right(); x += w) + draw_image(cvs, {x, y, w, h}, img); + break; + + case background_repeat_repeat_y: + while (y > bg.clip_box.top()) y -= h; + for (; y < bg.clip_box.bottom(); y += h) + draw_image(cvs, {x, y, w, h}, img); + break; + + case background_repeat_repeat: + while (x > bg.clip_box.left()) x -= w; + while (y > bg.clip_box.top()) y -= h; + for (; x < bg.clip_box.right(); x += w) + for (int _y = y; _y < bg.clip_box.bottom(); _y += h) + draw_image(cvs, {x, _y, w, h}, img); + break; + } + cvs.restore(); +} + +void test_container::draw_image(uint_ptr hdc, const background_layer& bg, const string& src, const string& base_url) +{ + auto& cvs = *(canvas*)hdc; + string url = make_url(src.c_str(), base_url.c_str()); + auto& img = images[url]; + if (!img) return; + + draw_image_pattern(cvs, bg, img); } + +void set_gradient(canvas& cvs, const background_layer::linear_gradient& gradient, int origin_x, int origin_y) +{ + cvs.set_linear_gradient(fill_style, + gradient.start.x - origin_x, + gradient.start.y - origin_y, + gradient.end.x - origin_x, + gradient.end.y - origin_y); +} +void set_gradient(canvas& cvs, const background_layer::radial_gradient& gradient, int origin_x, int origin_y) +{ + cvs.set_css_radial_gradient(fill_style, + gradient.position.x - origin_x, + gradient.position.y - origin_y, + gradient.radius.x, + gradient.radius.y); +} +void set_gradient(canvas& cvs, const background_layer::conic_gradient& gradient, int origin_x, int origin_y) +{ + cvs.set_conic_gradient(fill_style, + gradient.position.x - origin_x, + gradient.position.y - origin_y, + gradient.angle); +} + +template +void draw_gradient(uint_ptr hdc, const background_layer& bg, const Gradient& gradient) +{ + int x = bg.origin_box.x; + int y = bg.origin_box.y; + int w = bg.origin_box.width; + int h = bg.origin_box.height; + + canvas img(w, h); + + set_gradient(img, gradient, x, y); + + for (auto cs : gradient.color_points) + add_color_stop(img, fill_style, cs.offset, cs.color, cs.hint); + + fill_rect(img, {0, 0, w, h}); + + draw_image_pattern(*(canvas*)hdc, bg, img); +} + +void test_container::draw_linear_gradient(uint_ptr hdc, const background_layer& layer, const background_layer::linear_gradient& gradient) +{ + draw_gradient(hdc, layer, gradient); +} + +void test_container::draw_radial_gradient(uint_ptr hdc, const background_layer& layer, const background_layer::radial_gradient& gradient) +{ + draw_gradient(hdc, layer, gradient); +} + +void test_container::draw_conic_gradient(uint_ptr hdc, const background_layer& layer, const background_layer::conic_gradient& gradient) +{ + draw_gradient(hdc, layer, gradient); +} \ No newline at end of file diff --git a/containers/test/test_container.h b/containers/test/test_container.h index 964312667..18f0aa662 100644 --- a/containers/test/test_container.h +++ b/containers/test/test_container.h @@ -1,5 +1,8 @@ #include +#include "Bitmap.h" +#include "canvas_ity.hpp" using namespace litehtml; +using namespace canvas_ity; class test_container : public document_container { @@ -7,36 +10,44 @@ class test_container : public document_container int width; int height; string basedir; + std::map images; test_container(int width, int height, string basedir) : width(width), height(height), basedir(basedir) {} + string make_url(const char* src, const char* baseurl); + uint_ptr create_font(const char* faceName, int size, int weight, font_style italic, unsigned int decoration, font_metrics* fm) override; - void delete_font(uint_ptr hFont) override {} + void delete_font(uint_ptr /*hFont*/) override {} int text_width(const char* text, uint_ptr hFont) override; void draw_text(uint_ptr hdc, const char* text, uint_ptr hFont, web_color color, const position& pos) override; int pt_to_px(int pt) const override; int get_default_font_size() const override; const char* get_default_font_name() const override; - void load_image(const char* src, const char* baseurl, bool redraw_on_ready) override {} - void get_image_size(const char* src, const char* baseurl, size& sz) override {} - void draw_background(uint_ptr hdc, const std::vector& bg) override; + void load_image(const char* src, const char* baseurl, bool redraw_on_ready) override; + void get_image_size(const char* src, const char* baseurl, size& sz) override; + void draw_image(uint_ptr hdc, const background_layer& layer, const string& url, const string& base_url) override; + void draw_solid_fill(uint_ptr hdc, const background_layer& layer, const web_color& color) override; + void draw_linear_gradient(uint_ptr hdc, const background_layer& layer, const background_layer::linear_gradient& gradient) override; + void draw_radial_gradient(uint_ptr hdc, const background_layer& layer, const background_layer::radial_gradient& gradient) override; + void draw_conic_gradient(uint_ptr hdc, const background_layer& layer, const background_layer::conic_gradient& gradient) override; void draw_borders(uint_ptr hdc, const borders& borders, const position& draw_pos, bool root) override; void draw_list_marker(uint_ptr hdc, const list_marker& marker) override; - element::ptr create_element(const char* tag_name, - const string_map& attributes, - const document::ptr& doc) override { return 0; } - void get_media_features(media_features& media) const override {} - void get_language(string& language, string& culture) const override {} - void link(const document::ptr& doc, const element::ptr& el) override {} + element::ptr create_element(const char* /*tag_name*/, + const string_map& /*attributes*/, + const document::ptr& /*doc*/) override { return nullptr; } + void get_media_features(media_features& media) const override; + void get_language(string& /*language*/, string& /*culture*/) const override {} + void link(const document::ptr& /*doc*/, const element::ptr& /*el*/) override {} - void transform_text(string& text, text_transform tt) override {} - void set_clip(const position& pos, const border_radiuses& bdr_radius) override {} + void transform_text(string& /*text*/, text_transform /*tt*/) override {} + void set_clip(const position& /*pos*/, const border_radiuses& /*bdr_radius*/) override {} void del_clip() override {} - void set_caption(const char* caption) override {} - void set_base_url(const char* base_url) override {} - void on_anchor_click(const char* url, const element::ptr& el) override {} - void set_cursor(const char* cursor) override {} + void set_caption(const char* /*caption*/) override {} + void set_base_url(const char* /*base_url*/) override {} + void on_anchor_click(const char* /*url*/, const element::ptr& /*el*/) override {} + void on_mouse_event(const element::ptr& /*el*/, mouse_event /*event*/) override {}; + void set_cursor(const char* /*cursor*/) override {} void import_css(string& text, const string& url, string& baseurl) override; void get_client_rect(position& client) const override; -}; \ No newline at end of file +}; diff --git a/containers/cairo/cairo_font.cpp b/containers/windows/cairo/cairo_font.cpp similarity index 85% rename from containers/cairo/cairo_font.cpp rename to containers/windows/cairo/cairo_font.cpp index 2401f8721..76dcaa5ee 100644 --- a/containers/cairo/cairo_font.cpp +++ b/containers/windows/cairo/cairo_font.cpp @@ -139,10 +139,10 @@ void cairo_font::show_text( cairo_t* cr, int x, int y, const char* str ) void cairo_font::split_text( const char* src, text_chunk::vector& chunks ) { - wchar_t* str = cairo_font::utf8_to_wchar(src); - wchar_t* str_start = str; + auto str = cairo_font::utf8_to_wchar(src); + const wchar_t* str_start = str.c_str(); - int cch = lstrlen(str); + int cch = str.length(); HDC hdc = GetDC(NULL); SelectObject(hdc, m_hFont); @@ -153,7 +153,7 @@ void cairo_font::split_text( const char* src, text_chunk::vector& chunks ) long cchActual; if(m_font_link) { - hr = m_font_link->GetStrCodePages(str, cch, m_font_code_pages, &dwActualCodePages, &cchActual); + hr = m_font_link->GetStrCodePages(str.c_str(), cch, m_font_code_pages, &dwActualCodePages, &cchActual); } else { hr = S_FALSE; @@ -166,9 +166,9 @@ void cairo_font::split_text( const char* src, text_chunk::vector& chunks ) text_chunk* chk = new text_chunk; - int sz = WideCharToMultiByte(CP_UTF8, 0, str, cchActual, chk->text, 0, NULL, NULL) + 1; + int sz = WideCharToMultiByte(CP_UTF8, 0, str.c_str(), cchActual, chk->text, 0, NULL, NULL) + 1; chk->text = new CHAR[sz]; - sz = WideCharToMultiByte(CP_UTF8, 0, str, cchActual, chk->text, sz, NULL, NULL); + sz = WideCharToMultiByte(CP_UTF8, 0, str.c_str(), cchActual, chk->text, sz, NULL, NULL); chk->text[sz] = 0; chk->font = NULL; @@ -212,16 +212,15 @@ void cairo_font::split_text( const char* src, text_chunk::vector& chunks ) { text_chunk* chk = new text_chunk; - int sz = WideCharToMultiByte(CP_UTF8, 0, str, -1, chk->text, 0, NULL, NULL) + 1; + int sz = WideCharToMultiByte(CP_UTF8, 0, str.c_str(), -1, chk->text, 0, NULL, NULL) + 1; chk->text = new CHAR[sz]; - sz = WideCharToMultiByte(CP_UTF8, 0, str, -1, chk->text, sz, NULL, NULL); + sz = WideCharToMultiByte(CP_UTF8, 0, str.c_str(), -1, chk->text, sz, NULL, NULL); chk->text[sz] = 0; chk->font = NULL; chunks.push_back(chk); } ReleaseDC(NULL, hdc); - delete str_start; } void cairo_font::free_text_chunks( text_chunk::vector& chunks ) @@ -350,22 +349,26 @@ void cairo_font::init() m_bStrikeOut = FALSE; } -wchar_t* cairo_font::utf8_to_wchar( const char* src ) +std::wstring cairo_font::utf8_to_wchar(const std::string& src ) { - if(!src) return NULL; + if (src.empty()) return std::wstring(); - int len = (int) strlen(src); - wchar_t* ret = new wchar_t[len + 1]; - MultiByteToWideChar(CP_UTF8, 0, src, -1, ret, len + 1); + int len = (int) src.size(); + wchar_t* str = new wchar_t[len + 1]; + MultiByteToWideChar(CP_UTF8, 0, src.c_str(), -1, str, len + 1); + std::wstring ret(str); + delete str; return ret; } -char* cairo_font::wchar_to_utf8( const wchar_t* src ) +std::string cairo_font::wchar_to_utf8(const std::wstring& src) { - if(!src) return NULL; + if(src.empty()) return std::string(); - int len = WideCharToMultiByte(CP_UTF8, 0, src, -1, NULL, 0, NULL, NULL); - char* ret = new char[len]; - WideCharToMultiByte(CP_UTF8, 0, src, -1, ret, len, NULL, NULL); + int len = WideCharToMultiByte(CP_UTF8, 0, src.c_str(), -1, NULL, 0, NULL, NULL); + char* str = new char[len]; + WideCharToMultiByte(CP_UTF8, 0, src.c_str(), -1, str, len, NULL, NULL); + std::string ret(str); + delete str; return ret; -} \ No newline at end of file +} diff --git a/containers/cairo/cairo_font.h b/containers/windows/cairo/cairo_font.h similarity index 93% rename from containers/cairo/cairo_font.h rename to containers/windows/cairo/cairo_font.h index 238efe434..fc9dd84ed 100644 --- a/containers/cairo/cairo_font.h +++ b/containers/windows/cairo/cairo_font.h @@ -1,5 +1,8 @@ #pragma once +#ifndef NOMINMAX +#define NOMINMAX +#endif #include #include #include @@ -71,8 +74,8 @@ class cairo_font int text_width(cairo_t* cr, const char* str); void load_metrics(cairo_t* cr); cairo_font_metrics& metrics(); - static wchar_t* utf8_to_wchar(const char* src); - static char* wchar_to_utf8(const wchar_t* src); + static std::wstring utf8_to_wchar(const std::string& src); + static std::string wchar_to_utf8(const std::wstring& src); private: void split_text(const char* str, text_chunk::vector& chunks); void free_text_chunks(text_chunk::vector& chunks); diff --git a/containers/windows/cairo/windows_container.cpp b/containers/windows/cairo/windows_container.cpp new file mode 100644 index 000000000..e19ad337e --- /dev/null +++ b/containers/windows/cairo/windows_container.cpp @@ -0,0 +1,194 @@ +#include "windows_container.h" +#define _USE_MATH_DEFINES +#include +#include "cairo_font.h" +#include + +windows_container::windows_container(void) +{ + m_temp_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 2, 2); + m_temp_cr = cairo_create(m_temp_surface); + m_font_link = NULL; + CoCreateInstance(CLSID_CMultiLanguage, NULL, CLSCTX_ALL, IID_IMLangFontLink2, (void**) &m_font_link); +} + +windows_container::~windows_container(void) +{ + clear_images(); + if(m_font_link) + { + m_font_link->Release(); + } + cairo_surface_destroy(m_temp_surface); + cairo_destroy(m_temp_cr); +} + +litehtml::uint_ptr windows_container::create_font( const char* faceName, int size, int weight, litehtml::font_style italic, unsigned int decoration, litehtml::font_metrics* fm ) +{ + std::wstring fnt_name = L"sans-serif"; + + litehtml::string_vector fonts; + litehtml::split_string(faceName, fonts, ","); + if(!fonts.empty()) + { + litehtml::trim(fonts[0]); + fnt_name = cairo_font::utf8_to_wchar(fonts[0]); + if (fnt_name.front() == L'"' || fnt_name.front() == L'\'') + { + fnt_name.erase(0, 1); + } + if (fnt_name.back() == L'"' || fnt_name.back() == L'\'') + { + fnt_name.erase(fnt_name.length() - 1, 1); + } + } + + cairo_font* fnt = new cairo_font( m_font_link, + fnt_name.c_str(), + size, + weight, + (italic == litehtml::font_style_italic) ? TRUE : FALSE, + (decoration & litehtml::font_decoration_linethrough) ? TRUE : FALSE, + (decoration & litehtml::font_decoration_underline) ? TRUE : FALSE); + + cairo_save(m_temp_cr); + fnt->load_metrics(m_temp_cr); + + if(fm) + { + fm->ascent = fnt->metrics().ascent; + fm->descent = fnt->metrics().descent; + fm->height = fnt->metrics().height; + fm->x_height = fnt->metrics().x_height; + if(italic == litehtml::font_style_italic || decoration) + { + fm->draw_spaces = true; + } else + { + fm->draw_spaces = false; + } + } + + cairo_restore(m_temp_cr); + + return (litehtml::uint_ptr) fnt; +} + +void windows_container::delete_font( litehtml::uint_ptr hFont ) +{ + cairo_font* fnt = (cairo_font*) hFont; + if(fnt) + { + delete fnt; + } +} + +int windows_container::text_width( const char* text, litehtml::uint_ptr hFont ) +{ + cairo_font* fnt = (cairo_font*) hFont; + + cairo_save(m_temp_cr); + int ret = fnt->text_width(m_temp_cr, text); + cairo_restore(m_temp_cr); + return ret; +} + +void windows_container::draw_text( litehtml::uint_ptr hdc, const char* text, litehtml::uint_ptr hFont, litehtml::web_color color, const litehtml::position& pos ) +{ + if(hFont) + { + cairo_font* fnt = (cairo_font*) hFont; + cairo_t* cr = (cairo_t*) hdc; + cairo_save(cr); + + apply_clip(cr); + + int x = pos.left(); + int y = pos.bottom() - fnt->metrics().descent; + + set_color(cr, color); + fnt->show_text(cr, x, y, text); + + cairo_restore(cr); + } +} + +litehtml::string windows_container::resolve_color(const litehtml::string& color) const +{ + struct custom_color + { + const char* name; + int color_index; + }; + + static custom_color colors[] = { + { "ActiveBorder", COLOR_ACTIVEBORDER}, + { "ActiveCaption", COLOR_ACTIVECAPTION}, + { "AppWorkspace", COLOR_APPWORKSPACE }, + { "Background", COLOR_BACKGROUND }, + { "ButtonFace", COLOR_BTNFACE }, + { "ButtonHighlight", COLOR_BTNHIGHLIGHT }, + { "ButtonShadow", COLOR_BTNSHADOW }, + { "ButtonText", COLOR_BTNTEXT }, + { "CaptionText", COLOR_CAPTIONTEXT }, + { "GrayText", COLOR_GRAYTEXT }, + { "Highlight", COLOR_HIGHLIGHT }, + { "HighlightText", COLOR_HIGHLIGHTTEXT }, + { "InactiveBorder", COLOR_INACTIVEBORDER }, + { "InactiveCaption", COLOR_INACTIVECAPTION }, + { "InactiveCaptionText", COLOR_INACTIVECAPTIONTEXT }, + { "InfoBackground", COLOR_INFOBK }, + { "InfoText", COLOR_INFOTEXT }, + { "Menu", COLOR_MENU }, + { "MenuText", COLOR_MENUTEXT }, + { "Scrollbar", COLOR_SCROLLBAR }, + { "ThreeDDarkShadow", COLOR_3DDKSHADOW }, + { "ThreeDFace", COLOR_3DFACE }, + { "ThreeDHighlight", COLOR_3DHILIGHT }, + { "ThreeDLightShadow", COLOR_3DLIGHT }, + { "ThreeDShadow", COLOR_3DSHADOW }, + { "Window", COLOR_WINDOW }, + { "WindowFrame", COLOR_WINDOWFRAME }, + { "WindowText", COLOR_WINDOWTEXT } + }; + + for (auto& clr : colors) + { + if (!litehtml::t_strcasecmp(clr.name, color.c_str())) + { + char str_clr[20]; + DWORD rgb_color = GetSysColor(clr.color_index); + StringCchPrintfA(str_clr, 20, "#%02X%02X%02X", GetRValue(rgb_color), GetGValue(rgb_color), GetBValue(rgb_color)); + return std::move(litehtml::string(str_clr)); + } + } + return std::move(litehtml::string()); +} + +cairo_surface_t* windows_container::get_image(const std::string& url) +{ + return nullptr; +} + +double windows_container::get_screen_dpi() const +{ + HDC hdc = GetDC(NULL); + int ret = GetDeviceCaps(hdc, LOGPIXELSX); + ReleaseDC(NULL, hdc); + return ret; +} + +int windows_container::get_screen_width() const +{ + HDC hdc = GetDC(NULL); + int ret = GetDeviceCaps(hdc, HORZRES); + ReleaseDC(NULL, hdc); + return ret; +} +int windows_container::get_screen_height() const +{ + HDC hdc = GetDC(NULL); + int ret = GetDeviceCaps(hdc, VERTRES); + ReleaseDC(NULL, hdc); + return ret; +} diff --git a/containers/windows/cairo/windows_container.h b/containers/windows/cairo/windows_container.h new file mode 100644 index 000000000..daae519ca --- /dev/null +++ b/containers/windows/cairo/windows_container.h @@ -0,0 +1,39 @@ +#pragma once +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#include +#include +#include +#include +#include +#include +#include "cairo.h" +#include "cairo-win32.h" +#include +#include +#include +#include "../../libs/litehtml/containers/cairo/container_cairo.h" + +class windows_container : public container_cairo +{ +protected: + cairo_surface_t* m_temp_surface; + cairo_t* m_temp_cr; + IMLangFontLink2* m_font_link; +public: + windows_container(void); + virtual ~windows_container(void); + + litehtml::uint_ptr create_font(const char* faceName, int size, int weight, litehtml::font_style italic, unsigned int decoration, litehtml::font_metrics* fm) override; + void delete_font(litehtml::uint_ptr hFont) override; + int text_width(const char* text, litehtml::uint_ptr hFont) override; + void draw_text(litehtml::uint_ptr hdc, const char* text, litehtml::uint_ptr hFont, litehtml::web_color color, const litehtml::position& pos) override; + litehtml::string resolve_color(const litehtml::string& color) const override; + + cairo_surface_t* get_image(const std::string& url) override; + double get_screen_dpi() const override; + int get_screen_width() const override; + int get_screen_height() const override; +}; diff --git a/containers/gdiplus/gdiplus_container.cpp b/containers/windows/gdiplus/gdiplus_container.cpp similarity index 86% rename from containers/gdiplus/gdiplus_container.cpp rename to containers/windows/gdiplus/gdiplus_container.cpp index 9813d387c..17b706318 100644 --- a/containers/gdiplus/gdiplus_container.cpp +++ b/containers/windows/gdiplus/gdiplus_container.cpp @@ -1,3 +1,6 @@ +#ifndef NOMINMAX +#define NOMINMAX +#endif #include #include #include "gdiplus_container.h" @@ -68,7 +71,7 @@ void gdiplus_container::free_image(uint_ptr img) delete bmp; } -void gdiplus_container::draw_img_bg(HDC hdc, uint_ptr img, const background_paint& bg) +void gdiplus_container::draw_img_bg(HDC hdc, uint_ptr img, const litehtml::background_layer& bg) { Bitmap* bgbmp = (Bitmap*)img; @@ -80,12 +83,12 @@ void gdiplus_container::draw_img_bg(HDC hdc, uint_ptr img, const background_pain graphics.SetClip(®); Bitmap* scaled_img = nullptr; - if (bg.image_size.width != bgbmp->GetWidth() || bg.image_size.height != bgbmp->GetHeight()) + if (bg.origin_box.width != bgbmp->GetWidth() || bg.origin_box.height != bgbmp->GetHeight()) { - scaled_img = new Bitmap(bg.image_size.width, bg.image_size.height); + scaled_img = new Bitmap(bg.origin_box.width, bg.origin_box.height); Graphics gr(scaled_img); gr.SetPixelOffsetMode(PixelOffsetModeHighQuality); - gr.DrawImage(bgbmp, 0, 0, bg.image_size.width, bg.image_size.height); + gr.DrawImage(bgbmp, 0, 0, bg.origin_box.width, bg.origin_box.height); bgbmp = scaled_img; } @@ -93,38 +96,38 @@ void gdiplus_container::draw_img_bg(HDC hdc, uint_ptr img, const background_pain { case background_repeat_no_repeat: { - graphics.DrawImage(bgbmp, bg.position_x, bg.position_y, bgbmp->GetWidth(), bgbmp->GetHeight()); + graphics.DrawImage(bgbmp, bg.origin_box.x, bg.origin_box.y, bgbmp->GetWidth(), bgbmp->GetHeight()); } break; case background_repeat_repeat_x: { CachedBitmap bmp(bgbmp, &graphics); - int x = bg.position_x; - while(x > bg.clip_box.left()) x -= bgbmp->GetWidth(); - for(; x < bg.clip_box.right(); x += bgbmp->GetWidth()) + int x = bg.origin_box.x; + while (x > bg.clip_box.left()) x -= bgbmp->GetWidth(); + for (; x < bg.clip_box.right(); x += bgbmp->GetWidth()) { - graphics.DrawCachedBitmap(&bmp, x, bg.position_y); + graphics.DrawCachedBitmap(&bmp, x, bg.origin_box.y); } } break; case background_repeat_repeat_y: { CachedBitmap bmp(bgbmp, &graphics); - int y = bg.position_y; - while(y > bg.clip_box.top()) y -= bgbmp->GetHeight(); - for(; y < bg.clip_box.bottom(); y += bgbmp->GetHeight()) + int y = bg.origin_box.y; + while (y > bg.clip_box.top()) y -= bgbmp->GetHeight(); + for (; y < bg.clip_box.bottom(); y += bgbmp->GetHeight()) { - graphics.DrawCachedBitmap(&bmp, bg.position_x, y); + graphics.DrawCachedBitmap(&bmp, bg.origin_box.x, y); } } break; case background_repeat_repeat: { CachedBitmap bmp(bgbmp, &graphics); - int x = bg.position_x; - while(x > bg.clip_box.left()) x -= bgbmp->GetWidth(); - int y0 = bg.position_y; - while(y0 > bg.clip_box.top()) y0 -= bgbmp->GetHeight(); + int x = bg.origin_box.x; + while (x > bg.clip_box.left()) x -= bgbmp->GetWidth(); + int y0 = bg.origin_box.y; + while (y0 > bg.clip_box.top()) y0 -= bgbmp->GetHeight(); for(; x < bg.clip_box.right(); x += bgbmp->GetWidth()) { diff --git a/containers/gdiplus/gdiplus_container.h b/containers/windows/gdiplus/gdiplus_container.h similarity index 96% rename from containers/gdiplus/gdiplus_container.h rename to containers/windows/gdiplus/gdiplus_container.h index a5a61382c..c90482d22 100644 --- a/containers/gdiplus/gdiplus_container.h +++ b/containers/windows/gdiplus/gdiplus_container.h @@ -17,7 +17,7 @@ class gdiplus_container : public win32_container void fill_rect(HDC hdc, int x, int y, int width, int height, litehtml::web_color color) override; void get_img_size(uint_ptr img, litehtml::size& sz) override; void free_image(uint_ptr img) override; - void draw_img_bg(HDC hdc, uint_ptr img, const litehtml::background_paint& bg) override; + void draw_img_bg(HDC hdc, uint_ptr img, const litehtml::background_layer& layer) override; // litehtml::document_container members void draw_borders(uint_ptr hdc, const litehtml::borders& borders, const litehtml::position& draw_pos, bool root) override; }; diff --git a/containers/win32/win32_container.cpp b/containers/windows/win32/win32_container.cpp similarity index 70% rename from containers/win32/win32_container.cpp rename to containers/windows/win32/win32_container.cpp index 520b1ed23..7930856eb 100644 --- a/containers/win32/win32_container.cpp +++ b/containers/windows/win32/win32_container.cpp @@ -1,4 +1,41 @@ #include "win32_container.h" +using namespace std; +using namespace litehtml; + +wstring utf8_to_utf16(string str) +{ + utf8_to_utf32 utf32 = str; + wstring wstr; + for(const char32_t* ptr = utf32; *ptr; ptr++) + { + char32_t ch = *ptr; + if (ch <= 0xFFFF) wstr += (wchar_t)ch; + // high surrogate goes first in UTF-16LE + else wstr += {(wchar_t)(((ch - 0x10000) >> 10) + 0xD800), (ch & 0x3FF) + 0xDC00}; + } + return wstr; +} + +string utf16_to_utf8(wstring str) +{ + u32string ustr; + for (size_t i = 0; i < str.size(); i++) + { + char16_t ch = str[i]; + if (ch >= 0xD800 && ch < 0xDC00) + { + char16_t ch2 = str[i + 1]; + if (ch2 >= 0xDC00 && ch2 < 0xE000) + { + ustr += 0x10000 + (ch2 - 0xDC00) + (ch - 0xD800) * 0x400; + i++; + continue; + } + } + ustr += ch; + } + return utf32_to_utf8(ustr).c_str(); +} win32_container::win32_container() { @@ -41,7 +78,7 @@ static LPCWSTR get_exact_font_name(LPCWSTR facename) else return facename; } -static void trim_quotes(litehtml::string& str) +static void trim_quotes(string& str) { if (str.front() == '"' || str.front() == '\'') str.erase(0, 1); @@ -50,17 +87,17 @@ static void trim_quotes(litehtml::string& str) str.erase(str.length() - 1, 1); } -litehtml::uint_ptr win32_container::create_font( const char* font_list, int size, int weight, litehtml::font_style italic, unsigned int decoration, litehtml::font_metrics* fm ) +uint_ptr win32_container::create_font( const char* font_list, int size, int weight, font_style italic, unsigned int decoration, font_metrics* fm ) { - std::wstring font_name; - litehtml::string_vector fonts; - litehtml::split_string(font_list, fonts, ","); + wstring font_name; + string_vector fonts; + split_string(font_list, fonts, ","); bool found = false; for (auto& name : fonts) { - litehtml::trim(name); + trim(name); trim_quotes(name); - std::wstring wname = (const wchar_t*)litehtml_to_wchar(name.c_str()); + wstring wname = utf8_to_utf16(name); if (m_installed_fonts.count(wname)) { font_name = wname; @@ -68,7 +105,7 @@ litehtml::uint_ptr win32_container::create_font( const char* font_list, int size break; } } - if (!found) font_name = litehtml_to_wchar(get_default_font_name()); + if (!found) font_name = utf8_to_utf16(get_default_font_name()); font_name = get_exact_font_name(font_name.c_str()); LOGFONT lf = {}; @@ -76,13 +113,13 @@ litehtml::uint_ptr win32_container::create_font( const char* font_list, int size lf.lfHeight = -size; lf.lfWeight = weight; - lf.lfItalic = (italic == litehtml::font_style_italic) ? TRUE : FALSE; + lf.lfItalic = italic == font_style_italic; lf.lfCharSet = DEFAULT_CHARSET; lf.lfOutPrecision = OUT_DEFAULT_PRECIS; lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; lf.lfQuality = DEFAULT_QUALITY; - lf.lfStrikeOut = (decoration & litehtml::font_decoration_linethrough) ? TRUE : FALSE; - lf.lfUnderline = (decoration & litehtml::font_decoration_underline) ? TRUE : FALSE; + lf.lfStrikeOut = (decoration & font_decoration_linethrough) != 0; + lf.lfUnderline = (decoration & font_decoration_underline) != 0; HFONT hFont = CreateFontIndirect(&lf); if (fm) @@ -119,12 +156,12 @@ int win32_container::text_width( const char* text, uint_ptr hFont ) { SIZE size = {}; SelectObject(m_tmp_hdc, (HFONT)hFont); - std::wstring wtext = (const wchar_t*)litehtml_to_wchar(text); + wstring wtext = utf8_to_utf16(text); GetTextExtentPoint32(m_tmp_hdc, wtext.c_str(), (int)wtext.size(), &size); return size.cx; } -void win32_container::draw_text( uint_ptr hdc, const char* text, uint_ptr hFont, litehtml::web_color color, const litehtml::position& pos ) +void win32_container::draw_text( uint_ptr hdc, const char* text, uint_ptr hFont, web_color color, const position& pos ) { apply_clip((HDC) hdc); @@ -135,7 +172,7 @@ void win32_container::draw_text( uint_ptr hdc, const char* text, uint_ptr hFont, SetTextColor((HDC) hdc, RGB(color.red, color.green, color.blue)); RECT rcText = { pos.left(), pos.top(), pos.right(), pos.bottom() }; - DrawText((HDC) hdc, litehtml_to_wchar(text), -1, &rcText, DT_SINGLELINE | DT_NOPREFIX | DT_BOTTOM | DT_NOCLIP); + DrawText((HDC) hdc, utf8_to_utf16(text).c_str(), -1, &rcText, DT_SINGLELINE | DT_NOPREFIX | DT_BOTTOM | DT_NOCLIP); SelectObject((HDC) hdc, oldFont); @@ -147,7 +184,7 @@ int win32_container::pt_to_px( int pt ) const return MulDiv(pt, GetDeviceCaps(m_tmp_hdc, LOGPIXELSY), 72); } -void win32_container::draw_list_marker(uint_ptr hdc, const litehtml::list_marker& marker) +void win32_container::draw_list_marker(uint_ptr hdc, const list_marker& marker) { apply_clip((HDC)hdc); @@ -162,17 +199,17 @@ void win32_container::draw_list_marker(uint_ptr hdc, const litehtml::list_marker switch (marker.marker_type) { - case litehtml::list_style_type_circle: + case list_style_type_circle: { draw_ellipse((HDC)hdc, draw_x, draw_y, draw_width, draw_height, marker.color, 1); } break; - case litehtml::list_style_type_disc: + case list_style_type_disc: { fill_ellipse((HDC)hdc, draw_x, draw_y, draw_width, draw_height, marker.color); } break; - case litehtml::list_style_type_square: + case list_style_type_square: { fill_rect((HDC)hdc, draw_x, draw_y, draw_width, draw_height, marker.color); } @@ -181,14 +218,15 @@ void win32_container::draw_list_marker(uint_ptr hdc, const litehtml::list_marker release_clip((HDC)hdc); } -void win32_container::make_url_utf8(const char* url, const char* basepath, std::wstring& out) +void win32_container::make_url_utf8(const char* url, const char* basepath, wstring& out) { - make_url(litehtml::utf8_to_wchar(url), litehtml::utf8_to_wchar(basepath), out); + if (!basepath) basepath = ""; + make_url(utf8_to_utf16(url).c_str(), utf8_to_utf16(basepath).c_str(), out); } void win32_container::load_image( const char* src, const char* baseurl, bool redraw_on_ready ) { - std::wstring url; + wstring url; make_url_utf8(src, baseurl, url); lock_images_cache(); @@ -213,7 +251,7 @@ void win32_container::add_image(LPCWSTR url, uint_ptr img) void win32_container::get_image_size( const char* src, const char* baseurl, litehtml::size& sz ) { - std::wstring url; + wstring url; make_url_utf8(src, baseurl, url); sz.width = 0; @@ -252,33 +290,37 @@ void win32_container::unlock_images_cache() LeaveCriticalSection(&m_img_sync); } -void win32_container::draw_background( uint_ptr _hdc, const std::vector& bg ) +void win32_container::draw_solid_fill(uint_ptr _hdc, const background_layer& layer, const web_color& color) { HDC hdc = (HDC)_hdc; apply_clip(hdc); - auto border_box = bg.back().border_box; - auto color = bg.back().color; + auto border_box = layer.border_box; fill_rect(hdc, border_box.x, border_box.y, border_box.width, border_box.height, color); - for (int i = (int)bg.size() - 1; i >= 0; i--) - { - std::wstring url; - make_url_utf8(bg[i].image.c_str(), bg[i].baseurl.c_str(), url); + release_clip(hdc); +} - lock_images_cache(); - images_map::iterator img = m_images.find(url); - if (img != m_images.end() && img->second) - { - draw_img_bg(hdc, img->second, bg[i]); - } - unlock_images_cache(); +void win32_container::draw_image(uint_ptr _hdc, const background_layer& layer, const string& url, const string& base_url) +{ + HDC hdc = (HDC)_hdc; + apply_clip(hdc); + + wstring wurl; + make_url_utf8(url.c_str(), base_url.c_str(), wurl); + + lock_images_cache(); + images_map::iterator img = m_images.find(wurl); + if (img != m_images.end() && img->second) + { + draw_img_bg(hdc, img->second, layer); } + unlock_images_cache(); release_clip(hdc); } -void win32_container::set_clip( const litehtml::position& pos, const litehtml::border_radiuses& bdr_radius ) +void win32_container::set_clip( const position& pos, const border_radiuses& bdr_radius ) { m_clips.push_back(pos); } @@ -304,7 +346,7 @@ void win32_container::apply_clip(HDC hdc) POINT ptView = {0, 0}; GetWindowOrgEx(hdc, &ptView); - litehtml::position clip_pos = m_clips.back(); + position clip_pos = m_clips.back(); m_hClipRgn = CreateRectRgn(clip_pos.left() - ptView.x, clip_pos.top() - ptView.y, clip_pos.right() - ptView.x, clip_pos.bottom() - ptView.y); SelectClipRgn(hdc, m_hClipRgn); } @@ -321,17 +363,17 @@ void win32_container::release_clip(HDC hdc) } } -litehtml::element::ptr win32_container::create_element(const char* tag_name, const litehtml::string_map& attributes, const litehtml::document::ptr& doc) +element::ptr win32_container::create_element(const char* tag_name, const string_map& attributes, const document::ptr& doc) { return 0; } -void win32_container::get_media_features(litehtml::media_features& media) const +void win32_container::get_media_features(media_features& media) const { - litehtml::position client; + position client; get_client_rect(client); - media.type = litehtml::media_type_screen; + media.type = media_type_screen; media.width = client.width; media.height = client.height; media.color = 8; @@ -342,43 +384,43 @@ void win32_container::get_media_features(litehtml::media_features& media) const media.device_height = GetDeviceCaps(m_tmp_hdc, VERTRES); } -void win32_container::get_language(litehtml::string& language, litehtml::string& culture) const +void win32_container::get_language(string& language, string& culture) const { language = "en"; culture = ""; } -void win32_container::transform_text(litehtml::string& text, litehtml::text_transform tt) +void win32_container::transform_text(string& text, text_transform tt) { if (text.empty()) return; - LPWSTR txt = _wcsdup(litehtml_to_wchar(text.c_str())); + LPWSTR txt = _wcsdup(utf8_to_utf16(text).c_str()); switch (tt) { - case litehtml::text_transform_capitalize: + case text_transform_capitalize: CharUpperBuff(txt, 1); break; - case litehtml::text_transform_uppercase: + case text_transform_uppercase: CharUpperBuff(txt, lstrlen(txt)); break; - case litehtml::text_transform_lowercase: + case text_transform_lowercase: CharLowerBuff(txt, lstrlen(txt)); break; } - text = litehtml_from_wchar(txt); + text = utf16_to_utf8(txt); free(txt); } -void win32_container::link(const litehtml::document::ptr& doc, const litehtml::element::ptr& el) +void win32_container::link(const document::ptr& doc, const element::ptr& el) { } -litehtml::string win32_container::resolve_color(const litehtml::string& color) const +string win32_container::resolve_color(const string& color) const { struct custom_color { const char* name; - int color_index; + int color_index; }; static custom_color colors[] = { @@ -414,13 +456,13 @@ litehtml::string win32_container::resolve_color(const litehtml::string& color) c for (auto& clr : colors) { - if (!litehtml::t_strcasecmp(color.c_str(), clr.name)) + if (!t_strcasecmp(color.c_str(), clr.name)) { char str_clr[20]; DWORD rgb_color = GetSysColor(clr.color_index); t_snprintf(str_clr, 20, "#%02X%02X%02X", GetRValue(rgb_color), GetGValue(rgb_color), GetBValue(rgb_color)); - return std::move(litehtml::string(str_clr)); + return std::move(string(str_clr)); } } - return std::move(litehtml::string()); + return std::move(string()); } diff --git a/containers/win32/win32_container.h b/containers/windows/win32/win32_container.h similarity index 79% rename from containers/win32/win32_container.h rename to containers/windows/win32/win32_container.h index 5a85a47a7..f8b13dd3b 100644 --- a/containers/win32/win32_container.h +++ b/containers/windows/win32/win32_container.h @@ -1,4 +1,7 @@ #pragma once +#ifndef NOMINMAX +#define NOMINMAX +#endif #include #include #include @@ -34,7 +37,11 @@ class win32_container : public litehtml::document_container void draw_list_marker(uint_ptr hdc, const litehtml::list_marker& marker) override; void load_image(const char* src, const char* baseurl, bool redraw_on_ready) override; void get_image_size(const char* src, const char* baseurl, litehtml::size& sz) override; - void draw_background(uint_ptr hdc, const std::vector& bg) override; + void draw_image(litehtml::uint_ptr hdc, const litehtml::background_layer& layer, const std::string& url, const std::string& base_url) override; + void draw_solid_fill(litehtml::uint_ptr hdc, const litehtml::background_layer& layer, const litehtml::web_color& color) override; + void draw_linear_gradient(litehtml::uint_ptr hdc, const litehtml::background_layer& layer, const litehtml::background_layer::linear_gradient& gradient) override {} + void draw_radial_gradient(litehtml::uint_ptr hdc, const litehtml::background_layer& layer, const litehtml::background_layer::radial_gradient& gradient) override {} + void draw_conic_gradient(litehtml::uint_ptr hdc, const litehtml::background_layer& layer, const litehtml::background_layer::conic_gradient& gradient) override {} void set_clip(const litehtml::position& pos, const litehtml::border_radiuses& bdr_radius) override; void del_clip() override; @@ -59,7 +66,7 @@ class win32_container : public litehtml::document_container void clear_images(); virtual void free_image(uint_ptr img) = 0; virtual void get_img_size(uint_ptr img, litehtml::size& sz) = 0; - virtual void draw_img_bg(HDC hdc, uint_ptr img, const litehtml::background_paint& bg) = 0; + virtual void draw_img_bg(HDC hdc, uint_ptr img, const litehtml::background_layer& layer) = 0; virtual void draw_ellipse(HDC hdc, int x, int y, int width, int height, litehtml::web_color color, int line_width) = 0; virtual void fill_ellipse(HDC hdc, int x, int y, int width, int height, litehtml::web_color color) = 0; diff --git a/doc/document_container.md b/doc/document_container.md new file mode 100644 index 000000000..d9d617cc8 --- /dev/null +++ b/doc/document_container.md @@ -0,0 +1,523 @@ +# litehtml::document_container + +```litehtml::document_container``` class draws the HTML elements and perform some actions depended of the platform. This is the only class you should implement in your application. + +```cpp +class document_container +{ +public: + virtual litehtml::uint_ptr create_font(const font_description& descr, const document* doc, litehtml::font_metrics* fm) = 0; + virtual void delete_font(litehtml::uint_ptr hFont) = 0; + virtual int text_width(const char* text, litehtml::uint_ptr hFont) = 0; + virtual void draw_text(litehtml::uint_ptr hdc, const char* text, litehtml::uint_ptr hFont, litehtml::web_color color, const litehtml::position& pos) = 0; + virtual int pt_to_px(int pt) const = 0; + virtual int get_default_font_size() const = 0; + virtual const char* get_default_font_name() const = 0; + virtual void draw_list_marker(litehtml::uint_ptr hdc, const litehtml::list_marker& marker) = 0; + virtual void load_image(const char* src, const char* baseurl, bool redraw_on_ready) = 0; + virtual void get_image_size(const char* src, const char* baseurl, litehtml::size& sz) = 0; + virtual void draw_image(litehtml::uint_ptr hdc, const background_layer& layer, const std::string& url, const std::string& base_url) = 0; + virtual void draw_solid_fill(litehtml::uint_ptr hdc, const background_layer& layer, const web_color& color) = 0; + virtual void draw_linear_gradient(litehtml::uint_ptr hdc, const background_layer& layer, const background_layer::linear_gradient& gradient) = 0; + virtual void draw_radial_gradient(litehtml::uint_ptr hdc, const background_layer& layer, const background_layer::radial_gradient& gradient) = 0; + virtual void draw_conic_gradient(litehtml::uint_ptr hdc, const background_layer& layer, const background_layer::conic_gradient& gradient) = 0; + virtual void draw_borders(litehtml::uint_ptr hdc, const litehtml::borders& borders, const litehtml::position& draw_pos, bool root) = 0; + + virtual void set_caption(const char* caption) = 0; + virtual void set_base_url(const char* base_url) = 0; + virtual void link(const std::shared_ptr& doc, const litehtml::element::ptr& el) = 0; + virtual void on_anchor_click(const char* url, const litehtml::element::ptr& el) = 0; + virtual bool on_element_click(const litehtml::element::ptr& /*el*/) { return false; }; + virtual void on_mouse_event(const litehtml::element::ptr& el, litehtml::mouse_event event) = 0; + virtual void set_cursor(const char* cursor) = 0; + virtual void transform_text(litehtml::string& text, litehtml::text_transform tt) = 0; + virtual void import_css(litehtml::string& text, const litehtml::string& url, litehtml::string& baseurl) = 0; + virtual void set_clip(const litehtml::position& pos, const litehtml::border_radiuses& bdr_radius) = 0; + virtual void del_clip() = 0; + virtual void get_viewport(litehtml::position& viewport) const = 0; + virtual litehtml::element::ptr create_element( const char* tag_name, + const litehtml::string_map& attributes, + const std::shared_ptr& doc) = 0; + + virtual void get_media_features(litehtml::media_features& media) const = 0; + virtual void get_language(litehtml::string& language, litehtml::string& culture) const = 0; + virtual litehtml::string resolve_color(const litehtml::string& /*color*/) const { return litehtml::string(); } + virtual void split_text(const char* text, const std::function& on_word, const std::function& on_space); + +protected: + virtual ~document_container() = default; +}; + +``` + +Below is the list of member functions you have to implement: + +1. **Font Management** + - [create_font](#create_font) + - [delete_font](#delete_font) + - [text_width](#text_width) + - [draw_text](#draw_text) + - [pt_to_px](#pt_to_px) + - [get_default_font_size](#get_default_font_size) + - [get_default_font_name](#get_default_font_name) + +2. **Drawing** + - [draw_list_marker](#draw_list_marker) + - [draw_image](#draw_image) + - [draw_solid_fill](#draw_solid_fill) + - [draw_linear_gradient](#draw_linear_gradient) + - [draw_radial_gradient](#draw_radial_gradient) + - [draw_conic_gradient](#draw_conic_gradient) + - [draw_borders](#draw_borders) + +3. **Image Handling** + - [load_image](#load_image) + - [get_image_size](#get_image_size) + +4. **HTML Element Interaction** + - [link](#link) + - [on_anchor_click](#on_anchor_click) + - [on_element_click](#on_element_click) + - [on_mouse_event](#on_mouse_event) + +5. **CSS and Styling** + - [import_css](#import_css) + - [transform_text](#transform_text) + - [set_cursor](#set_cursor) + +6. **Clipping** + - [set_clip](#set_clip) + - [del_clip](#del_clip) + +7. **Viewport and Media** + - [get_viewport](#get_viewport) + - [get_media_features](#get_media_features) + +8. **Custom Elements** + - [create_element](#create_element) + +9. **Localization** + - [get_language](#get_language) + +10. **Miscellaneous** + - [set_caption](#set_caption) + - [set_base_url](#set_base_url) + - [resolve_color](#resolve_color) + - [split_text](#split_text) + + +### create_font + +```cpp +virtual litehtml::uint_ptr create_font(const font_description& descr, const document* doc, litehtml::font_metrics* fm); +``` + +This function called by litehtml to create the font. ```create_font``` returns ```uint_ptr``` that must identify the created font. +Parameters: +* ```const font_description& descr``` - the description of the font. This structure contains the font face name, size, weight and decoration. + +```font_description``` definition: +```cpp +struct font_description +{ + std::string family; + int size; + font_style style; + int weight; + int decoration_line; + css_length decoration_thickness; + text_decoration_style decoration_style; + web_color decoration_color; + std::string emphasis_style; + web_color emphasis_color; + int emphasis_position; +}; +``` +* ```family``` - Font family name as described in [CSS specifications](https://developer.mozilla.org/en-US/docs/Web/CSS/font-family). You have to select the proper font. +* ```size``` - Font size in pixels. +* ```style``` - Font style: + * ```font_style_normal``` - normal font style. + * ```font_style_italic``` - italic font style. + * ```font_style_oblique``` - oblique font style. +* ```weight``` - Font weight. Numerical value from 100 to 900. 400 is the same as normal, and 700 is the same as bold. See [CSS specifications](https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight). +* ```decoration_line``` - one or more flags: + * ```const unsigned int font_decoration_none=0x00;``` + * ```const unsigned int font_decoration_underline=0x01;``` + * ```const unsigned int font_decoration_linethrough=0x02;``` + * ```const unsigned int font_decoration_overline=0x04;``` +* ```decoration_thickness``` - the thickness of the decoration line in pixels. See predefined values in enum text_decoration_thickness. +* ```decoration_style``` - the style of the decoration line. See enum text_decoration_style. +* ```decoration_color``` - the color of the decoration line. +* ```emphasis_style``` - the emphasis style. +* ```emphasis_color``` - the emphasis color. +* ```emphasis_position``` - the emphasis position. + +--- +* ```litehtml::font_metrics* fm``` Defines the font metrics. This is the output format so you have to fill this structure with the font metrics: + +```cpp +struct font_metrics +{ + int font_size; + int height; + int ascent; + int descent; + int x_height; + int ch_width; + bool draw_spaces; + int sub_shift; + int super_shift; +}; +``` +* ```font_size``` - the font size in pixels. The same as size argument of the create_font function. +* ```height``` - the recommended vertical distance between baselines when setting consecutive lines of text with the font. This is greater than ```ascent+descent``` by a quantity known as the line spacing or external leading. When space is at a premium, most fonts can be set with only a distance of ```ascent+descent``` between lines. +* ```ascent``` - the distance from the baseline to the top of a line of text. +* ```descent``` - the distance from the baseline to the bottom of a line of text. +* ```x_height``` - height of the symbol x. +* ```ch_width``` - height of the symbol 0. +* ```draw_spaces``` - true to call draw text function for spaces. If false, just use space width without draw. +* ```sub_shift``` - the baseline shift for subscripts. +* ```super_shift``` - the baseline shift for superscripts. + +### delete_font +```cpp +virtual void delete_font(uint_ptr hFont); +``` + +delete the font created in [create_font](#create_font) function + +### text_width +```cpp +virtual int text_width(const char* text, uint_ptr hFont); +``` + +Returns the text width. + +### draw_text +```cpp +virtual void draw_text(uint_ptr hdc, const char* text, uint_ptr hFont, litehtml::web_color color, const litehtml::position& pos); +``` + +This function draw the text string. + +### pt_to_px +```cpp +virtual int pt_to_px(int pt); +``` + +Convert **points** into **pixels**. + +### get_default_font_size +```cpp +virtual int get_default_font_size(); +``` + +Returns the default font size in pixels. + +### get_default_font_name +```cpp +virtual const char* get_default_font_name(); +``` + +Returns the default font face name. + +### draw_list_marker +```cpp +virtual void draw_list_marker(uint_ptr hdc, const litehtml::list_marker& marker); +``` + +Draws the list marker. Use the parameter **marker** to find the marker type and position. + +### load_image +```cpp +virtual void load_image(const char* src, const char* baseurl); +``` + +You can preload image in this function. litehtml does not cache the images, so you have to create own images cache if you need it. + +### get_image_size +```cpp +virtual void get_image_size(const char* src, const char* baseurl, litehtml::size& sz); +``` + +Fill the **sz** parameter with image width and height. + +## Draw Background members + +There are five functions to draw the background: +* [draw_image](#draw_image) draw the image background. CSS example: ```background: no-repeat url("image.png");```. Also this function is used to draw the `````` tags. +* [draw_solid_fill](#draw_solid_fill) draw the solid color background. CSS example: ```background-color: #FF0000;```. +* [draw_linear_gradient](#draw_linear_gradient) draw the linear gradient background. CSS example: ```background: linear-gradient(to right, #FF0000, #00FF00);```. +* [draw_radial_gradient](#draw_radial_gradient) draw the radial gradient background. CSS example: ```background: radial-gradient(circle, #FF0000, #00FF00);```. +* [draw_conic_gradient](#draw_conic_gradient) draw the conic gradient background. CSS example: ```background: conic-gradient(#FF0000, #00FF00);```. + +All background functions accept the ```background_layer``` parameter. This structure defines the position and size of the background. +The definition of the ```background_layer``` structure: +```cpp +class background_layer +{ +public: + position border_box; + border_radiuses border_radius; + position clip_box; + position origin_box; + background_attachment attachment; + background_repeat repeat; + bool is_root; +} +``` +* ```border_box``` - defines the draw boundary. Everything must be drawn inside this rectangle only. +* ```border_radius``` - defines the radius of the border_box. +* ```clip_box``` - defines the clipping rectangle. Works like border_box. Container must set additional clipping. +* ```origin_box``` - defines the origin rectangle. +* ```attachment``` - defines the background attachment: + * ```background_attachment_fixed``` - fixed background. + * ```background_attachment_scroll``` - scroll background. +* ```repeat``` - defines the background repeat: + * ```background_repeat_no_repeat``` - no repeat. + * ```background_repeat_repeat``` - repeat. + * ```background_repeat_repeat_x``` - repeat horizontally. + * ```background_repeat_repeat_y``` - repeat vertically. +* ```is_root``` - is true for a root element. Container can use this flag to apply background to the top window. + +The gradient related functions accept the parameter based on the ```background_layer::gradient_base``` structure: +```cpp +class gradient_base +{ +public: + vector color_points; + color_space_t color_space = color_space_none; + hue_interpolation_t hue_interpolation = hue_interpolation_none; +}; +``` +* ```color_points``` - the color points of the gradient. Each point is defined by the color and position. +* ```color_space``` - the color space of the gradient. See enum ```color_space_t``` +* ```hue_interpolation``` - the hue interpolation of the gradient. See enum ```hue_interpolation_t```. + +### draw_image +```cpp +virtual void draw_image(litehtml::uint_ptr hdc, const background_layer& layer, const std::string& url, const std::string& base_url); +``` + +Draw the image in this function. The parameter ```layer``` describes the image position and size. The ```url``` and ```base_url``` parameters are the image source. + + +### draw_solid_fill +```cpp +virtual void draw_solid_fill(uint_ptr hdc, const background_layer& layer, const web_color& color); +``` + +Draw the solid color in the background. The ```layer``` parameter defines the position and size of the rectangle. The ```color``` parameter is the color to fill. + +### draw_linear_gradient +```cpp +virtual void draw_linear_gradient(uint_ptr hdc, const background_layer& layer, const background_layer::linear_gradient& gradient); +``` +Draw the linear gradient in the background. The ```layer``` parameter defines the position and size of the rectangle. The ```gradient``` parameter is the gradient to fill. + +The definition of the ```background_layer::linear_gradient``` structure: +```cpp +class linear_gradient : public gradient_base +{ +public: + pointF start; + pointF end; +}; +``` +* ```start``` - the start point of the gradient. +* ```end``` - the end point of the gradient. + +### draw_radial_gradient +```cpp +virtual void draw_radial_gradient(uint_ptr hdc, const background_layer& layer, const background_layer::radial_gradient& gradient); +``` +Draw the radial gradient in the background. The ```layer``` parameter defines the position and size of the rectangle. The ```gradient``` parameter is the gradient to fill. + +The definition of the ```background_layer::radial_gradient``` structure: +```cpp +class radial_gradient : public gradient_base +{ +public: + pointF position; + pointF radius; +}; +``` +* ```position``` - the center of the gradient. +* ```radius``` - the radius of the gradient. + +### draw_conic_gradient +```cpp +virtual void draw_conic_gradient(uint_ptr hdc, const background_layer& layer, const background_layer::conic_gradient& gradient); +``` +Draw the conic gradient in the background. The ```layer``` parameter defines the position and size of the rectangle. The ```gradient``` parameter is the gradient to fill. + +The definition of the ```background_layer::conic_gradient``` structure: +```cpp +class conic_gradient : public gradient_base +{ +public: + pointF position; + float angle; + float radius; +}; +``` +* ```position``` - the center of the conic gradient +* ```angle``` - the angle of the gradient in degrees, starting from 0 at the top and going clockwise +* ```radius``` - the distance from the center to the farthest corner of the background box + +### draw_borders +```cpp +virtual void draw_borders(uint_ptr hdc, const css_borders& borders, const litehtml::position& draw_pos, bool root); +``` + +Draw the element borders here. The parameter ```root``` is ```true``` if you have to draw the borders for the root element (usually this is ``````). + +### set_caption +```cpp +virtual void set_caption(const char* caption); +``` + +litehtml calls this function with `````` html tag text. You can use the ```caption``` parameter to set the window caption text into the html page title. + +### set_base_url +```cpp +virtual void set_base_url(const char* base_url); +``` + +litehtml calls this function for the ```<base>``` html tag to set the base url. Save this string for future use in the functions that get the ```baseurl``` parameter. + +### link +```cpp +virtual void link(const std::shared_ptr<litehtml::document>& doc, const litehtml::element::ptr& el); +``` + +This function is used to process the ```<link>``` html tags. Note, litehtml processes the stylesheets references automatically and calls import_css function to load CSS file. + +### on_anchor_click +```cpp +virtual void on_anchor_click(const char* url, litehtml::element::ptr el); +``` + +litehtml calls this function on anchor element click. You can open new website or do something other here. + +### on_element_click +```cpp +virtual bool on_element_click(const litehtml::element::ptr& el) +``` +litehtml calls this function on element click. + +### on_mouse_event +```cpp +virtual void on_mouse_event(const litehtml::element::ptr& el, litehtml::mouse_event event); +``` +litehtml calls this function on mouse event. The parameter **el** is the element that received the event. The **event** parameter is the type of the event: +- ```mouse_event_enter``` - mouse over the element. +- ```mouse_event_leave``` - mouse leave the element. + +### set_cursor +```cpp +virtual void set_cursor(const char* cursor); +``` +Define this function to handle the CSS cursor property. + +### transform_text +```cpp +virtual void transform_text(std::string& text, litehtml::text_transform tt); +``` +Transform the ```text``` parameter according the ```tt``` value: +- ```text_transform_capitalize``` - make the first char upper case. +- ```text_transform_uppercase``` - make all chars upper case. +- ```text_transform_lowercase``` - make all chars lower case. + +### import_css +```cpp +virtual void import_css(std::string& text, const std::string& url, std::string& baseurl); +``` + +litehtml calls this function to load stylesheet. You have to download CSS file referred by **url** and **baseurl** parameters and copy content into **text** parameter. + +### set_clip +```cpp +virtual void set_clip(const litehtml::position& pos, const litehtml::border_radiuses& bdr_radius); +``` + +Set the painting clip rectangle here. ```bdr_radius``` defines borders radius for rounded rectangles. Please note, litehtml can set some clip rects. You have to save the clip positions and apply clipping on draw something. + +### del_clip +```cpp +virtual void del_clip(); +``` + +Deletes the last clipping. + +### get_viewport +```cpp +virtual void get_viewport(litehtml::position& viewport) const; +``` + +Fill the parameter **viewport** with the viewport position and size. Usually this is the size of the client rectangle of the window where you want to draw html. + +### create_element +```cpp +virtual litehtml::element::ptr create_element( const char* tag_name, const litehtml::string_map& attributes, const std::shared_ptr<litehtml::document>& doc); +``` + +Using this function, you can process custom tags. Just make your own ```litehtml::element``` and return is from this function. Parameter ```tag_name``` is the HTML tag (a, p, table etc.). + +### get_media_features +```cpp +virtual void get_media_features(litehtml::media_features& media) +``` + +Fill the ```media``` with the parameters of media where you render HTML. litehtml process stylesheets using the media features. Call ```document::media_changed``` function when any media feature is changed (for example user is changed the window size). + +The definition of the ```media_features``` structure: +```cpp +struct media_features +{ + media_type type; + int width; + int height; + int device_width; + int device_height; + int color; + int color_index; + int monochrome; + int resolution; +}; +``` +* ```type``` - the type of the media. See enum ```media_type```. +* ```width``` - (pixels) For continuous media, this is the width of the viewport including the size of a rendered scroll bar (if any). For paged media, this is the width of the page box. +* ```height``` - (pixels) The height of the targeted display area of the output device. For continuous media, this is the height of the viewport including the size of a rendered scroll bar (if any). For paged media, this is the height of the page box. +* ```device_width``` - (pixels) The width of the rendering surface of the output device. For continuous media, this is the width of the screen. For paged media, this is the width of the page sheet size. +* ```device_height``` - (pixels) The height of the rendering surface of the output device. For continuous media, this is the height of the screen. For paged media, this is the height of the page sheet size. +* ```color``` - The number of bits per color component of the output device. If the device is not a color device, the value is zero. +* ```color_index``` - The number of entries in the color lookup table of the output device. If the device does not use a color lookup table, the value is zero. +* ```monochrome``` - The number of bits per pixel in a monochrome frame buffer. If the device is not a monochrome device, the output device value will be 0. +* ```resolution``` - The resolution of the output device (in DPI). + +### get_language +```cpp +virtual void get_language(litehtml::string& language, litehtml::string& culture) const; +``` + +Fill the **language** and **culture** parameters with the language and culture of the user. This is used to process the ```lang``` attribute of the HTML tags. + +### resolve_color +```cpp +virtual litehtml::string resolve_color(const litehtml::string& color) const; +``` +Resolve the color name to the color value. For example, ```red``` will be resolved to ```#FF0000```. If you don't need this, just return the empty string. + +**Note**: The standard colors are resolved in the litehtml library. You can use the ```resolve_color``` function to resolve the custom colors. + +### split_text +```cpp +virtual void split_text(const char* text, const std::function<void(const char*)>& on_word, const std::function<void(const char*)>& on_space); +``` +This function is used to split the text into words and spaces. The ```text``` parameter is the text to split. The ```on_word``` and ```on_space``` parameters are the functions to call for each word and space respectively. The functions are called with the pointer to the word or space. + +**Note**: The default implementation splits the text with this condition: +```cpp +if (c <= ' ' && (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f')) +``` diff --git a/doc/document_createFromString.md b/doc/document_createFromString.md new file mode 100644 index 000000000..1b77e3489 --- /dev/null +++ b/doc/document_createFromString.md @@ -0,0 +1,75 @@ +```cpp +static document::ptr document::createFromString( + const estring& str, + document_container* container, + const string& master_styles = litehtml::master_css, + const string& user_styles = ""); +``` + +### Terminology: + +**BOM encoding** is the encoding suggested by the byte-order-mark (BOM). Can be UTF-8, UTF-16LE, or UTF-16BE. + Cannot be UTF-32 because it is not a valid HTML encoding. See bom_sniff. + +**meta encoding** is an HTML encoding suggested by a valid <meta> charset tag. + +valid <meta> charset tag: +* must be inside \<head> +* must have one of these forms: + * \<meta charset="utf-8"> or + * \<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> +* encoding name must be one of the encoding labels https://encoding.spec.whatwg.org/#names-and-labels (see get_encoding) + +**HTTP encoding** is the encoding specified in HTTP Content-Type header https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type + +**user override encoding** - when your program allows user to manually choose encoding for particular page or site + +--------------------------------------------------------------------------------------------------------- + + +### Call without specifying encoding: +```cpp +createFromString(string, container); +``` +where string is ```std::string``` or ```char*``` +* if BOM is present, BOM encoding will be used +* otherwise, if valid <meta> tag is present, meta encoding will be used +* otherwise, UTF-8 will be used + +### Call with encoding, confidence is certain: +```cpp +createFromString({string, encoding::big5}, container) +``` +* if BOM is present, BOM encoding will be used +* otherwise, Big5 will be used + +**NOTE**: encoding from <meta> tag will be ignored + +### Call with encoding, confidence is tentative (very rare, you probably don't need this): +```cpp +createFromString({string, encoding::big5, confidence::tentative}, container) +``` +* if BOM is present, BOM encoding will be used +* otherwise, if valid <meta> tag is present, meta encoding will be used +* otherwise, Big5 will be used + +--------------------------------------------------------------------------------------------------------- + +User override encoding and HTTP encoding must be passed with confidence certain, if both are present user +override encoding should take precedence. + +If both user override encoding and HTTP encoding are unspecified, your program may guess encoding by using +encoding of the page when it was last visited or by performing frequency analysis or by URL domain or +by current user locale or smth else. Any such encoding should be passed with confidence tentative. +The precedence of these guesses is specified in the encoding sniffing algorithm, see litehtml::encoding_sniffing_algorithm +and https://html.spec.whatwg.org/multipage/parsing.html#encoding-sniffing-algorithm + +litehtml implements only the 3 steps of this algorithm: +1. set encoding to BOM encoding if BOM is present +5. prescan the input to determine its encoding - this only called if no BOM found and user didn't specify encoding or specified tentative encoding +9. return an implementation-defined default character encoding (UTF-8) - this only called if there is no BOM, user didn't specify an encoding and + prescan failed. + +If your program is displaying html files from the web it is recommended to detect HTTP encoding, because +it is not very unusual for web pages to have encoding specified only in HTTP header or meta encoding be different +from HTTP encoding (HTTP encoding takes the precedence in this case). diff --git a/doc/using.md b/doc/using.md new file mode 100644 index 000000000..ddf5e5638 --- /dev/null +++ b/doc/using.md @@ -0,0 +1,125 @@ +# How to use litehtml + +To use litehtml, you need some easy steps: + +1. Implement [litehtml::document_container](document_container.md) class. +2. Call some functions from **litehtml::document** to parse, render and draw HTML text. + +## Implementing litehtml::document_container + +The first step to integrate litehtml into your application is implementing abstract class [litehtml::document_container](document_container.md). This class draws the HTML elements and performs some actions depended on the platform. + +## Loading HTML page + +To load HTML page you have to call **litehtml::document::createFromString** function. This function creates the litehtml::document object from the HTML string. The HTML string must be in UTF-8 encoding. The function has the following signature: + +```cpp +static document::ptr document::createFromString( + const estring& str, + document_container* container, + const string& master_styles = litehtml::master_css, + const string& user_styles = ""); +``` + +Please refer to the [document::createFromString](document_createFromString.md) document for more details. + +**createFromString** returns the ```litehtml::document``` pointer. Call ```litehtml::document::render(max_width)``` to render HTML elements: +```cpp +m_doc->render(max_width); +``` + +The parameter ```max_width``` usually the browser window width. Also ```render``` returns the optimal width for HTML text. You can use the returned value to render elements into the optimal width. This can be useful for tooltips. + +Use ```height``` and ```width``` ```litehtml::document``` functions to find the width and height of the rendered document: +```cpp +m_doc->height() +m_doc->width() +``` +Now call **draw** function: +```cpp +m_doc->draw(hdc, m_left, m_top, &clip); +``` +draw accepts 4 parameters: +* ```uint_ptr hdc``` - usually device context or something where to draw. For example HDC in windows. This parameter will be passed into all draw functions of [litehtml::document_container](document_container.md). +* ```int x, int y``` - position where to draw HTML. +* ```const position* clip*``` - clipping area. litehtml does not draw the elements that don't intersect the clipping rectangle. But note, you have to set your own clipping if you want to clip HTML content. + +That's all! Your HTML page is painted! + +## Fit to the document + +If you don't have the fixed size window to draw HTML, you need to get the HTML *best* width. It is straightforward: ```document::render``` returns the best width for you. + +So the example looks like this: +```cpp +int best_width = m_doc->render(max_width); +if(best_width < max_width) +{ + m_doc->render(best_width); +} +``` +First, you render document with the maximum possible width and ```document::render``` returns the best width for HTML. If the best width is less then maximum width, you have to render your document with the returned *best width*. Now you can use ```document::width``` to get the actual document width. + +You can use this technique to show HTML tooltips in your application or to create the HTML widgets. + +## Scrolling and Clipping + +Scrolling of html document is released via x,y parameters of the document::draw function. For example, if you want to scroll document on 50px horizontally and 100px vertically: +```cpp +m_doc->draw(hdc, -50, -100, litehtml::position(50, 100, window_width, window_height)); +``` + +Note, the ```x``` and ```y``` parameters are screen relative. The ```clip``` parameter is always document relative. Also note, defining the ```clip``` parameter in ```draw``` does not guarantee the valid clipping. Some elements can be drawn out off the clipping area. litehtml just checks if the element's bounding rectangle is intersected with clipping rectangle to draw elements. So you must implement your own clipping. + +## Handling the mouse +If you want to handle the mouse you have call some functions from ```litehtml::document```: +```cpp +bool litehtml::document::on_mouse_over( int x, int y, int client_x, int client_y, position::vector& redraw_boxes ); +bool litehtml::document::on_mouse_leave( position::vector& redraw_boxes ); +bool litehtml::document::on_lbutton_down( int x, int y, int client_x, int client_y, position::vector& redraw_boxes ); +bool litehtml::document::on_lbutton_up( int x, int y, int client_x, int client_y, position::vector& redraw_boxes ); +bool on_mouse_leave(position::vector& redraw_boxes); +``` +All functions returns the ```bool``` to indicate that you have to redraw the rectangles from *redraw_boxes* vector. Also note the ```x``` and ```y``` are relative to the HTML layout. So ```0,0``` is the top-left corner. +The parameters ```client_x``` and ```client_y``` are the mouse position in the client area (draw area). These parameters are used to handle the elements with **fixed** position. + +## Processing anchor click + +If you process the mouse, the clicking on anchor tag will call the function [document_container::on_anchor_click](document_container.md#on_anchor_click). This function gets the url of the anchor as parameter and pointer to litehtml::element. You can open the new document at this point. + +**NOTE:** Don't delete the document inside [document_container::on_anchor_click](document_container.md#on_anchor_click), this will cause the crash. The [document_container::on_anchor_click](document_container.md#on_anchor_click) is generated by ```document::on_lbutton_up```, so it is safe to delete old document and show new one after ```document::on_lbutton_up``` is finished. + +## Processing named anchors + +litehtml does not have the special handling of the named anchors. But it is easy to process the named anchors manually. You get the url with the anchor name into [document_container::on_anchor_click](document_container.md#on_anchor_click). Find the hash char (\#) and extract the name. In the example below, the variable ```anchor_name``` contains the anchor name: +```cpp +if(anchor_name.empty()) +{ + std::string selector = "#" + anchor_name; + litehtml::element::ptr el = m_html->root()->select_one(selector); + if (!el) + { + selector = "[name=" + anchor_name + "]"; + el = m_html->root()->select_one(selector); + } + if (el) + { + litehtml::position pos = el->get_placement(); + m_html_host->scroll_to(0, pos.top()); + } +} +``` +The function ```element::select_one``` returns the first element for the CSS selector. So ```select_one("#name")``` returns the element with attribute ```id="name"```. ```el->get_placement()``` returns the absolute position of the element (document relative coordinates). + +So, you have the position of the named anchor and now you can scroll your document into this position. + +## Images + +litehtml does not cache the images. So if you want to increase the performance, you have to create your own images cache. Also, you can implement the delayed images loading. Start image downloading in [document_container::load_image](document_container.md#load_image) function. If the image is not loaded return fake sizes and draw placeholder. When the image is loaded call ```litehtml::document::render```, and redraw the page. + +## Support for CSS media features + +litehtml supports CSS ```@media``` at-rule as well are ```media``` attribute in the ```<link>``` and ```<style>``` html tags. To make CSS media support you need: + +1. Implement [document_container::get_media_features](document_container.md#get_media_features) function and fill the ```media``` parameter with valid media features (like width, height etc.). +2. Call ```document::media_changed``` function when any media feature is changed (for example user is changed the window size). \ No newline at end of file diff --git a/include/litehtml.h b/include/litehtml.h index 2537aee83..5687703df 100644 --- a/include/litehtml.h +++ b/include/litehtml.h @@ -7,5 +7,6 @@ #include <litehtml/stylesheet.h> #include <litehtml/element.h> #include <litehtml/utf8_strings.h> +#include <litehtml/document_container.h> #endif // LITEHTML_H diff --git a/include/litehtml/background.h b/include/litehtml/background.h index c56443d54..51db39d00 100644 --- a/include/litehtml/background.h +++ b/include/litehtml/background.h @@ -6,13 +6,102 @@ #include "css_position.h" #include "web_color.h" #include "borders.h" +#include "gradient.h" namespace litehtml { + class background_layer + { + public: + // border_box defines draw boundary. Everything must be drawn inside this rectangle only. + position border_box; + // border_radius defines radius of the border_box. + border_radiuses border_radius; + // clip_box defines clipping rectangle. Works like border_box. Container must set additional clipping. + position clip_box; + // origin_box defines origin rectangle. + position origin_box; + background_attachment attachment; + background_repeat repeat; + // is_root is true for root element. Container can use this flag to apply background to the top window. + bool is_root; + + background_layer() : + attachment(background_attachment_scroll), + repeat(background_repeat_repeat), + is_root(false) + {} + + class image + { + public: + string url; + string base_url; + }; + + struct color_point + { + float offset; + web_color color; + optional<float> hint; + color_point() { offset = 0.0; } + color_point(float _offset, web_color _color) : offset(_offset), color(_color) {} + }; + + class color + { + public: + web_color color; + }; + + class gradient_base + { + public: + vector<color_point> color_points; + color_space_t color_space = color_space_none; + hue_interpolation_t hue_interpolation = hue_interpolation_none; + + void color_points_transparent_fix(); + bool prepare_color_points(float len, string_id grad_type, const vector<gradient::color_stop>& colors); + }; + + class linear_gradient : public gradient_base + { + public: + pointF start; + pointF end; + }; + + class radial_gradient : public gradient_base + { + public: + pointF position; + pointF radius; + }; + + class conic_gradient : public gradient_base + { + public: + pointF position; // position is the center of the conic gradient + float angle = 0; // angle is the angle of the gradient in degrees, starting from 0 at the top and going clockwise + float radius = 0; // radius is the distance from the center to the farthest corner of the background box + }; + }; + class background { public: - string_vector m_image; + enum layer_type + { + type_none, + type_color, + type_image, + type_linear_gradient, + type_radial_gradient, + type_conic_gradient, + }; + + vector<image> m_image; string m_baseurl; web_color m_color; int_vector m_attachment; @@ -25,45 +114,33 @@ namespace litehtml bool is_empty() const { - if(m_color.alpha != 0) return false; - if(m_image.empty()) return true; + if(m_color.alpha != 0) + return false; + if(m_image.empty()) + return true; for(const auto& img : m_image) { - if(!img.empty()) return false; + if(!img.is_empty()) return false; } return true; } - }; - - class background_paint - { - public: - string image; - string baseurl; - background_attachment attachment; - background_repeat repeat; - web_color color; - position clip_box; - position origin_box; - position border_box; - border_radiuses border_radius; - size image_size; - int position_x; - int position_y; - bool is_root; - - public: - background_paint() + int get_layers_number() const { - attachment = background_attachment_scroll; - repeat = background_repeat_repeat; - color = web_color::transparent; - position_x = 0; - position_y = 0; - is_root = false; + if(m_color != web_color::transparent) + { + return (int) m_image.size() + 1; + } + return (int) m_image.size(); } + bool get_layer(int idx, position pos, const element* el, const std::shared_ptr<render_item>& ri, background_layer& layer) const; + layer_type get_layer_type(int idx) const; + std::unique_ptr<background_layer::image> get_image_layer(int idx) const; + std::unique_ptr<background_layer::color> get_color_layer(int idx) const; + std::unique_ptr<background_layer::linear_gradient> get_linear_gradient_layer(int idx, const background_layer& layer) const; + std::unique_ptr<background_layer::radial_gradient> get_radial_gradient_layer(int idx, const background_layer& layer) const; + std::unique_ptr<background_layer::conic_gradient> get_conic_gradient_layer(int idx, const background_layer& layer) const; + void draw_layer(uint_ptr hdc, int idx, const background_layer& layer, document_container* container) const; }; - } #endif // LH_BACKGROUND_H diff --git a/include/litehtml/borders.h b/include/litehtml/borders.h index 9c47abca0..7a14e869c 100644 --- a/include/litehtml/borders.h +++ b/include/litehtml/borders.h @@ -25,13 +25,7 @@ namespace litehtml color = val.color; } - css_border& operator=(const css_border& val) - { - width = val.width; - style = val.style; - color = val.color; - return *this; - } + css_border& operator=(const css_border& val) = default; string to_string() const; }; @@ -45,6 +39,7 @@ namespace litehtml border() { width = 0; + style = border_style_none; } border(const border& val) { @@ -58,13 +53,7 @@ namespace litehtml style = val.style; color = val.color; } - border& operator=(const border& val) - { - width = val.width; - style = val.style; - color = val.color; - return *this; - } + border& operator=(const border& val) = default; border& operator=(const css_border& val) { width = (int) val.width.val(); @@ -110,18 +99,7 @@ namespace litehtml bottom_left_x = val.bottom_left_x; bottom_left_y = val.bottom_left_y; } - border_radiuses& operator = (const border_radiuses& val) - { - top_left_x = val.top_left_x; - top_left_y = val.top_left_y; - top_right_x = val.top_right_x; - top_right_y = val.top_right_y; - bottom_right_x = val.bottom_right_x; - bottom_right_y = val.bottom_right_y; - bottom_left_x = val.bottom_left_x; - bottom_left_y = val.bottom_left_y; - return *this; - } + border_radiuses& operator = (const border_radiuses& val) = default; void operator += (const margins& mg) { top_left_x += mg.left; @@ -202,10 +180,7 @@ namespace litehtml css_length bottom_left_x; css_length bottom_left_y; - css_border_radius() - { - - } + css_border_radius() = default; css_border_radius(const css_border_radius& val) { @@ -219,18 +194,8 @@ namespace litehtml bottom_right_y = val.bottom_right_y; } - css_border_radius& operator=(const css_border_radius& val) - { - top_left_x = val.top_left_x; - top_left_y = val.top_left_y; - top_right_x = val.top_right_x; - top_right_y = val.top_right_y; - bottom_left_x = val.bottom_left_x; - bottom_left_y = val.bottom_left_y; - bottom_right_x = val.bottom_right_x; - bottom_right_y = val.bottom_right_y; - return *this; - } + css_border_radius& operator=(const css_border_radius& val) = default; + border_radiuses calc_percents(int width, int height) const { border_radiuses ret; @@ -271,15 +236,7 @@ namespace litehtml radius = val.radius; } - css_borders& operator=(const css_borders& val) - { - left = val.left; - right = val.right; - top = val.top; - bottom = val.bottom; - radius = val.radius; - return *this; - } + css_borders& operator=(const css_borders& val) = default; string to_string() const { return "left: " + left.to_string() + @@ -321,15 +278,7 @@ namespace litehtml return left.width != 0 || right.width != 0 || top.width != 0 || bottom.width != 0; } - borders& operator=(const borders& val) - { - left = val.left; - right = val.right; - top = val.top; - bottom = val.bottom; - radius = val.radius; - return *this; - } + borders& operator=(const borders& val) = default; borders& operator=(const css_borders& val) { diff --git a/include/litehtml/codepoint.h b/include/litehtml/codepoint.h index 52dd49555..3e06e86e0 100644 --- a/include/litehtml/codepoint.h +++ b/include/litehtml/codepoint.h @@ -30,10 +30,6 @@ #ifndef LITEHTML_CODEPOINT_H__ #define LITEHTML_CODEPOINT_H__ -#include <string> - -#include "os_types.h" - namespace litehtml { bool is_ascii_codepoint(char c); diff --git a/include/litehtml/css_length.h b/include/litehtml/css_length.h index ae787105a..235ff5458 100644 --- a/include/litehtml/css_length.h +++ b/include/litehtml/css_length.h @@ -2,9 +2,22 @@ #define LH_CSS_LENGTH_H #include "types.h" +#include "css_tokenizer.h" namespace litehtml { + // from_token options (flags) + enum { + f_length = 1, // <length> (includes unitless zero) + f_percentage = 2, // <percentage> + f_length_percentage = f_length | f_percentage, // <length-percentage> + f_number = 4, // <number> + f_integer = 8, // <integer> + f_positive = 16 // ⩾0 (non-negative) + }; + + // <length> | <percentage> | <number> | auto | none | normal + // https://developer.mozilla.org/en-US/docs/Web/CSS/length class css_length { union @@ -27,12 +40,11 @@ namespace litehtml float val() const; css_units units() const; int calc_percent(int width) const; - void fromString(const string& str, const string& predefs = "", int defValue = 0); - static css_length from_string(const string& str, const string& predefs = "", int defValue = 0); + bool from_token(const css_token& token, int options, const string& predefined_keywords = ""); string to_string() const; }; - using length_vector = std::vector<css_length>; + using length_vector = vector<css_length>; // css_length inlines diff --git a/include/litehtml/css_margins.h b/include/litehtml/css_margins.h index 17dc7698d..f23ea64ab 100644 --- a/include/litehtml/css_margins.h +++ b/include/litehtml/css_margins.h @@ -22,14 +22,7 @@ namespace litehtml bottom = val.bottom; } - css_margins& operator=(const css_margins& val) - { - left = val.left; - right = val.right; - top = val.top; - bottom = val.bottom; - return *this; - } + css_margins& operator=(const css_margins& val) = default; string to_string() const { diff --git a/include/litehtml/css_offsets.h b/include/litehtml/css_offsets.h index 5ab175ce2..bff3c8711 100644 --- a/include/litehtml/css_offsets.h +++ b/include/litehtml/css_offsets.h @@ -22,14 +22,7 @@ namespace litehtml bottom = val.bottom; } - css_offsets& operator=(const css_offsets& val) - { - left = val.left; - top = val.top; - right = val.right; - bottom = val.bottom; - return *this; - } + css_offsets& operator=(const css_offsets& val) = default; string to_string() const { diff --git a/include/litehtml/css_parser.h b/include/litehtml/css_parser.h new file mode 100644 index 000000000..eb49099fb --- /dev/null +++ b/include/litehtml/css_parser.h @@ -0,0 +1,53 @@ +#ifndef LH_CSS_PARSER_H +#define LH_CSS_PARSER_H + +#include "css_tokenizer.h" +#include "stylesheet.h" +#include <functional> + +namespace litehtml +{ + +class css_parser +{ + css_token_vector m_tokens; + int m_index = 0; + + css_token next_token(); + css_token peek_token(); + +public: + css_parser() {} + css_parser(const css_token_vector& tokens) : m_tokens(tokens) {} + + static raw_rule::vector parse_stylesheet(const string& input, bool top_level); + static raw_rule::vector parse_stylesheet(const css_token_vector& input, bool top_level); + raw_rule::vector consume_list_of_rules(bool top_level); + raw_rule::ptr consume_qualified_rule(); + raw_rule::ptr consume_at_rule(); + css_token consume_simple_block(char opening_bracket); + css_token consume_component_value(); + css_token consume_function(const string& name); + + raw_declaration consume_declaration(); + void consume_style_block_contents(/*out*/ raw_declaration::vector& decls, /*out*/ raw_rule::vector& rules); +}; + +using keep_whitespace_fn = std::function<bool (const css_token& left, const css_token& right)>; +void remove_whitespace(css_token_vector& tokens, keep_whitespace_fn keep_whitespace = 0); + +enum { + f_componentize = 1, + f_remove_whitespace = 2 +}; +template<class Input> +css_token_vector normalize(Input input, int options = 0, keep_whitespace_fn keep_whitespace = 0); + +vector<css_token_vector> parse_comma_separated_list(const css_token_vector& tokens); +bool is_declaration_value(const css_token_vector& tokens, int index = 0); +bool is_any_value(const css_token_vector& tokens); +bool skip_whitespace(const css_token_vector& tokens, int& index); + +} // namespace litehtml + +#endif // LH_CSS_PARSER_H \ No newline at end of file diff --git a/include/litehtml/css_properties.h b/include/litehtml/css_properties.h index a5609a9b1..a904b9cce 100644 --- a/include/litehtml/css_properties.h +++ b/include/litehtml/css_properties.h @@ -1,7 +1,7 @@ #ifndef LITEHTML_CSS_PROPERTIES_H #define LITEHTML_CSS_PROPERTIES_H -#include "os_types.h" +#include "string_id.h" #include "types.h" #include "css_margins.h" #include "borders.h" @@ -10,9 +10,22 @@ namespace litehtml { - class element; + class html_tag; class document; + template<class CssT, class CompT> + class css_property + { + public: + CssT css_value; + CompT computed_value; + + css_property(const CssT& css_val, const CompT& computed_val) : css_value(css_val), computed_value(computed_val) {} + }; + + // CSS Properties types + using css_line_height_t = css_property<css_length, int>; + class css_properties { private: @@ -22,6 +35,7 @@ namespace litehtml white_space m_white_space; style_display m_display; visibility m_visibility; + appearance m_appearance; box_sizing m_box_sizing; css_length m_z_index; vertical_align m_vertical_align; @@ -39,7 +53,7 @@ namespace litehtml css_offsets m_css_offsets; css_length m_css_text_indent; css_length m_css_line_height; - int m_line_height; + css_line_height_t m_line_height {{}, 0}; list_style_type m_list_style_type; list_style_position m_list_style_position; string m_list_style_image; @@ -48,9 +62,15 @@ namespace litehtml uint_ptr m_font; css_length m_font_size; string m_font_family; - font_weight m_font_weight; + css_length m_font_weight; font_style m_font_style; - string m_text_decoration; + int m_text_decoration_line = text_decoration_line_none; + text_decoration_style m_text_decoration_style = text_decoration_style_solid; + css_length m_text_decoration_thickness; + web_color m_text_decoration_color; + string m_text_emphasis_style; + web_color m_text_emphasis_color; + int m_text_emphasis_position; font_metrics m_font_metrics; text_transform m_text_transform; web_color m_color; @@ -75,9 +95,10 @@ namespace litehtml int m_order; private: - void compute_font(const element* el, const std::shared_ptr<document>& doc); - void compute_background(const element* el, const std::shared_ptr<document>& doc); - void compute_flex(const element* el, const std::shared_ptr<document>& doc); + void compute_font(const html_tag* el, const std::shared_ptr<document>& doc); + void compute_background(const html_tag* el, const std::shared_ptr<document>& doc); + void compute_flex(const html_tag* el, const std::shared_ptr<document>& doc); + web_color get_color_property(const html_tag* el, string_id name, bool inherited, web_color default_value, uint_ptr member_offset) const; public: css_properties() : @@ -87,6 +108,7 @@ namespace litehtml m_white_space(white_space_normal), m_display(display_inline), m_visibility(visibility_visible), + m_appearance(appearance_none), m_box_sizing(box_sizing_content_box), m_z_index(0), m_vertical_align(va_baseline), @@ -104,12 +126,11 @@ namespace litehtml m_css_offsets(), m_css_text_indent(), m_css_line_height(0), - m_line_height(0), m_list_style_type(list_style_type_none), m_list_style_position(list_style_position_outside), m_bg(), - m_font_size(0), m_font(0), + m_font_size(0), m_font_metrics(), m_text_transform(text_transform_none), m_border_collapse(border_collapse_separate), @@ -126,7 +147,7 @@ namespace litehtml m_order(0) {} - void compute(const element* el, const std::shared_ptr<document>& doc); + void compute(const html_tag* el, const std::shared_ptr<document>& doc); std::vector<std::tuple<string, string>> dump_get_attrs(); element_position get_position() const; @@ -147,6 +168,9 @@ namespace litehtml visibility get_visibility() const; void set_visibility(visibility mVisibility); + appearance get_appearance() const; + void set_appearance(appearance mAppearance); + box_sizing get_box_sizing() const; void set_box_sizing(box_sizing mBoxSizing); @@ -195,8 +219,8 @@ namespace litehtml const css_length &get_text_indent() const; void set_text_indent(const css_length &mCssTextIndent); - int get_line_height() const; - void set_line_height(int mLineHeight); + const css_line_height_t& line_height() const; + css_line_height_t& line_height_w(); list_style_type get_list_style_type() const; void set_list_style_type(list_style_type mListStyleType); @@ -204,10 +228,10 @@ namespace litehtml list_style_position get_list_style_position() const; void set_list_style_position(list_style_position mListStylePosition); - string get_list_style_image() const; + const string& get_list_style_image() const; void set_list_style_image(const string& url); - string get_list_style_image_baseurl() const; + const string& get_list_style_image_baseurl() const; void set_list_style_image_baseurl(const string& url); const background &get_bg() const; @@ -228,10 +252,10 @@ namespace litehtml web_color get_color() const; void set_color(web_color color); - string get_cursor() const; + const string& get_cursor() const; void set_cursor(const string& cursor); - string get_content() const; + const string& get_content() const; void set_content(const string& content); border_collapse get_border_collapse() const; @@ -258,6 +282,15 @@ namespace litehtml int get_order() const; void set_order(int order); + + int get_text_decoration_line() const; + text_decoration_style get_text_decoration_style() const; + const css_length& get_text_decoration_thickness() const; + const web_color& get_text_decoration_color() const; + + string get_text_emphasis_style() const; + web_color get_text_emphasis_color() const; + int get_text_emphasis_position() const; }; inline element_position css_properties::get_position() const @@ -320,6 +353,16 @@ namespace litehtml m_visibility = mVisibility; } + inline appearance css_properties::get_appearance() const + { + return m_appearance; + } + + inline void css_properties::set_appearance(appearance mAppearance) + { + m_appearance = mAppearance; + } + inline box_sizing css_properties::get_box_sizing() const { return m_box_sizing; @@ -480,14 +523,14 @@ namespace litehtml m_css_text_indent = mCssTextIndent; } - inline int css_properties::get_line_height() const + inline const css_line_height_t& css_properties::line_height() const { return m_line_height; } - inline void css_properties::set_line_height(int mLineHeight) + inline css_line_height_t& css_properties::line_height_w() { - m_line_height = mLineHeight; + return m_line_height; } inline list_style_type css_properties::get_list_style_type() const @@ -510,10 +553,10 @@ namespace litehtml m_list_style_position = mListStylePosition; } - inline string css_properties::get_list_style_image() const { return m_list_style_image; } + inline const string& css_properties::get_list_style_image() const { return m_list_style_image; } inline void css_properties::set_list_style_image(const string& url) { m_list_style_image = url; } - inline string css_properties::get_list_style_image_baseurl() const { return m_list_style_image_baseurl; } + inline const string& css_properties::get_list_style_image_baseurl() const { return m_list_style_image_baseurl; } inline void css_properties::set_list_style_image_baseurl(const string& url) { m_list_style_image_baseurl = url; } inline const background &css_properties::get_bg() const @@ -569,10 +612,10 @@ namespace litehtml inline web_color css_properties::get_color() const { return m_color; } inline void css_properties::set_color(web_color color) { m_color = color; } - inline string css_properties::get_cursor() const { return m_cursor; } + inline const string& css_properties::get_cursor() const { return m_cursor; } inline void css_properties::set_cursor(const string& cursor) { m_cursor = cursor; } - inline string css_properties::get_content() const { return m_content; } + inline const string& css_properties::get_content() const { return m_content; } inline void css_properties::set_content(const string& content) { m_content = content; } inline border_collapse css_properties::get_border_collapse() const @@ -668,6 +711,41 @@ namespace litehtml { m_order = order; } + + inline int css_properties::get_text_decoration_line() const + { + return m_text_decoration_line; + } + + inline text_decoration_style css_properties::get_text_decoration_style() const + { + return m_text_decoration_style; + } + + inline const css_length& css_properties::get_text_decoration_thickness() const + { + return m_text_decoration_thickness; + } + + inline const web_color& css_properties::get_text_decoration_color() const + { + return m_text_decoration_color; + } + + inline string css_properties::get_text_emphasis_style() const + { + return m_text_emphasis_style; + } + + inline web_color css_properties::get_text_emphasis_color() const + { + return m_text_emphasis_color; + } + + inline int css_properties::get_text_emphasis_position() const + { + return m_text_emphasis_position; + } } #endif //LITEHTML_CSS_PROPERTIES_H diff --git a/include/litehtml/css_selector.h b/include/litehtml/css_selector.h index 4fe7db9e5..57aa2846b 100644 --- a/include/litehtml/css_selector.h +++ b/include/litehtml/css_selector.h @@ -1,8 +1,10 @@ #ifndef LH_CSS_SELECTOR_H #define LH_CSS_SELECTOR_H +#include "string_id.h" #include "style.h" #include "media_query.h" +#include "css_tokenizer.h" namespace litehtml { @@ -121,105 +123,92 @@ namespace litehtml { select_class, select_id, - - select_exists, - select_equal, - select_contain_str, - select_start_str, - select_end_str, - + select_attr, select_pseudo_class, select_pseudo_element, }; + // https://www.w3.org/TR/selectors-4/#attribute-selectors + enum attr_matcher : char + { + attribute_exists = 0, + attribute_equals = '=', + attribute_contains_string = '*', // *= + attribute_contains_word = '~', // ~= + attribute_starts_with_string = '^', // ^= + attribute_starts_with_string_hyphen = '|', // |= + attribute_ends_with_string = '$', // $= + }; + ////////////////////////////////////////////////////////////////////////// - class css_element_selector; + class css_selector; + class html_tag; + // <subclass-selector> | <pseudo-element-selector> + // = <id-selector> | <class-selector> | <attribute-selector> | <pseudo-class-selector> | <pseudo-element-selector> struct css_attribute_selector { - typedef std::vector<css_attribute_selector> vector; + using vector = std::vector<css_attribute_selector>; attr_select_type type; - string_id name; // .name, #name, [name], :name - string val; // [name=val], :lang(val) + string_id prefix; // [prefix|name] + string_id name; // .name, #name, [name], :name + string value; // [name=value], :lang(value) - std::shared_ptr<css_element_selector> sel; // :not(sel) - int a, b; // :nth-child(an+b) + attr_matcher matcher; // <attr-matcher> = ~= |= ^= $= *= + bool caseless_match; // value is matched ASCII case-insensitively - css_attribute_selector() - { - type = select_class; - name = empty_id; - a = b = 0; - } + std::vector<shared_ptr<css_selector>> selector_list; // :not(selector_list) + int a, b; // :nth-child(an+b of selector_list) + + css_attribute_selector(attr_select_type type = select_class, string name = "") + : type(type), prefix(empty_id), name(_id(name)), matcher(), caseless_match(0), a(0), b(0) {} + + operator bool() const { return name != empty_id; } }; ////////////////////////////////////////////////////////////////////////// - class css_element_selector + class css_element_selector // compound selector: div.class:hover { public: - string_id m_tag; - css_attribute_selector::vector m_attrs; + using ptr = shared_ptr<css_element_selector>; public: - - void parse(const string& txt); - static void parse_nth_child_params(const string& param, int& num, int& off); + string_id m_prefix; + string_id m_tag; + css_attribute_selector::vector m_attrs; // subclass and pseudo-element selectors }; ////////////////////////////////////////////////////////////////////////// enum css_combinator { - combinator_descendant, - combinator_child, - combinator_adjacent_sibling, - combinator_general_sibling + combinator_descendant = ' ', + combinator_child = '>', + combinator_adjacent_sibling = '+', + combinator_general_sibling = '~' }; ////////////////////////////////////////////////////////////////////////// - class css_selector + class css_selector // complex selector: div + p { public: - typedef std::shared_ptr<css_selector> ptr; - typedef std::vector<css_selector::ptr> vector; - public: - selector_specificity m_specificity; - css_element_selector m_right; - css_selector::ptr m_left; - css_combinator m_combinator; - style::ptr m_style; - int m_order; - media_query_list::ptr m_media_query; - public: - explicit css_selector(const media_query_list::ptr& media = nullptr) - { - m_media_query = media; - m_combinator = combinator_descendant; - m_order = 0; - } + using ptr = shared_ptr<css_selector>; + using vector = std::vector<css_selector::ptr>; - ~css_selector() = default; - - css_selector(const css_selector& val) - { - m_right = val.m_right; - if(val.m_left) - { - m_left = std::make_shared<css_selector>(*val.m_left); - } else - { - m_left = nullptr; - } - m_combinator = val.m_combinator; - m_specificity = val.m_specificity; - m_order = val.m_order; - m_media_query = val.m_media_query; - } + public: + selector_specificity m_specificity; + int m_order = 0; + css_selector::ptr m_left; + css_element_selector m_right; + css_combinator m_combinator = combinator_descendant; + media_query_list_list::ptr m_media_query; + style::ptr m_style; - bool parse(const string& text); + public: + bool parse(const string& text, document_mode mode); void calc_specificity(); bool is_media_valid() const; void add_media_to_doc(document* doc) const; @@ -281,20 +270,16 @@ namespace litehtml m_used = used; m_selector = selector; } + }; - used_selector(const used_selector& val) - { - m_used = val.m_used; - m_selector = val.m_selector; - } - used_selector& operator=(const used_selector& val) - { - m_used = val.m_used; - m_selector = val.m_selector; - return *this; - } + enum { + strict_mode = 0, + forgiving_mode = 1, + forbid_pseudo_elements = 1 << 1, }; + + css_selector::vector parse_selector_list(const css_token_vector& tokens, int options, document_mode mode); } #endif // LH_CSS_SELECTOR_H diff --git a/include/litehtml/css_tokenizer.h b/include/litehtml/css_tokenizer.h new file mode 100644 index 000000000..f4a61af49 --- /dev/null +++ b/include/litehtml/css_tokenizer.h @@ -0,0 +1,218 @@ +#ifndef LH_CSS_TOKENIZER_H +#define LH_CSS_TOKENIZER_H + +#include "types.h" +#include <cstdio> + +namespace litehtml +{ + +// https://www.w3.org/TR/css-syntax-3/#tokenization +// :;,()[]{} token or delim token: type == this char +// EOF token: type == EOF (-1) +// type may be 0 to indicate an error, see at() +enum css_token_type +{ + WHITESPACE = ' ', + + // Giving EOF and some chars explicit names to facilitate debugging and to get rid of warning C4063: case '41' is not a valid value for switch of enum 'litehtml::css_token_type' + _EOF = EOF, + LEFT_BRACE = '{', + RIGHT_BRACE = '}', + LEFT_BRACKET = '[', + RIGHT_BRACKET = ']', + LEFT_PAREN = '(', + RIGHT_PAREN = ')', + COLON = ':', + SEMICOLON = ';', + COMMA = ',', + BANG = '!', + DOT = '.', + AMPERSAND = '&', + + IDENT = -20, // do not collide with any unicode chars + FUNCTION, // calc( + AT_KEYWORD, // @media + HASH, // #foo + STRING, // "xxx" or 'xxx' + BAD_STRING, + URL, // url(x.com) - but not url("x.com"), which is function + string + ')' + BAD_URL, + NUMBER, // 25 + PERCENTAGE, // 25% + DIMENSION, // 25px + CDO, // <!-- + CDC, // --> + + // https://www.w3.org/tr/css-syntax-3/#component-value + CV_FUNCTION = -100, + // simple block: + CURLY_BLOCK = -100 - '{', + ROUND_BLOCK = -100 - '(', + SQUARE_BLOCK = -100 - '[' +}; + +enum css_number_type +{ + css_number_integer, + css_number_number +}; + +enum css_hash_type +{ + css_hash_unrestricted, + css_hash_id +}; + +// css_token: CSS token or component value ("fat" token) +// Tokens exist in uncomponentized form only a short time after tokenization, most of the time they are "fat". +// All functions in css_parser work regardless of whether tokens are fat or not, as per standard. +// All functions outside of css_parser that parse media queries, selectors, property values assume tokens are componentized. +struct css_token +{ + css_token(css_token_type type = css_token_type(), + float number = 0, css_number_type number_type = css_number_integer, string str = "") + : type(type), str(str), n{number, number_type} + { + if (is_component_value()) new(&value) vector<css_token>; + } + + css_token(css_token_type type, const string& str) + : type(type), str(str), n() + { + if (is_component_value()) new(&value) vector<css_token>; + } + + css_token(const css_token& token) : type(token.type), str(token.str), repr(token.repr) + { + switch (type) + { + case HASH: + hash_type = token.hash_type; + break; + + case NUMBER: + case PERCENTAGE: + case DIMENSION: + n = token.n; + break; + + case CV_FUNCTION: + case CURLY_BLOCK: + case ROUND_BLOCK: + case SQUARE_BLOCK: + new(&value) vector(token.value); + break; + + default:; + } + } + + css_token& operator=(const css_token& token) + { + this->~css_token(); + new(this) css_token(token); + return *this; + } + + ~css_token() + { + str.~string(); + if (is_component_value()) value.~vector(); + } + + bool is_component_value() const + { + return type <= CV_FUNCTION; + } + + string ident() const; + string get_repr(bool insert_spaces = false) const; + + union { + css_token_type type; + int ch; // used for <delim-token> or :;,()[]{} + }; + union { + string str; // STRING, URL + string name; // HASH, IDENT, AT_KEYWORD, FUNCTION, CV_FUNCTION + string unit; // DIMENSION + }; + struct number { + float number; // NUMBER, PERCENTAGE, DIMENSION + css_number_type number_type; // NUMBER, DIMENSION + }; + union { + css_hash_type hash_type; // HASH + number n; + vector<css_token> value; // CV_FUNCTION, XXX_BLOCK + }; + + string repr; // https://www.w3.org/TR/css-syntax-3/#representation +}; + +using css_token_vector = vector<css_token>; +string get_repr(const css_token_vector& tokens, int index = 0, int count = -1, bool insert_spaces = false); + +class css_tokenizer +{ +public: + css_tokenizer(const string& input) : str(input), index(0), current_char(0) {} + + css_token_vector tokenize(); + +private: + // Input stream. Valid UTF-8; no NUL bytes. https://www.w3.org/TR/css-syntax-3/#input-stream + string str; + + // Index of the next input char. https://www.w3.org/TR/css-syntax-3/#next-input-code-point + int index; + + // https://www.w3.org/TR/css-syntax-3/#current-input-code-point + // This is needed to handle the situation when unconsume_char is called when index == str.size(). + // We need to distinguish between the situation when we just read the last char and + // the situation when we already have been at the end and just read NUL. + // If we don't do this tokenizer will loop forever on input "a". + int current_char; + +private: + static bool is_whitespace(int ch); + static bool is_non_printable_code_point(int ch); + static bool is_ident_start_code_point(int ch); + static bool is_ident_code_point(int ch); + + struct three_chars { int _1, _2, _3; }; + + int consume_char(); + void unconsume_char(); + int peek_char(); + three_chars peek_chars(); + + void consume_comments(); + int consume_escaped_code_point(); + css_token consume_string_token(int ending_code_point); + + static bool would_start_ident_sequence(three_chars chars); + string consume_ident_sequence(); + + static bool would_start_a_number(int x, int y, int z); + static double convert_string_to_number(const string& str); + double consume_number(css_number_type& number_type); + css_token consume_numeric_token(); + + void consume_remnants_of_bad_url(); + css_token consume_url_token(); + + css_token consume_ident_like_token(); + css_token consume_token(); +}; + +void css_parse_error(string msg); +inline css_token_vector tokenize(const string& str) +{ + return css_tokenizer(str).tokenize(); +} + +} // namespace litehtml + +#endif // LH_CSS_TOKENIZER_H \ No newline at end of file diff --git a/include/litehtml/document.h b/include/litehtml/document.h index e2623b222..9be846541 100644 --- a/include/litehtml/document.h +++ b/include/litehtml/document.h @@ -1,9 +1,13 @@ #ifndef LH_DOCUMENT_H #define LH_DOCUMENT_H -#include "style.h" +#include "stylesheet.h" #include "types.h" #include "master_css.h" +#include "encodings.h" +#include "font_description.h" + +typedef struct GumboInternalOutput GumboOutput; namespace litehtml { @@ -14,7 +18,7 @@ namespace litehtml string text; string baseurl; string media; - + css_text() = default; css_text(const char* txt, const char* url, const char* media_str) @@ -23,28 +27,21 @@ namespace litehtml baseurl = url ? url : ""; media = media_str ? media_str : ""; } - - css_text(const css_text& val) - { - text = val.text; - baseurl = val.baseurl; - media = val.media; - } }; - class dumper - { - public: - virtual ~dumper() {} - virtual void begin_node(const litehtml::string& descr) = 0; - virtual void end_node() = 0; - virtual void begin_attrs_group(const litehtml::string& descr) = 0; - virtual void end_attrs_group() = 0; - virtual void add_attr(const litehtml::string& name, const litehtml::string& value) = 0; - }; + class dumper + { + public: + virtual ~dumper() {} + virtual void begin_node(const string& descr) = 0; + virtual void end_node() = 0; + virtual void begin_attrs_group(const string& descr) = 0; + virtual void end_attrs_group() = 0; + virtual void add_attr(const string& name, const string& value) = 0; + }; class html_tag; - class render_item; + class render_item; class document : public std::enable_shared_from_this<document> { @@ -59,29 +56,32 @@ namespace litehtml css_text::vector m_css; litehtml::css m_styles; litehtml::web_color m_def_color; - litehtml::css m_master_css; - litehtml::css m_user_css; + litehtml::css m_master_css; + litehtml::css m_user_css; litehtml::size m_size; litehtml::size m_content_size; position::vector m_fixed_boxes; - media_query_list::vector m_media_lists; - element::ptr m_over_element; - std::list<std::shared_ptr<render_item>> m_tabular_elements; + std::shared_ptr<element> m_over_element; + std::shared_ptr<element> m_active_element; + std::list<shared_ptr<render_item>> m_tabular_elements; + media_query_list_list::vector m_media_lists; media_features m_media; string m_lang; string m_culture; + string m_text; + document_mode m_mode = no_quirks_mode; public: document(document_container* objContainer); virtual ~document(); document_container* container() { return m_container; } - uint_ptr get_font(const char* name, int size, const char* weight, const char* style, const char* decoration, font_metrics* fm); + document_mode mode() const { return m_mode; } + uint_ptr get_font(const font_description& descr, font_metrics* fm); int render(int max_width, render_type rt = render_all); void draw(uint_ptr hdc, int x, int y, const position* clip); web_color get_def_color() { return m_def_color; } - int to_pixels(const char* str, int fontSize, bool* is_percent = nullptr) const; - void cvt_units(css_length& val, int fontSize, int size = 0) const; - int to_pixels(const css_length& val, int fontSize, int size = 0) const; + void cvt_units(css_length& val, const font_metrics& metrics, int size) const; + int to_pixels(const css_length& val, const font_metrics& metrics, int size) const; int width() const; int height() const; int content_width() const; @@ -90,26 +90,34 @@ namespace litehtml bool on_mouse_over(int x, int y, int client_x, int client_y, position::vector& redraw_boxes); bool on_lbutton_down(int x, int y, int client_x, int client_y, position::vector& redraw_boxes); bool on_lbutton_up(int x, int y, int client_x, int client_y, position::vector& redraw_boxes); + bool on_button_cancel(position::vector& redraw_boxes); bool on_mouse_leave(position::vector& redraw_boxes); - element::ptr create_element(const char* tag_name, const string_map& attributes); - element::ptr root(); + std::shared_ptr<element> create_element(const char* tag_name, const string_map& attributes); + std::shared_ptr<element> root(); + std::shared_ptr<render_item> root_render(); void get_fixed_boxes(position::vector& fixed_boxes); void add_fixed_box(const position& pos); - void add_media_list(const media_query_list::ptr& list); + void add_media_list(media_query_list_list::ptr list); bool media_changed(); bool lang_changed(); bool match_lang(const string& lang); void add_tabular(const std::shared_ptr<render_item>& el); - element::const_ptr get_over_element() const { return m_over_element; } + std::shared_ptr<const element> get_over_element() const { return m_over_element; } void append_children_from_string(element& parent, const char* str); void dump(dumper& cout); - static litehtml::document::ptr createFromString(const char* str, litehtml::document_container* objPainter, const char* master_styles = litehtml::master_css, const char* user_styles = ""); - + // see doc/document_createFromString.txt + static document::ptr createFromString( + const estring& str, + document_container* container, + const string& master_styles = litehtml::master_css, + const string& user_styles = ""); + private: - uint_ptr add_font(const char* name, int size, const char* weight, const char* style, const char* decoration, font_metrics* fm); + uint_ptr add_font(const font_description& descr, font_metrics* fm); + GumboOutput* parse_html(estring str); void create_node(void* gnode, elements_list& elements, bool parseTextNode); bool update_media_lists(const media_features& features); void fix_tables_layout(); @@ -117,10 +125,16 @@ namespace litehtml void fix_table_parent(const std::shared_ptr<render_item> & el_ptr, style_display disp, const char* disp_str); }; - inline element::ptr document::root() + inline std::shared_ptr<element> document::root() { return m_root; } + + inline std::shared_ptr<render_item> document::root_render() + { + return m_root_render; + } + inline void document::add_tabular(const std::shared_ptr<render_item>& el) { m_tabular_elements.push_back(el); diff --git a/include/litehtml/document_container.h b/include/litehtml/document_container.h index 391873c1a..f706bd382 100644 --- a/include/litehtml/document_container.h +++ b/include/litehtml/document_container.h @@ -1,12 +1,12 @@ #ifndef LH_DOCUMENT_CONTAINER_H #define LH_DOCUMENT_CONTAINER_H -#include "os_types.h" #include "types.h" #include "web_color.h" #include "background.h" #include "borders.h" #include "element.h" +#include "font_description.h" #include <memory> #include <functional> @@ -23,11 +23,17 @@ namespace litehtml uint_ptr font; }; + enum mouse_event + { + mouse_event_enter, + mouse_event_leave, + }; + // call back interface to draw text, images and other elements class document_container { public: - virtual litehtml::uint_ptr create_font(const char* faceName, int size, int weight, litehtml::font_style italic, unsigned int decoration, litehtml::font_metrics* fm) = 0; + virtual litehtml::uint_ptr create_font(const font_description& descr, const document* doc, litehtml::font_metrics* fm) = 0; virtual void delete_font(litehtml::uint_ptr hFont) = 0; virtual int text_width(const char* text, litehtml::uint_ptr hFont) = 0; virtual void draw_text(litehtml::uint_ptr hdc, const char* text, litehtml::uint_ptr hFont, litehtml::web_color color, const litehtml::position& pos) = 0; @@ -37,23 +43,25 @@ namespace litehtml virtual void draw_list_marker(litehtml::uint_ptr hdc, const litehtml::list_marker& marker) = 0; virtual void load_image(const char* src, const char* baseurl, bool redraw_on_ready) = 0; virtual void get_image_size(const char* src, const char* baseurl, litehtml::size& sz) = 0; - // Note: regular <img> images are also drawn with draw_background - // bg is guaranteed to have at least one item. - // backgrounds in bg are in CSS order - the last one is the farthest from the user. - // only the last background has valid background-color. - virtual void draw_background(litehtml::uint_ptr hdc, const std::vector<litehtml::background_paint>& bg) = 0; + virtual void draw_image(litehtml::uint_ptr hdc, const background_layer& layer, const std::string& url, const std::string& base_url) = 0; + virtual void draw_solid_fill(litehtml::uint_ptr hdc, const background_layer& layer, const web_color& color) = 0; + virtual void draw_linear_gradient(litehtml::uint_ptr hdc, const background_layer& layer, const background_layer::linear_gradient& gradient) = 0; + virtual void draw_radial_gradient(litehtml::uint_ptr hdc, const background_layer& layer, const background_layer::radial_gradient& gradient) = 0; + virtual void draw_conic_gradient(litehtml::uint_ptr hdc, const background_layer& layer, const background_layer::conic_gradient& gradient) = 0; virtual void draw_borders(litehtml::uint_ptr hdc, const litehtml::borders& borders, const litehtml::position& draw_pos, bool root) = 0; virtual void set_caption(const char* caption) = 0; virtual void set_base_url(const char* base_url) = 0; virtual void link(const std::shared_ptr<litehtml::document>& doc, const litehtml::element::ptr& el) = 0; virtual void on_anchor_click(const char* url, const litehtml::element::ptr& el) = 0; + virtual bool on_element_click(const litehtml::element::ptr& /*el*/) { return false; }; + virtual void on_mouse_event(const litehtml::element::ptr& el, litehtml::mouse_event event) = 0; virtual void set_cursor(const char* cursor) = 0; virtual void transform_text(litehtml::string& text, litehtml::text_transform tt) = 0; virtual void import_css(litehtml::string& text, const litehtml::string& url, litehtml::string& baseurl) = 0; virtual void set_clip(const litehtml::position& pos, const litehtml::border_radiuses& bdr_radius) = 0; virtual void del_clip() = 0; - virtual void get_client_rect(litehtml::position& client) const = 0; + virtual void get_viewport(litehtml::position& viewport) const = 0; virtual litehtml::element::ptr create_element( const char* tag_name, const litehtml::string_map& attributes, const std::shared_ptr<litehtml::document>& doc) = 0; @@ -64,7 +72,7 @@ namespace litehtml virtual void split_text(const char* text, const std::function<void(const char*)>& on_word, const std::function<void(const char*)>& on_space); protected: - ~document_container() = default; + virtual ~document_container() = default; }; } diff --git a/include/litehtml/el_break.h b/include/litehtml/el_break.h index 2a4d8fe45..81f38fec0 100644 --- a/include/litehtml/el_break.h +++ b/include/litehtml/el_break.h @@ -11,7 +11,7 @@ namespace litehtml explicit el_break(const std::shared_ptr<litehtml::document>& doc); bool is_break() const override; - }; + }; } #endif // LH_EL_BREAK_H diff --git a/include/litehtml/el_cdata.h b/include/litehtml/el_cdata.h index 838cd92b1..3e9db43b8 100644 --- a/include/litehtml/el_cdata.h +++ b/include/litehtml/el_cdata.h @@ -1,7 +1,7 @@ #ifndef LH_EL_CDATA_H #define LH_EL_CDATA_H -#include "html_tag.h" +#include "element.h" namespace litehtml { diff --git a/include/litehtml/el_comment.h b/include/litehtml/el_comment.h index 454ee4787..fa8e72fc9 100644 --- a/include/litehtml/el_comment.h +++ b/include/litehtml/el_comment.h @@ -1,7 +1,7 @@ #ifndef LH_EL_COMMENT_H #define LH_EL_COMMENT_H -#include "html_tag.h" +#include "element.h" namespace litehtml { @@ -14,11 +14,11 @@ namespace litehtml bool is_comment() const override; void get_text(string& text) override; void set_data(const char* data) override; - std::shared_ptr<render_item> create_render_item(const std::shared_ptr<render_item>& parent_ri) override - { - // Comments are not rendered - return nullptr; - } + std::shared_ptr<render_item> create_render_item(const std::shared_ptr<render_item>& /*parent_ri*/) override + { + // Comments are not rendered + return nullptr; + } }; } diff --git a/include/litehtml/el_image.h b/include/litehtml/el_image.h index 2b7394081..b67de1194 100644 --- a/include/litehtml/el_image.h +++ b/include/litehtml/el_image.h @@ -2,6 +2,7 @@ #define LH_EL_IMAGE_H #include "html_tag.h" +#include "document.h" namespace litehtml { diff --git a/include/litehtml/el_script.h b/include/litehtml/el_script.h index e0a037405..0353b1579 100644 --- a/include/litehtml/el_script.h +++ b/include/litehtml/el_script.h @@ -1,7 +1,7 @@ #ifndef LH_EL_SCRIPT_H #define LH_EL_SCRIPT_H -#include "html_tag.h" +#include "element.h" namespace litehtml { diff --git a/include/litehtml/el_space.h b/include/litehtml/el_space.h index 46a292ebc..6d447f710 100644 --- a/include/litehtml/el_space.h +++ b/include/litehtml/el_space.h @@ -1,7 +1,6 @@ #ifndef LH_EL_SPACE_H #define LH_EL_SPACE_H -#include "html_tag.h" #include "el_text.h" namespace litehtml @@ -13,8 +12,8 @@ namespace litehtml bool is_white_space() const override; bool is_break() const override; - bool is_space() const override; - string dump_get_name() override; + bool is_space() const override; + string dump_get_name() override; }; } diff --git a/include/litehtml/el_style.h b/include/litehtml/el_style.h index 62f154625..60c0b50c6 100644 --- a/include/litehtml/el_style.h +++ b/include/litehtml/el_style.h @@ -1,7 +1,7 @@ #ifndef LH_EL_STYLE_H #define LH_EL_STYLE_H -#include "html_tag.h" +#include "element.h" namespace litehtml { @@ -13,6 +13,7 @@ namespace litehtml void parse_attributes() override; bool appendChild(const ptr &el) override; + void compute_styles(bool recursive) override; string_id tag() const override; const char* get_tagName() const override; }; diff --git a/include/litehtml/el_text.h b/include/litehtml/el_text.h index 4b8a442f3..f3da93082 100644 --- a/include/litehtml/el_text.h +++ b/include/litehtml/el_text.h @@ -1,7 +1,8 @@ #ifndef LH_EL_TEXT_H #define LH_EL_TEXT_H -#include "html_tag.h" +#include "element.h" +#include "document.h" namespace litehtml { @@ -18,11 +19,11 @@ namespace litehtml void get_text(string& text) override; void compute_styles(bool recursive) override; - bool is_text() const override { return true; } + bool is_text() const override { return true; } - void draw(uint_ptr hdc, int x, int y, const position *clip, const std::shared_ptr<render_item> &ri) override; - string dump_get_name() override; - std::vector<std::tuple<string, string>> dump_get_attrs() override; + void draw(uint_ptr hdc, int x, int y, const position *clip, const std::shared_ptr<render_item> &ri) override; + string dump_get_name() override; + std::vector<std::tuple<string, string>> dump_get_attrs() override; protected: void get_content_size(size& sz, int max_width) override; }; diff --git a/include/litehtml/element.h b/include/litehtml/element.h index 4d244736b..ef74af295 100644 --- a/include/litehtml/element.h +++ b/include/litehtml/element.h @@ -1,12 +1,12 @@ #ifndef LH_ELEMENT_H #define LH_ELEMENT_H +#include <functional> #include <memory> #include <tuple> #include <list> +#include "litehtml/types.h" #include "stylesheet.h" -#include "css_offsets.h" -#include "css_margins.h" #include "css_properties.h" namespace litehtml @@ -94,7 +94,7 @@ namespace litehtml virtual bool on_mouse_over(); virtual bool on_mouse_leave(); virtual bool on_lbutton_down(); - virtual bool on_lbutton_up(); + virtual bool on_lbutton_up(bool is_click = true); virtual void on_click(); virtual bool set_pseudo_class(string_id cls, bool add); virtual bool set_class(const char* pclass, bool add); @@ -102,20 +102,10 @@ namespace litehtml virtual void compute_styles(bool recursive = true); virtual void draw(uint_ptr hdc, int x, int y, const position *clip, const std::shared_ptr<render_item>& ri); virtual void draw_background(uint_ptr hdc, int x, int y, const position *clip, const std::shared_ptr<render_item> &ri); - virtual int get_enum_property (string_id name, bool inherited, int default_value, uint_ptr css_properties_member_offset) const; - virtual int get_int_property (string_id name, bool inherited, int default_value, uint_ptr css_properties_member_offset) const; - virtual css_length get_length_property(string_id name, bool inherited, css_length default_value, uint_ptr css_properties_member_offset) const; - virtual web_color get_color_property (string_id name, bool inherited, web_color default_value, uint_ptr css_properties_member_offset) const; - virtual string get_string_property(string_id name, bool inherited, const string& default_value, uint_ptr css_properties_member_offset) const; - virtual float get_number_property(string_id name, bool inherited, float default_value, uint_ptr css_properties_member_offset) const; - virtual string_vector get_string_vector_property(string_id name, bool inherited, const string_vector& default_value, uint_ptr css_properties_member_offset) const; - virtual int_vector get_int_vector_property (string_id name, bool inherited, const int_vector& default_value, uint_ptr css_properties_member_offset) const; - virtual length_vector get_length_vector_property(string_id name, bool inherited, const length_vector& default_value, uint_ptr css_properties_member_offset) const; - virtual size_vector get_size_vector_property (string_id name, bool inherited, const size_vector& default_value, uint_ptr css_properties_member_offset) const; - virtual string get_custom_property(string_id name, const string& default_value) const; virtual void get_text(string& text); virtual void parse_attributes(); + virtual int select(const css_selector::vector& selector_list, bool apply_pseudo = true); virtual int select(const string& selector); virtual int select(const css_selector& selector, bool apply_pseudo = true); virtual int select(const css_element_selector& selector, bool apply_pseudo = true); @@ -124,8 +114,8 @@ namespace litehtml virtual element::ptr find_adjacent_sibling(const element::ptr& el, const css_selector& selector, bool apply_pseudo = true, bool* is_pseudo = nullptr); virtual element::ptr find_sibling(const element::ptr& el, const css_selector& selector, bool apply_pseudo = true, bool* is_pseudo = nullptr); virtual void get_content_size(size& sz, int max_width); - virtual bool is_nth_child(const element::ptr& el, int num, int off, bool of_type) const; - virtual bool is_nth_last_child(const element::ptr& el, int num, int off, bool of_type) const; + virtual bool is_nth_child(const element::ptr& el, int num, int off, bool of_type, const css_selector::vector& selector_list = {}) const; + virtual bool is_nth_last_child(const element::ptr& el, int num, int off, bool of_type, const css_selector::vector& selector_list = {}) const; virtual bool is_only_child(const element::ptr& el, bool of_type) const; virtual void add_style(const style& style); virtual const background* get_background(bool own_only = false); @@ -165,7 +155,9 @@ namespace litehtml inline bool litehtml::element::in_normal_flow() const { - if(css().get_position() != element_position_absolute && css().get_display() != display_none) + if(css().get_position() != element_position_absolute && + css().get_display() != display_none && + css().get_position() != element_position_fixed) { return true; } @@ -217,8 +209,7 @@ namespace litehtml if (css().get_display() == display_block || css().get_display() == display_flex || css().get_display() == display_table || - css().get_display() == display_list_item || - css().get_display() == display_flex) + css().get_display() == display_list_item) { return true; } diff --git a/include/litehtml/encodings.h b/include/litehtml/encodings.h new file mode 100644 index 000000000..ec5cb03e8 --- /dev/null +++ b/include/litehtml/encodings.h @@ -0,0 +1,92 @@ +#ifndef LH_ENCODINGS_H +#define LH_ENCODINGS_H + +#include "types.h" +namespace litehtml +{ + +// https://encoding.spec.whatwg.org/#names-and-labels +enum class encoding +{ + null, // indicates error or absence of encoding + utf_8, + + // Legacy single-byte encodings; must be in sync with single_byte_indexes + ibm866, + iso_8859_2, + iso_8859_3, + iso_8859_4, + iso_8859_5, + iso_8859_6, + iso_8859_7, + iso_8859_8, + iso_8859_8_i, + iso_8859_10, + iso_8859_13, + iso_8859_14, + iso_8859_15, + iso_8859_16, + koi8_r, + koi8_u, + macintosh, + windows_874, + windows_1250, + windows_1251, + windows_1252, + windows_1253, + windows_1254, + windows_1255, + windows_1256, + windows_1257, + windows_1258, + x_mac_cyrillic, + + // Legacy multi-byte East Asian encodings + gbk, + gb18030, + big5, + euc_jp, + iso_2022_jp, + shift_jis, + euc_kr, + + // Legacy miscellaneous encodings + replacement, + utf_16be, + utf_16le, + x_user_defined +}; + +// https://html.spec.whatwg.org/multipage/parsing.html#concept-encoding-confidence +enum class confidence // encoding confidence +{ + tentative, + certain, + // irrelevant // not used here +}; + +// Used as argument for document::createFromString, parse_html and encoding_sniffing_algorithm. +struct estring : string // string with encoding +{ + litehtml::encoding encoding; + litehtml::confidence confidence; + + estring(const string& str, litehtml::encoding encoding = encoding::null, litehtml::confidence confidence = confidence::certain) + : string(str), encoding(encoding), confidence(confidence) {} + + estring(const char* str) : string(str), encoding(encoding::null), confidence(confidence::certain) {} +}; + + +encoding bom_sniff(const string& str); +void encoding_sniffing_algorithm(estring& str); + +encoding get_encoding(string label); +encoding extract_encoding_from_meta_element(string str); + +void decode(string input, encoding coding, string& output); +string decode(string input, encoding coding); + +} // namespace litehtml + +#endif // LH_ENCODINGS_H \ No newline at end of file diff --git a/include/litehtml/flex_item.h b/include/litehtml/flex_item.h index a2b3426f1..4ad53ac11 100644 --- a/include/litehtml/flex_item.h +++ b/include/litehtml/flex_item.h @@ -1,8 +1,8 @@ #ifndef LITEHTML_FLEX_ITEM_H #define LITEHTML_FLEX_ITEM_H -#include <functional> #include "formatting_context.h" +#include "render_item.h" namespace litehtml { @@ -33,21 +33,21 @@ namespace litehtml explicit flex_item(std::shared_ptr<render_item> &_el) : el(_el), - align(flex_align_items_auto), - grow(0), base_size(0), - shrink(0), min_size(0), - frozen(false), - main_size(0), max_size(0), + main_size(0), + grow(0), + shrink(0), + scaled_flex_shrink_factor(0), + frozen(false), order(0), src_order(0), - scaled_flex_shrink_factor(0), auto_margin_main_start(0), auto_margin_main_end(0), auto_margin_cross_start(false), - auto_margin_cross_end(false) + auto_margin_cross_end(false), + align(flex_align_items_auto) {} virtual ~flex_item() = default; diff --git a/include/litehtml/flex_line.h b/include/litehtml/flex_line.h index 4803d2386..c96df86bb 100644 --- a/include/litehtml/flex_line.h +++ b/include/litehtml/flex_line.h @@ -25,12 +25,12 @@ namespace litehtml bool reverse_cross; flex_line(bool _reverse_main, bool _reverse_cross) : - cross_size(0), cross_start(0), - total_grow(0), + main_size(0), + cross_size(0), base_size(0), + total_grow(0), total_shrink(0), - main_size(0), num_auto_margin_main_start(0), num_auto_margin_main_end(0), first_baseline(), diff --git a/include/litehtml/font_description.h b/include/litehtml/font_description.h new file mode 100644 index 000000000..c4c3ee722 --- /dev/null +++ b/include/litehtml/font_description.h @@ -0,0 +1,45 @@ +#ifndef LITEHTML_FONT_DESCRIPTION +#define LITEHTML_FONT_DESCRIPTION + +#include <string> +#include "types.h" +#include "css_length.h" +#include "web_color.h" + +namespace litehtml +{ + struct font_description + { + std::string family; // Font Family + int size = 0; // Font size + font_style style = font_style_normal; // Font stype, see the enum font_style + int weight; // Font weight. + int decoration_line = text_decoration_line_none; // Decoration line. A bitset of flags of the enum text_decoration_line + css_length decoration_thickness; // Decoration line thickness in pixels. See predefined values in enumtext_decoration_thickness + text_decoration_style decoration_style = text_decoration_style_solid; // Decoration line style. See enum text_decoration_style + web_color decoration_color = web_color::current_color; // Decoration line color + std::string emphasis_style; // Text emphasis style + web_color emphasis_color = web_color::current_color; // Text emphasis color + int emphasis_position = text_emphasis_position_over; // Text emphasis position + + std::string hash() const + { + std::string out; + out += family; + out += ":sz=" + std::to_string(size); + out += ":st=" + std::to_string(style); + out += ":w=" + std::to_string(weight); + out += ":dl=" + std::to_string(decoration_line); + out += ":dt=" + decoration_thickness.to_string(); + out += ":ds=" + std::to_string(decoration_style); + out += ":dc=" + decoration_color.to_string(); + out += ":ephs=" + emphasis_style; + out += ":ephc=" + emphasis_color.to_string(); + out += ":ephp=" + std::to_string(emphasis_position); + + return out; + } + }; +} + +#endif \ No newline at end of file diff --git a/include/litehtml/gradient.h b/include/litehtml/gradient.h new file mode 100644 index 000000000..1bfe77cde --- /dev/null +++ b/include/litehtml/gradient.h @@ -0,0 +1,209 @@ +#ifndef LITEHTML_GRADIENT_H +#define LITEHTML_GRADIENT_H + +#include "css_length.h" +#include "string_id.h" +#include "types.h" +#include "web_color.h" + +namespace litehtml +{ + enum gradient_side + { + gradient_side_none = 0, + gradient_side_left = 0x01, + gradient_side_right = 0x02, + gradient_side_top = 0x04, + gradient_side_bottom = 0x08, + gradient_side_x_center = 0x10, + gradient_side_y_center = 0x20, + gradient_side_x_length = 0x40, + gradient_side_y_length = 0x80, + }; + static_assert(gradient_side_left == (1 << background_position_left), "parse_bg_position is also used to parse radial gradient position"); + + enum radial_shape_t + { + radial_shape_none, + radial_shape_circle, + radial_shape_ellipse, + }; + +#define radial_extent_strings "closest-corner;closest-side;farthest-corner;farthest-side" + enum radial_extent_t + { + radial_extent_none, + radial_extent_closest_corner, + radial_extent_closest_side, + radial_extent_farthest_corner, + radial_extent_farthest_side, + }; + +#define color_space_strings \ + "srgb;"\ + "srgb-linear;"\ + "display-p3;"\ + "a98-rgb;"\ + "prophoto-rgb;"\ + "rec2020;"\ + "lab;"\ + "oklab;"\ + "xyz;"\ + "xyz-d50;"\ + "xyz-d65;"\ + "hsl;"\ + "hwb;"\ + "lch;"\ + "oklch" + + enum color_space_t + { + color_space_none, + + // rectangular-color-space + color_space_srgb, + color_space_srgb_linear, + color_space_display_p3, + color_space_a98_rgb, + color_space_prophoto_rgb, + color_space_rec2020, + color_space_lab, + color_space_oklab, + color_space_xyz, + color_space_xyz_d50, + color_space_xyz_d65, + + // polar-color-space + color_space_hsl, color_space_polar_start = color_space_hsl, + color_space_hwb, + color_space_lch, + color_space_oklch, + }; + +#define hue_interpolation_strings "shorter;longer;increasing;decreasing" + enum hue_interpolation_t + { + hue_interpolation_none, + hue_interpolation_shorter, + hue_interpolation_longer, + hue_interpolation_increasing, + hue_interpolation_decreasing + }; + + class gradient + { + public: + // https://drafts.csswg.org/css-images-4/#typedef-color-stop-list + // color_stop represents <linear-color-stop> | <linear-color-hint> | <angular-color-stop> | <angular-color-hint> + // <linear-color-stop> = <color> <length-percentage>{1,2}? + // <linear-color-hint> = <length-percentage> + // <angular-color-stop> = <color> <angle-percentage>{1,2}? + // <angular-color-hint> = <angle-percentage> + // If two lengths/angles are specified they create two color-stops with the same color. + class color_stop + { + public: + bool is_color_hint = false; + web_color color; + optional<css_length> length; + optional<float> angle; + + color_stop() {} + color_stop(web_color color) : color(color) {} + color_stop(web_color color, css_length length) : color(color), length(length) {} + color_stop(web_color color, float angle) : color(color), angle(angle) {} + color_stop(css_length length) : is_color_hint(true), length(length) {} + color_stop(float angle) : is_color_hint(true), angle(angle) {} + }; + + string_id m_type; + uint32_t m_side; + float angle; + vector<color_stop> m_colors; + css_length position_x; + css_length position_y; + radial_shape_t radial_shape; + radial_extent_t radial_extent; + css_length radial_radius_x; + css_length radial_radius_y; + float conic_from_angle; + color_space_t color_space; + hue_interpolation_t hue_interpolation; + + + explicit gradient(string_id type = empty_id) + { + m_type = type; + + // linear gradient: m_side default is gradient_side_none (use angle) + // radial,conic gradient: m_side default is gradient_side_x_center | gradient_side_y_center (see parse_gradient) + m_side = gradient_side_none; + // linear gradient angle, used when m_side == gradient_side_none + angle = 180; // to bottom + // radial,conic position (together with m_side) + position_x.predef(0); + position_y.predef(0); + + // actual default depends on the number of radii provided, see parse_radial_gradient_shape_size_position_interpolation + radial_shape = radial_shape_ellipse; + + // <radial-size> https://drafts.csswg.org/css-images-3/#valdef-radial-gradient-radial-size + // if radius is specified radial_extent will be set to none, see parse_radial_size + radial_extent = radial_extent_farthest_corner; + radial_radius_x.predef(0); + radial_radius_y.predef(0); + + conic_from_angle = 0; + + // If the host syntax does not define what color space interpolation should take place in, it defaults to Oklab. + color_space = color_space_oklab; + // Unless otherwise specified, if no specific hue interpolation algorithm is selected by the host syntax, the default is shorter. + hue_interpolation = hue_interpolation_shorter; + } + + bool is_empty() const + { + return m_type == empty_id || m_colors.empty(); + } + + bool is_linear() const { return m_type == _linear_gradient_ || m_type == _repeating_linear_gradient_; } + bool is_radial() const { return m_type == _radial_gradient_ || m_type == _repeating_radial_gradient_; } + bool is_conic() const { return m_type == _conic_gradient_ || m_type == _repeating_conic_gradient_; } + + static gradient transparent; + }; + + class image + { + public: + enum type + { + type_none, + type_url, + type_gradient, + }; + type type; + string url; + gradient m_gradient; + + image() : type(type_none) {} + + bool is_empty() const + { + switch (type) + { + case type_none: + return true; + case type_url: + return url.empty(); + case type_gradient: + return m_gradient.is_empty(); + } + return true; + } + }; + + bool parse_gradient(const css_token& token, gradient& gradient, document_container* container); +} + +#endif //LITEHTML_GRADIENT_H diff --git a/include/litehtml/html.h b/include/litehtml/html.h index 818d5ce56..9b725d9a6 100644 --- a/include/litehtml/html.h +++ b/include/litehtml/html.h @@ -1,59 +1,157 @@ #ifndef LH_HTML_H #define LH_HTML_H -#include <stdlib.h> -#include <string> -#include <ctype.h> -#include <vector> -#include <map> +#include <cstdlib> +#include <cmath> +#include <cctype> #include <cstring> #include <algorithm> -#include <functional> -#include "os_types.h" -#include "string_id.h" #include "types.h" -#include "utf8_strings.h" -#include "background.h" -#include "borders.h" -#include "web_color.h" #include "media_query.h" -#include "html_tag.h" -#include "document_container.h" -#include "document.h" namespace litehtml { - void trim(string &s, const string& chars_to_trim = " \n\r\t"); - void lcase(string &s); + const string whitespace = " \n\r\t\f"; + string& trim(string& s, const string& chars_to_trim = whitespace); + string trim(const string& s, const string& chars_to_trim = whitespace); + string& lcase(string& s); int value_index(const string& val, const string& strings, int defValue = -1, char delim = ';'); - string index_value(int index, const string& strings, char delim = ';'); + string index_value(int index, const string& strings, char delim = ';'); bool value_in_list(const string& val, const string& strings, char delim = ';'); - string::size_type find_close_bracket(const string &s, string::size_type off, char open_b = '(', char close_b = ')'); - void split_string(const string& str, string_vector& tokens, const string& delims, const string& delims_preserve = "", const string& quote = "\""); + string::size_type find_close_bracket(const string& s, string::size_type off, char open_b = '(', char close_b = ')'); + void split_string(const string& str, string_vector& tokens, const string& delims = whitespace, const string& delims_preserve = "", const string& quote = "\""); + string_vector split_string(const string& str, const string& delims = whitespace, const string& delims_preserve = "", const string& quote = "\""); void join_string(string& str, const string_vector& tokens, const string& delims); - double t_strtod(const char* string, char** endPtr = nullptr); - string get_escaped_string(const string& in_str); + double t_strtod(const char* string, char** endPtr = nullptr); + string get_escaped_string(const string& in_str); + + template<typename T, typename... Opts> + bool is_one_of(T val, Opts ...opts) + { + return (... || (val == opts)); + } + template<class T> + const T& at(const vector<T>& vec, int index /*may be negative*/) + { + static T invalid_item; // T's default constructor must create invalid item + if (index < 0) index += (int)vec.size(); + return index >= 0 && index < (int)vec.size() ? vec[index] : invalid_item; + } + template<class Map, class Key> + auto at(const Map& map, Key key) + { + static typename Map::mapped_type invalid_value; // mapped_type's default constructor must create invalid item + auto it = map.find(key); + return it != map.end() ? it->second : invalid_value; + } + template<typename T> + vector<T> slice(const vector<T>& vec, int index, int count = -1) + { + if (count == -1) count = (int)vec.size() - index; + return {vec.begin() + index, vec.begin() + index + count}; + } + template<class C> // C == vector or string + void remove(C& vec, int index /*may be negative*/, int count = 1) + { + if (index < 0) index += (int)vec.size(); + + if (!(index >= 0 && index < (int)vec.size())) + return; + + count = min(count, (int)vec.size() - index); + if (count <= 0) return; + + vec.erase(vec.begin() + index, vec.begin() + index + count); + } + template<class T> + void insert(vector<T>& vec, int index, const vector<T>& x) + { + vec.insert(vec.begin() + index, x.begin(), x.end()); + } + template<class T> + vector<T>& operator+=(vector<T>& vec, const vector<T>& x) + { + vec.insert(vec.end(), x.begin(), x.end()); + return vec; + } + template<class C, class T> + bool contains(const C& coll, const T& item) + { + return std::find(coll.begin(), coll.end(), item) != coll.end(); + } + inline bool contains(const string& str, const string& substr) + { + return str.find(substr) != string::npos; + } + template<class C> void sort(C& coll) { std::sort(coll.begin(), coll.end()); } int t_strcasecmp(const char *s1, const char *s2); int t_strncasecmp(const char *s1, const char *s2, size_t n); + inline bool equal_i(const string& s1, const string& s2) + { + if (s1.size() != s2.size()) return false; + return t_strncasecmp(s1.c_str(), s2.c_str(), s1.size()) == 0; + } + inline bool match(const string& str, int index /*may be negative*/, const string& substr) + { + if (index < 0) index += (int)str.size(); + if (index < 0) return false; + return str.substr(index, substr.size()) == substr; + } + inline bool match_i(const string& str, int index /*may be negative*/, const string& substr) + { + if (index < 0) index += (int)str.size(); + if (index < 0) return false; + return equal_i(str.substr(index, substr.size()), substr); + } - bool is_number(const string& string, const bool allow_dot = 1); + bool is_number(const string& string, bool allow_dot = true); - inline int t_isdigit(int c) + // https://infra.spec.whatwg.org/#ascii-whitespace + inline bool is_whitespace(int c) { - return (c >= '0' && c <= '9'); + return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f'; } inline int t_isalpha(int c) { return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); } + const auto is_letter = t_isalpha; inline int t_tolower(int c) { return (c >= 'A' && c <= 'Z' ? c + 'a' - 'A' : c); } - + // https://infra.spec.whatwg.org/#ascii-lowercase + inline int lowcase(int c) + { + return t_tolower(c); + } + inline string lowcase(string str) + { + for (char& c : str) c = (char)t_tolower(c); + return str; + } + + inline int t_isdigit(int c) + { + return (c >= '0' && c <= '9'); + } + const auto is_digit = t_isdigit; + + inline bool is_hex_digit(int ch) { + return is_digit(ch) || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F'); + } + + inline int digit_value(int ch) { + return is_digit(ch) ? ch - '0' : lowcase(ch) - 'a' + 10; + } + + inline bool is_surrogate(int ch) { + return ch >= 0xD800 && ch < 0xE000; + } + inline int round_f(float val) { int int_val = (int) val; diff --git a/include/litehtml/html_microsyntaxes.h b/include/litehtml/html_microsyntaxes.h new file mode 100644 index 000000000..04c7445ee --- /dev/null +++ b/include/litehtml/html_microsyntaxes.h @@ -0,0 +1,23 @@ +#ifndef LH_HTML_MICROSYNTAXES_H +#define LH_HTML_MICROSYNTAXES_H + +#include "types.h" + +namespace litehtml +{ + + bool html_parse_integer(const string& str, int& val); + bool html_parse_non_negative_integer(const string& str, int& val); + + enum html_dimension_type + { + html_length, + html_percentage + }; + + bool html_parse_dimension_value(const string& str, float& val, html_dimension_type& type); + bool html_parse_nonzero_dimension_value(const string& str, float& val, html_dimension_type& type); + +} // namespace litehtml + +#endif // LH_HTML_MICROSYNTAXES_H diff --git a/include/litehtml/html_tag.h b/include/litehtml/html_tag.h index b085d7dfb..d8c9dfa40 100644 --- a/include/litehtml/html_tag.h +++ b/include/litehtml/html_tag.h @@ -4,8 +4,6 @@ #include "element.h" #include "style.h" #include "background.h" -#include "css_margins.h" -#include "borders.h" #include "css_selector.h" #include "stylesheet.h" #include "line_box.h" @@ -21,31 +19,33 @@ namespace litehtml friend class table_grid; friend class line_box; public: - typedef std::shared_ptr<html_tag> ptr; + typedef shared_ptr<html_tag> ptr; protected: string_id m_tag; string_id m_id; string_vector m_str_classes; - std::vector<string_id> m_classes; - litehtml::style m_style; + vector<string_id> m_classes; + style m_style; string_map m_attrs; - std::vector<string_id> m_pseudo_classes; + vector<string_id> m_pseudo_classes; void select_all(const css_selector& selector, elements_list& res) override; public: - explicit html_tag(const std::shared_ptr<document>& doc); + explicit html_tag(const shared_ptr<document>& doc); // constructor for anonymous wrapper boxes explicit html_tag(const element::ptr& parent, const string& style = "display: block"); - bool appendChild(const element::ptr &el) override; - bool removeChild(const element::ptr &el) override; + bool appendChild(const element::ptr& el) override; + bool removeChild(const element::ptr& el) override; void clearRecursive() override; string_id tag() const override; string_id id() const override; const char* get_tagName() const override; void set_tagName(const char* tag) override; void set_data(const char* data) override; + const vector<string_id>& classes() const { return m_classes; } + const string_vector& str_classes() const { return m_str_classes; } void set_attr(const char* name, const char* val) override; const char* get_attr(const char* name, const char* def = nullptr) const override; @@ -59,7 +59,7 @@ namespace litehtml bool on_mouse_over() override; bool on_mouse_leave() override; bool on_lbutton_down() override; - bool on_lbutton_up() override; + bool on_lbutton_up(bool is_click) override; void on_click() override; bool set_pseudo_class(string_id cls, bool add) override; bool set_class(const char* pclass, bool add) override; @@ -69,22 +69,13 @@ namespace litehtml void draw_background(uint_ptr hdc, int x, int y, const position *clip, const std::shared_ptr<render_item> &ri) override; - template<class Type, property_type property_value_type, Type property_value::* property_value_member> - const Type& get_property_impl (string_id name, bool inherited, const Type& default_value, uint_ptr css_properties_member_offset) const; - int get_enum_property (string_id name, bool inherited, int default_value, uint_ptr css_properties_member_offset) const override; - int get_int_property (string_id name, bool inherited, int default_value, uint_ptr css_properties_member_offset) const override; - css_length get_length_property(string_id name, bool inherited, css_length default_value, uint_ptr css_properties_member_offset) const override; - web_color get_color_property (string_id name, bool inherited, web_color default_value, uint_ptr css_properties_member_offset) const override; - string get_string_property(string_id name, bool inherited, const string& default_value, uint_ptr css_properties_member_offset) const override; - float get_number_property(string_id name, bool inherited, float default_value, uint_ptr css_properties_member_offset) const override; - string_vector get_string_vector_property(string_id name, bool inherited, const string_vector& default_value, uint_ptr css_properties_member_offset) const override; - int_vector get_int_vector_property (string_id name, bool inherited, const int_vector& default_value, uint_ptr css_properties_member_offset) const override; - length_vector get_length_vector_property(string_id name, bool inherited, const length_vector& default_value, uint_ptr css_properties_member_offset) const override; - size_vector get_size_vector_property (string_id name, bool inherited, const size_vector& default_value, uint_ptr css_properties_member_offset) const override; - string get_custom_property(string_id name, const string& default_value) const override; + template<class Type> + const Type& get_property(string_id name, bool inherited, const Type& default_value, uint_ptr css_properties_member_offset) const; + bool get_custom_property(string_id name, css_token_vector& result) const; elements_list& children(); + int select(const css_selector::vector& selector_list, bool apply_pseudo = true) override; int select(const string& selector) override; int select(const css_selector& selector, bool apply_pseudo = true) override; int select(const css_element_selector& selector, bool apply_pseudo = true) override; @@ -106,21 +97,24 @@ namespace litehtml void get_content_size(size& sz, int max_width) override; void add_style(const style& style) override; - bool is_nth_child(const element::ptr& el, int num, int off, bool of_type) const override; - bool is_nth_last_child(const element::ptr& el, int num, int off, bool of_type) const override; + bool is_nth_child(const element::ptr& el, int num, int off, bool of_type, const css_selector::vector& selector_list) const override; + bool is_nth_last_child(const element::ptr& el, int num, int off, bool of_type, const css_selector::vector& selector_list) const override; bool is_only_child(const element::ptr& el, bool of_type) const override; const background* get_background(bool own_only = false) override; string dump_get_name() override; protected: - void init_background_paint(position pos, std::vector<background_paint>& bg_paint, const background* bg, const std::shared_ptr<render_item>& ri); - void init_one_background_paint(int i, position pos, background_paint& bg_paint, const background* bg, const std::shared_ptr<render_item>& ri); void draw_list_marker( uint_ptr hdc, const position &pos ); string get_list_marker_text(int index); element::ptr get_element_before(const style& style, bool create); element::ptr get_element_after(const style& style, bool create); + void map_to_pixel_length_property(string_id prop_name, string attr_value); + void map_to_pixel_length_property_with_default_value(string_id prop_name, string attr_value, int default_value); + void map_to_dimension_property(string_id prop_name, string attr_value); + void map_to_dimension_property_ignoring_zero(string_id prop_name, string attr_value); + private: void handle_counter_properties(); @@ -130,10 +124,31 @@ namespace litehtml /* Inline Functions */ /************************************************************************/ - inline elements_list& litehtml::html_tag::children() + inline elements_list& html_tag::children() { return m_children; } + + template<class Type> + const Type& html_tag::get_property(string_id name, bool inherited, const Type& default_value, uint_ptr css_properties_member_offset) const + { + const property_value& value = m_style.get_property(name); + + if (value.is<Type>()) + { + return value.get<Type>(); + } + else if (inherited || value.is<inherit>()) + { + if (auto _parent = parent()) + { + return *(Type*)((byte*)&_parent->css() + css_properties_member_offset); + } + return default_value; + } + return default_value; + } + } #endif // LH_HTML_TAG_H diff --git a/include/litehtml/iterators.h b/include/litehtml/iterators.h index b1a678d7b..b7e1887c1 100644 --- a/include/litehtml/iterators.h +++ b/include/litehtml/iterators.h @@ -2,7 +2,6 @@ #define LH_ITERATORS_H #include "types.h" -#include <list> #include <functional> namespace litehtml @@ -45,7 +44,7 @@ namespace litehtml ~elements_iterator() = default; void process(const std::shared_ptr<render_item>& container, const std::function<void (std::shared_ptr<render_item>&, iterator_item_type)>& func); - + private: void next_idx(); }; diff --git a/include/litehtml/line_box.h b/include/litehtml/line_box.h index 43f5d439e..eac00d13d 100644 --- a/include/litehtml/line_box.h +++ b/include/litehtml/line_box.h @@ -1,9 +1,8 @@ #ifndef LH_LINE_BOX_H #define LH_LINE_BOX_H -#include <vector> #include <memory> -#include "os_types.h" +#include "css_properties.h" #include "types.h" namespace litehtml @@ -25,6 +24,7 @@ namespace litehtml { calculatedTop = top; } + line_context() : calculatedTop(0), top(0), left(0), right(0) {} }; class line_box_item @@ -39,14 +39,16 @@ namespace litehtml }; protected: std::shared_ptr<render_item> m_element; - int m_rendered_min_width; + int m_rendered_min_width = 0; + int m_items_top = 0; + int m_items_bottom = 0; public: - explicit line_box_item(const std::shared_ptr<render_item>& element) : m_element(element), m_rendered_min_width(0) {} - line_box_item() = default; + explicit line_box_item(const std::shared_ptr<render_item>& element) : m_element(element) {} line_box_item(const line_box_item& el) = default; line_box_item(line_box_item&&) = default; + virtual ~line_box_item(); - int height() const { return right() - left(); } + virtual int height() const; const std::shared_ptr<render_item>& get_el() const { return m_element; } virtual position& pos(); virtual void place_to(int x, int y); @@ -58,6 +60,15 @@ namespace litehtml virtual element_type get_type() const { return type_text_part; } virtual int get_rendered_min_width() const { return m_rendered_min_width; } virtual void set_rendered_min_width(int min_width) { m_rendered_min_width = min_width; } + + void reset_items_height() { m_items_top = m_items_bottom = 0; } + void add_item_height(int item_top, int item_bottom) + { + m_items_top = std::min(m_items_top, item_top); + m_items_bottom = std::max(m_items_bottom, item_bottom); + } + int get_items_top() const { return m_items_top; } + int get_items_bottom() const { return m_items_bottom; } }; class lbi_start : public line_box_item @@ -66,8 +77,10 @@ namespace litehtml position m_pos; public: explicit lbi_start(const std::shared_ptr<render_item>& element); + ~lbi_start() override; void place_to(int x, int y) override; + int height() const override; int width() const override; position& pos() override { return m_pos; } int top() const override; @@ -82,6 +95,7 @@ namespace litehtml { public: explicit lbi_end(const std::shared_ptr<render_item>& element); + virtual ~lbi_end() override; void place_to(int x, int y) override; int right() const override; @@ -93,6 +107,7 @@ namespace litehtml { public: explicit lbi_continue(const std::shared_ptr<render_item>& element); + virtual ~lbi_continue() override; void place_to(int x, int y) override; int right() const override; @@ -105,10 +120,10 @@ namespace litehtml { struct va_context { - int baseline; + int line_height = 0; + int baseline = 0; font_metrics fm; - - va_context() : baseline(0) {} + line_box_item* start_lbi = nullptr; }; int m_top; @@ -116,24 +131,22 @@ namespace litehtml int m_right; int m_height; int m_width; - int m_line_height; - int m_default_line_height; + css_line_height_t m_default_line_height; font_metrics m_font_metrics; int m_baseline; text_align m_text_align; int m_min_width; std::list< std::unique_ptr<line_box_item> > m_items; public: - line_box(int top, int left, int right, int line_height, const font_metrics& fm, text_align align) : + line_box(int top, int left, int right, const css_line_height_t& line_height, const font_metrics& fm, text_align align) : m_top(top), m_left(left), m_right(right), m_height(0), m_width(0), - m_font_metrics(fm), m_default_line_height(line_height), + m_font_metrics(fm), m_baseline(0), - m_line_height(0), m_text_align(align), m_min_width(0) { diff --git a/include/litehtml/master_css.h b/include/litehtml/master_css.h index 0e7b0ca4e..ea532922d 100644 --- a/include/litehtml/master_css.h +++ b/include/litehtml/master_css.h @@ -4,32 +4,31 @@ namespace litehtml{ const char* const master_css = R"##( html { - display: block; - position: relative; + display: block; } head { - display: none + display: none } meta { - display: none + display: none } title { - display: none + display: none } link { - display: none + display: none } style { - display: none + display: none } script { - display: none + display: none } body { @@ -157,29 +156,29 @@ img[align="left"] } hr { - display: block; - margin-top: 0.5em; - margin-bottom: 0.5em; - margin-left: auto; - margin-right: auto; - border-style: inset; - border-width: 1px + display: block; + margin-top: 0.5em; + margin-bottom: 0.5em; + margin-left: auto; + margin-right: auto; + border-style: inset; + border-width: 1px } /***************** TABLES ********************/ table { - display: table; - border-collapse: separate; - border-spacing: 2px; - border-top-color:gray; - border-left-color:gray; - border-bottom-color:black; - border-right-color:black; - font-size: medium; - font-weight: normal; - font-style: normal; + display: table; + border-collapse: separate; + border-spacing: 2px; + border-top-color:gray; + border-left-color:gray; + border-bottom-color:black; + border-right-color:black; + font-size: medium; + font-weight: normal; + font-style: normal; } tbody, tfoot, thead { @@ -188,16 +187,16 @@ tbody, tfoot, thead { } tr { - display: table-row; - vertical-align: inherit; - border-color: inherit; + display: table-row; + vertical-align: inherit; + border-color: inherit; } td, th { - display: table-cell; - vertical-align: inherit; - border-width:1px; - padding:1px; + display: table-cell; + vertical-align: inherit; + border-width:1px; + padding:1px; } th { @@ -205,23 +204,23 @@ th { } table[border] { - border-style:solid; + border-style:solid; } -table[border|=0] { - border-style:none; +table[border^="0"] { + border-style:none; } table[border] td, table[border] th { - border-style:solid; - border-top-color:black; - border-left-color:black; - border-bottom-color:gray; - border-right-color:gray; + border-style:solid; + border-top-color:black; + border-left-color:black; + border-bottom-color:gray; + border-right-color:gray; } -table[border|=0] td, table[border|=0] th { - border-style:none; +table[border^="0"] td, table[border^="0"] th { + border-style:none; } table[align=left] { @@ -246,70 +245,70 @@ td[nowrap], th[nowrap] { } tt, code, kbd, samp { - font-family: monospace + font-family: monospace } pre, xmp, plaintext, listing { - display: block; - font-family: monospace; - white-space: pre; - margin: 1em 0 + display: block; + font-family: monospace; + white-space: pre; + margin: 1em 0 } /***************** LISTS ********************/ ul, menu, dir { - display: block; - list-style-type: disc; - margin-top: 1em; - margin-bottom: 1em; - margin-left: 0; - margin-right: 0; - padding-left: 40px + display: block; + list-style-type: disc; + margin-top: 1em; + margin-bottom: 1em; + margin-left: 0; + margin-right: 0; + padding-left: 40px } ol { - display: block; - list-style-type: decimal; - margin-top: 1em; - margin-bottom: 1em; - margin-left: 0; - margin-right: 0; - padding-left: 40px + display: block; + list-style-type: decimal; + margin-top: 1em; + margin-bottom: 1em; + margin-left: 0; + margin-right: 0; + padding-left: 40px } li { - display: list-item; + display: list-item; } ul ul, ol ul { - list-style-type: circle; + list-style-type: circle; } ol ol ul, ol ul ul, ul ol ul, ul ul ul { - list-style-type: square; + list-style-type: square; } dd { - display: block; - margin-left: 40px; + display: block; + margin-left: 40px; } dl { - display: block; - margin-top: 1em; - margin-bottom: 1em; - margin-left: 0; - margin-right: 0; + display: block; + margin-top: 1em; + margin-bottom: 1em; + margin-left: 0; + margin-right: 0; } dt { - display: block; + display: block; } ol ul, ul ol, ul ul, ol ol { - margin-top: 0; - margin-bottom: 0 + margin-top: 0; + margin-bottom: 0 } blockquote { diff --git a/include/litehtml/media_query.h b/include/litehtml/media_query.h index 4cb21fc2f..163191a51 100644 --- a/include/litehtml/media_query.h +++ b/include/litehtml/media_query.h @@ -1,76 +1,119 @@ #ifndef LH_MEDIA_QUERY_H #define LH_MEDIA_QUERY_H +#include "css_tokenizer.h" +#include "string_id.h" +#include "types.h" + namespace litehtml { - struct media_query_expression + // https://drafts.csswg.org/mediaqueries/#evaluating + enum trilean { - typedef std::vector<media_query_expression> vector; - media_feature feature; - int val; - int val2; - bool check_as_bool; - - media_query_expression() - { - check_as_bool = false; - feature = media_feature_none; - val = 0; - val2 = 0; - } + False, + True, + Unknown + }; + static_assert(False == false && True == true); // for casting bool to trilean - bool check(const media_features& features) const; + inline trilean operator!(trilean x) + { + if (x == False) return True; + if (x == True) return False; + return Unknown; + } + + inline trilean operator&&(trilean a, trilean b) + { + if (a == False || b == False) return False; + if (a == True && b == True) return True; + return Unknown; + } + + inline trilean operator||(trilean a, trilean b) + { + if (a == True || b == True) return True; + if (a == False && b == False) return False; + return Unknown; + } + + + struct media_condition; + + // <media-query> = <media-condition> | [ not | only ]? <media-type> [ and <media-condition-without-or> ]? + struct media_query + { + bool m_not = true; + media_type m_media_type = media_type_all; + vector<media_condition> m_conditions; + + trilean check(const media_features& features) const; }; - class media_query + struct media_in_parens; + + // <media-condition> = not <media-in-parens> | <media-in-parens> [ [and <media-in-parens>]* | [or <media-in-parens>]* ] + struct media_condition { - public: - typedef std::shared_ptr<media_query> ptr; - typedef std::vector<media_query::ptr> vector; - private: - media_query_expression::vector m_expressions; - bool m_not; - media_type m_media_type; - public: - media_query(); - media_query(const media_query& val); + string_id op = _and_; // _not_ _and_ _or_ + vector<media_in_parens> m_conditions; - static media_query::ptr create_from_string(const string& str, const std::shared_ptr<document>& doc); - bool check(const media_features& features) const; + trilean check(const media_features& features) const; }; - class media_query_list + // <media-feature> = ( [ <mf-plain> | <mf-boolean> | <mf-range> ] ) + // <mf-plain> = <mf-name> : <mf-value> + struct media_feature { - public: - typedef std::shared_ptr<media_query_list> ptr; - typedef std::vector<media_query_list::ptr> vector; - private: - media_query::vector m_queries; - bool m_is_used; - public: - media_query_list(); - media_query_list(const media_query_list& val); + string name; + float value = 0; + float value2 = 0; + short op = 0; + short op2 = 0; + + bool verify_and_convert_units(string_id syntax, css_token val[2] = 0, css_token val2[2] = 0, shared_ptr<document> doc = 0); - static media_query_list::ptr create_from_string(const string& str, const std::shared_ptr<document>& doc); - bool is_used() const; - bool apply_media_features(const media_features& features); // returns true if the m_is_used changed + bool compare(int x) const { return compare((float)x); } + bool compare(float x) const; + bool check(const media_features& features) const; }; - inline media_query_list::media_query_list(const media_query_list& val) + // <media-in-parens> = ( <media-condition> ) | <media-feature> | <general-enclosed> + struct unknown {}; // <general-enclosed> + struct media_in_parens : variant<media_condition, media_feature, unknown> { - m_is_used = val.m_is_used; - m_queries = val.m_queries; - } + using base::base; // inherit ctors + + trilean check(const media_features& features) const; + }; - inline media_query_list::media_query_list() + struct media_query_list { - m_is_used = false; - } + vector<media_query> m_queries; + bool empty() const { return m_queries.empty(); } + bool check(const media_features& features) const; + }; + media_query_list parse_media_query_list(const css_token_vector& tokens, shared_ptr<document> doc); + media_query_list parse_media_query_list(const string& str, shared_ptr<document> doc); + - inline bool media_query_list::is_used() const + class media_query_list_list { - return m_is_used; - } + public: + using ptr = shared_ptr<media_query_list_list>; + using vector = std::vector<ptr>; + private: + std::vector<media_query_list> m_media_query_lists; + bool m_is_used = false; + public: + void add(const media_query_list& mq_list) + { + m_media_query_lists.push_back(mq_list); + } + bool is_used() const { return m_is_used; } + + bool apply_media_features(const media_features& features); // returns true if the m_is_used changed + }; } diff --git a/include/litehtml/num_cvt.h b/include/litehtml/num_cvt.h index 0eaaa6886..bdfad8ecd 100644 --- a/include/litehtml/num_cvt.h +++ b/include/litehtml/num_cvt.h @@ -1,8 +1,7 @@ #ifndef NUM_CVT_H #define NUM_CVT_H -#include <string> -#include "os_types.h" +#include "types.h" namespace litehtml { diff --git a/include/litehtml/os_types.h b/include/litehtml/os_types.h index bbc2c3cf6..7300a49fa 100644 --- a/include/litehtml/os_types.h +++ b/include/litehtml/os_types.h @@ -1,21 +1,8 @@ #ifndef LH_OS_TYPES_H #define LH_OS_TYPES_H -#include <string> -#include <cstdint> - -namespace litehtml -{ - using std::string; - typedef std::uintptr_t uint_ptr; - #if defined( WIN32 ) || defined( _WIN32 ) || defined( WINCE ) -// noexcept appeared since Visual Studio 2015 -#if defined(_MSC_VER) && _MSC_VER < 1900 -#define noexcept -#endif - #define t_itoa(value, buffer, size, radix) _itoa_s(value, buffer, size, radix) #define t_snprintf(s, n, format, ...) _snprintf_s(s, _TRUNCATE, n, format, __VA_ARGS__) @@ -25,6 +12,5 @@ namespace litehtml #define t_snprintf(s, n, format, ...) snprintf(s, n, format, __VA_ARGS__) #endif -} #endif // LH_OS_TYPES_H diff --git a/include/litehtml/render_block.h b/include/litehtml/render_block.h index eb058c618..1874be332 100644 --- a/include/litehtml/render_block.h +++ b/include/litehtml/render_block.h @@ -18,11 +18,11 @@ namespace litehtml * @param self_size - defines calculated size of block * @return return value is the minimal width of the content in block. Must be greater or equal to ret_width parameter */ - virtual int _render_content(int x, int y, bool second_pass, const containing_block_context &self_size, formatting_context* fmt_ctx) {return 0;} + virtual int _render_content(int /*x*/, int /*y*/, bool /*second_pass*/, const containing_block_context &/*self_size*/, formatting_context* /*fmt_ctx*/) {return 0;} int _render(int x, int y, const containing_block_context &containing_block_size, formatting_context* fmt_ctx, bool second_pass) override; int place_float(const std::shared_ptr<render_item> &el, int top, const containing_block_context &self_size, formatting_context* fmt_ctx); - virtual void fix_line_width(element_float flt, - const containing_block_context &containing_block_size, formatting_context* fmt_ctx) + virtual void fix_line_width(element_float /*flt*/, + const containing_block_context &/*containing_block_size*/, formatting_context* /*fmt_ctx*/) {} public: diff --git a/include/litehtml/render_item.h b/include/litehtml/render_item.h index ac8a9f4c6..fedc1f388 100644 --- a/include/litehtml/render_item.h +++ b/include/litehtml/render_item.h @@ -2,13 +2,14 @@ #define LH_RENDER_ITEM_H #include <memory> -#include <utility> #include <list> #include <tuple> +#include "html.h" #include "types.h" #include "line_box.h" #include "table.h" #include "formatting_context.h" +#include "element.h" namespace litehtml { @@ -29,7 +30,7 @@ namespace litehtml containing_block_context calculate_containing_block_context(const containing_block_context& cb_context); void calc_cb_length(const css_length& len, int percent_base, containing_block_context::typed_int& out_value) const; - virtual int _render(int x, int y, const containing_block_context& containing_block_size, formatting_context* fmt_ctx, bool second_pass = false) + virtual int _render(int /*x*/, int /*y*/, const containing_block_context& /*containing_block_size*/, formatting_context* /*fmt_ctx*/, bool /*second_pass = false*/) { return 0; } @@ -222,6 +223,52 @@ namespace litehtml return content_offset_top() + content_offset_bottom(); } + int render_offset_left() const + { + if(css().get_box_sizing() == box_sizing_content_box) + { + return m_margins.left + m_borders.left + m_padding.left; + } + return m_margins.left; + } + + int render_offset_right() const + { + if(css().get_box_sizing() == box_sizing_content_box) + { + return m_margins.right + m_borders.right + m_padding.right; + } + return m_margins.right; + } + + int render_offset_width() const + { + return render_offset_left() + render_offset_right(); + } + + int render_offset_top() const + { + if(css().get_box_sizing() == box_sizing_content_box) + { + return m_margins.top + m_borders.top + m_padding.top; + } + return m_margins.top; + } + + int render_offset_bottom() const + { + if(css().get_box_sizing() == box_sizing_content_box) + { + return m_margins.bottom + m_borders.bottom + m_padding.bottom; + } + return m_margins.bottom; + } + + int render_offset_height() const + { + return render_offset_top() + render_offset_bottom(); + } + int box_sizing_left() const { if(css().get_box_sizing() == box_sizing_border_box) @@ -302,7 +349,8 @@ namespace litehtml m_element->css().get_float() == float_none && m_margins.top >= 0 && !is_flex_item() && - !is_root(); + !is_root() && + !is_one_of(css().get_overflow(), overflow_hidden, overflow_scroll, overflow_auto); } bool collapse_bottom_margin() const @@ -312,7 +360,8 @@ namespace litehtml m_element->in_normal_flow() && m_element->css().get_float() == float_none && m_margins.bottom >= 0 && - !is_root(); + !is_root() && + !is_one_of(css().get_overflow(), overflow_hidden, overflow_scroll, overflow_auto); } bool is_visible() const @@ -358,13 +407,15 @@ namespace litehtml std::shared_ptr<litehtml::render_item> > split_inlines(); bool fetch_positioned(); - void render_positioned(render_type rt = render_all); + void render_positioned(render_type width = render_all); + // returns element offset related to the containing block + std::tuple<int, int> element_static_offset(const std::shared_ptr<litehtml::render_item> &el); void add_positioned(const std::shared_ptr<litehtml::render_item> &el); void get_redraw_box(litehtml::position& pos, int x = 0, int y = 0); void calc_document_size( litehtml::size& sz, litehtml::size& content_size, int x = 0, int y = 0 ); - virtual void get_inline_boxes( position::vector& boxes ) const {}; - virtual void set_inline_boxes( position::vector& boxes ) {}; - virtual void add_inline_box( const position& box ) {}; + virtual void get_inline_boxes( position::vector& /*boxes*/ ) const {}; + virtual void set_inline_boxes( position::vector& /*boxes*/ ) {}; + virtual void add_inline_box( const position& /*box*/ ) {}; virtual void clear_inline_boxes() {}; void draw_stacking_context( uint_ptr hdc, int x, int y, const position* clip, bool with_positioned ); virtual void draw_children( uint_ptr hdc, int x, int y, const position* clip, draw_flag flag, int zindex ); diff --git a/include/litehtml/string_id.h b/include/litehtml/string_id.h index 2c90fc659..6373ec9e4 100644 --- a/include/litehtml/string_id.h +++ b/include/litehtml/string_id.h @@ -1,6 +1,8 @@ #ifndef LH_STRING_ID_H #define LH_STRING_ID_H +#include <string> + namespace litehtml { @@ -153,6 +155,7 @@ STRING_ID( _nth_of_type_, _nth_last_child_, _nth_last_of_type_, + _is_, _not_, _lang_, @@ -160,6 +163,9 @@ STRING_ID( _hover_, // CSS property names + // Side properties must go in this order: top, right, bottom, left (clockwise starting from the top). + // Corner properties must go in this order: top-left, top-right, bottom-right, bottom-left (clockwise starting from the top-left). + // This is used in style::add_four_properties() for margin-*, padding-*, border-*-{width,style,color} and border-*-*-radius{,-x,-y}. _background_, _background_color_, _background_image_, @@ -172,6 +178,7 @@ STRING_ID( _background_position_, _background_position_x_, _background_position_y_, + _background_gradient_, _border_, _border_width_, @@ -182,45 +189,44 @@ STRING_ID( __litehtml_border_spacing_x_, __litehtml_border_spacing_y_, - _border_left_, - _border_right_, _border_top_, + _border_right_, _border_bottom_, + _border_left_, - _border_left_style_, - _border_right_style_, _border_top_style_, + _border_right_style_, _border_bottom_style_, + _border_left_style_, - _border_left_width_, - _border_right_width_, _border_top_width_, + _border_right_width_, _border_bottom_width_, + _border_left_width_, - _border_left_color_, - _border_right_color_, _border_top_color_, + _border_right_color_, _border_bottom_color_, + _border_left_color_, _border_radius_, _border_radius_x_, _border_radius_y_, + _border_top_left_radius_, + _border_top_right_radius_, + _border_bottom_right_radius_, _border_bottom_left_radius_, - _border_bottom_left_radius_x_, - _border_bottom_left_radius_y_, - _border_bottom_right_radius_, + _border_top_left_radius_x_, + _border_top_right_radius_x_, _border_bottom_right_radius_x_, - _border_bottom_right_radius_y_, + _border_bottom_left_radius_x_, - _border_top_left_radius_, - _border_top_left_radius_x_, _border_top_left_radius_y_, - - _border_top_right_radius_, - _border_top_right_radius_x_, _border_top_right_radius_y_, + _border_bottom_right_radius_y_, + _border_bottom_left_radius_y_, _list_style_, _list_style_type_, @@ -229,15 +235,16 @@ STRING_ID( _list_style_image_baseurl_, _margin_, - _margin_left_, - _margin_right_, _margin_top_, + _margin_right_, _margin_bottom_, + _margin_left_, + _padding_, - _padding_left_, - _padding_right_, _padding_top_, + _padding_right_, _padding_bottom_, + _padding_left_, _font_, _font_family_, @@ -247,6 +254,14 @@ STRING_ID( _font_size_, _line_height_, _text_decoration_, + _text_decoration_style_, + _text_decoration_line_, + _text_decoration_color_, + _text_decoration_thickness_, + _text_emphasis_, + _text_emphasis_style_, + _text_emphasis_color_, + _text_emphasis_position_, _white_space_, _text_align_, @@ -262,15 +277,16 @@ STRING_ID( _overflow_, _display_, _visibility_, + _appearance_, _box_sizing_, _z_index_, _float_, _clear_, _text_indent_, - _left_, - _right_, _top_, + _right_, _bottom_, + _left_, _cursor_, _content_, _border_collapse_, @@ -291,15 +307,58 @@ STRING_ID( _caption_side_, _order_, - _counter_reset_, + _counter_reset_, _counter_increment_, -); + + // some CSS dimensions + _deg_, + _grad_, + _rad_, + _turn_, + + // some CSS property values + _initial_, + _auto_, + _none_, + _linear_gradient_, + _repeating_linear_gradient_, + _radial_gradient_, + _repeating_radial_gradient_, + _conic_gradient_, + _repeating_conic_gradient_, + + // at-rules and their components + _charset_, + _layer_, + _import_, + _media_, + _and_, + _or_, + _boolean_, + _plain_, + _range_, + _discrete_, + _integer_, + _length_, + _resolution_, + _ratio_, + _keyword_, + _orientation_, + _portrait_, + _landscape_, + _device_width_, + _device_height_, + _aspect_ratio_, + _device_aspect_ratio_, + _color_index_, + _monochrome_, +) #undef STRING_ID extern const string_id empty_id; // _id("") extern const string_id star_id; // _id("*") -string_id _id(const string& str); -const string& _s(string_id id); +string_id _id(const std::string& str); +const std::string& _s(string_id id); } // namespace litehtml diff --git a/include/litehtml/style.h b/include/litehtml/style.h index 2e04059f2..e68a05b72 100644 --- a/include/litehtml/style.h +++ b/include/litehtml/style.h @@ -1,164 +1,46 @@ #ifndef LH_STYLE_H #define LH_STYLE_H +#include "background.h" +#include "css_length.h" +#include "css_position.h" +#include "css_tokenizer.h" +#include "gradient.h" +#include "web_color.h" + namespace litehtml { - enum property_type + struct invalid {}; // indicates "not found" condition in style::get_property + struct inherit {}; // "inherit" was specified as the value of this property + + struct property_value : variant< + invalid, + inherit, + int, + int_vector, + css_length, + length_vector, + float, + web_color, + vector<image>, + string, + string_vector, + size_vector, + css_token_vector + > { - prop_type_invalid, // indicates "not found" condition in style::get_property - prop_type_inherit, // "inherit" was specified as the value of this property - - prop_type_enum_item, - prop_type_enum_item_vector, - prop_type_length, - prop_type_length_vector, - prop_type_number, - prop_type_color, - prop_type_string, - prop_type_string_vector, - prop_type_size_vector, - - prop_type_var, // also string, but needs further parsing because of var() - }; + bool m_important = false; + bool m_has_var = false; // css_token_vector, parsing is delayed because of var() - class property_value - { - public: - property_type m_type; - bool m_important; - - union { - int m_enum_item; - int_vector m_enum_item_vector; - css_length m_length; - length_vector m_length_vector; - float m_number; - web_color m_color; - string m_string; - string_vector m_string_vector; - size_vector m_size_vector; - }; - - property_value() - : m_type(prop_type_invalid) - { - } - property_value(bool important, property_type type) - : m_type(type), m_important(important) - { - } - property_value(const string& str, bool important, property_type type = prop_type_string) - : m_string(str), m_type(type), m_important(important) - { - } - property_value(const string_vector& vec, bool important) - : m_string_vector(vec), m_type(prop_type_string_vector), m_important(important) - { - } - property_value(const css_length& length, bool important) - : m_length(length), m_type(prop_type_length), m_important(important) - { - } - property_value(const length_vector& vec, bool important) - : m_length_vector(vec), m_type(prop_type_length_vector), m_important(important) - { - } - property_value(float number, bool important) - : m_number(number), m_type(prop_type_number), m_important(important) - { - } - property_value(int enum_item, bool important) - : m_enum_item(enum_item), m_type(prop_type_enum_item), m_important(important) - { - } - property_value(const int_vector& vec, bool important) - : m_enum_item_vector(vec), m_type(prop_type_enum_item_vector), m_important(important) - { - } - property_value(web_color color, bool important) - : m_color(color), m_type(prop_type_color), m_important(important) - { - } - property_value(const size_vector& vec, bool important) - : m_size_vector(vec), m_type(prop_type_size_vector), m_important(important) - { - } - ~property_value() - { - switch (m_type) - { - case prop_type_string: - case prop_type_var: - m_string.~string(); - break; - case prop_type_string_vector: - m_string_vector.~string_vector(); - break; - case prop_type_length: - m_length.~css_length(); - break; - case prop_type_length_vector: - m_length_vector.~length_vector(); - break; - case prop_type_enum_item_vector: - m_enum_item_vector.~int_vector(); - break; - case prop_type_color: - m_color.~web_color(); - break; - case prop_type_size_vector: - m_size_vector.~size_vector(); - break; - } - } - property_value& operator=(const property_value& val) - { - this->~property_value(); - - switch (val.m_type) - { - case prop_type_invalid: - new(this) property_value(); - break; - case prop_type_inherit: - new(this) property_value(val.m_important, val.m_type); - break; - case prop_type_string: - case prop_type_var: - new(this) property_value(val.m_string, val.m_important, val.m_type); - break; - case prop_type_string_vector: - new(this) property_value(val.m_string_vector, val.m_important); - break; - case prop_type_enum_item: - new(this) property_value(val.m_enum_item, val.m_important); - break; - case prop_type_enum_item_vector: - new(this) property_value(val.m_enum_item_vector, val.m_important); - break; - case prop_type_length: - new(this) property_value(val.m_length, val.m_important); - break; - case prop_type_length_vector: - new(this) property_value(val.m_length_vector, val.m_important); - break; - case prop_type_number: - new(this) property_value(val.m_number, val.m_important); - break; - case prop_type_color: - new(this) property_value(val.m_color, val.m_important); - break; - case prop_type_size_vector: - new(this) property_value(val.m_size_vector, val.m_important); - break; - } - - return *this; - } + property_value() {} + template<class T> property_value(const T& val, bool important, bool has_var = false) + : base(val), m_important(important), m_has_var(has_var) {} }; + class html_tag; typedef std::map<string_id, property_value> props_map; + // represents a style block, eg. "color: black; display: inline" class style { public: @@ -168,12 +50,11 @@ namespace litehtml props_map m_properties; static std::map<string_id, string> m_valid_values; public: - void add(const string& txt, const string& baseurl = "", document_container* container = nullptr) - { - parse(txt, baseurl, container); - } + void add(const css_token_vector& tokens, const string& baseurl = "", document_container* container = nullptr); + void add(const string& txt, const string& baseurl = "", document_container* container = nullptr); - void add_property(string_id name, const string& val, const string& baseurl = "", bool important = false, document_container* container = nullptr); + void add_property(string_id name, const css_token_vector& tokens, const string& baseurl = "", bool important = false, document_container* container = nullptr); + void add_property(string_id name, const string& val, const string& baseurl = "", bool important = false, document_container* container = nullptr); const property_value& get_property(string_id name) const; @@ -183,31 +64,62 @@ namespace litehtml m_properties.clear(); } - void subst_vars(const element* el); + void subst_vars(const html_tag* el); private: - void parse_property(const string& txt, const string& baseurl, document_container* container); - void parse(const string& txt, const string& baseurl, document_container* container); - void parse_background(const string& val, const string& baseurl, bool important, document_container* container); - bool parse_one_background(const string& val, document_container* container, background& bg); - void parse_background_image(const string& val, const string& baseurl, bool important); + void inherit_property(string_id name, bool important); + + void parse_background(const css_token_vector& tokens, const string& baseurl, bool important, document_container* container); + bool parse_bg_layer(const css_token_vector& tokens, document_container* container, background& bg, bool final_layer); + // parse the value of background-image property, which is comma-separated list of <bg-image>s + void parse_background_image(const css_token_vector& tokens, const string& baseurl, bool important, document_container* container); + // parse comma-separated list of keywords - void parse_keyword_comma_list(string_id name, const string& val, bool important); - void parse_background_position(const string& val, bool important); - bool parse_one_background_position(const string& val, css_length& x, css_length& y); - void parse_background_size(const string& val, bool important); - bool parse_one_background_size(const string& val, css_size& size); - void parse_font(const string& val, bool important); - void parse_flex(const string& val, bool important); - void parse_align_self(string_id name, const string& val, bool important); - static css_length parse_border_width(const string& str); - static void parse_two_lengths(const string& str, css_length len[2]); - static int parse_four_lengths(const string& str, css_length len[4]); - static void subst_vars_(string& str, const element* el); + void parse_keyword_comma_list(string_id name, const css_token_vector& tokens, bool important); + void parse_background_position(const css_token_vector& tokens, bool important); + void parse_background_size(const css_token_vector& tokens, bool important); + + void parse_border(const css_token_vector& tokens, bool important, document_container* container); + void parse_border_side(string_id name, const css_token_vector& tokens, bool important, document_container* container); + void parse_border_radius(const css_token_vector& tokens, bool important); + + bool parse_list_style_image(const css_token& tok, string& url); + void parse_list_style(const css_token_vector& tokens, string baseurl, bool important); + + void parse_font(css_token_vector tokens, bool important); + void parse_text_decoration(const css_token_vector& tokens, bool important, document_container* container); + bool parse_text_decoration_color(const css_token& token, bool important, document_container* container); + void parse_text_decoration_line(const css_token_vector& tokens, bool important); + + void parse_text_emphasis(const css_token_vector& tokens, bool important, document_container* container); + bool parse_text_emphasis_color(const css_token& token, bool important, document_container* container); + void parse_text_emphasis_position(const css_token_vector& tokens, bool important); + + void parse_flex_flow(const css_token_vector& tokens, bool important); + void parse_flex(const css_token_vector& tokens, bool important); + void parse_align_self(string_id name, const css_token_vector& tokens, bool important); void add_parsed_property(string_id name, const property_value& propval); + void add_length_property(string_id name, css_token val, string keywords, int options, bool important); + template<class T> void add_four_properties(string_id top_name, T val[4], int n, bool important); void remove_property(string_id name, bool important); }; -} + + bool parse_url(const css_token& token, string& url); + bool parse_length(const css_token& tok, css_length& length, int options, string keywords = ""); + bool parse_angle(const css_token& tok, float& angle, bool percents_allowed = false); + bool parse_bg_position(const css_token_vector& tokens, int& index, css_length& x, css_length& y, bool convert_keywords_to_percents); + + template<typename Enum> + bool parse_keyword(const css_token& tok, Enum& val, string keywords, int first_keyword_value = 0) + { + int value_index(const string& val, const string& strings, int defValue = -1, char delim = ';'); + int idx = value_index(tok.ident(), keywords); + if (idx == -1) return false; + val = (Enum)(first_keyword_value + idx); + return true; + } + +} // namespace litehtml #endif // LH_STYLE_H diff --git a/include/litehtml/stylesheet.h b/include/litehtml/stylesheet.h index 923853d76..f81bac712 100644 --- a/include/litehtml/stylesheet.h +++ b/include/litehtml/stylesheet.h @@ -1,47 +1,73 @@ #ifndef LH_STYLESHEET_H #define LH_STYLESHEET_H -#include "style.h" #include "css_selector.h" +#include "css_tokenizer.h" namespace litehtml { - class document_container; - class css - { - css_selector::vector m_selectors; - public: - css() = default; - ~css() = default; +// https://www.w3.org/TR/cssom-1/#css-declarations +struct raw_declaration +{ + using vector = std::vector<raw_declaration>; + + string name; // property name + css_token_vector value = {}; // default value is specified here to get rid of gcc warning "missing initializer for member" + bool important = false; - const css_selector::vector& selectors() const - { - return m_selectors; - } + operator bool() const { return name != ""; } +}; - void clear() - { - m_selectors.clear(); - } +// intermediate half-parsed rule that is used internally by the parser +class raw_rule +{ +public: + using ptr = shared_ptr<raw_rule>; + using vector = std::vector<ptr>; + + enum rule_type { qualified, at }; - void parse_stylesheet(const char* str, const char* baseurl, const std::shared_ptr<document>& doc, const media_query_list::ptr& media); - void sort_selectors(); - static void parse_css_url(const string& str, string& url); + raw_rule(rule_type type, string name = "") : type(type), name(name) {} - private: - void parse_atrule(const string& text, const char* baseurl, const std::shared_ptr<document>& doc, const media_query_list::ptr& media); - void add_selector(const css_selector::ptr& selector); - bool parse_selectors(const string& txt, const style::ptr& styles, const media_query_list::ptr& media); + rule_type type; + // An at-rule has a name, a prelude consisting of a list of component values, and an optional block consisting of a simple {} block. + string name; + // https://www.w3.org/TR/css-syntax-3/#qualified-rule + // A qualified rule has a prelude consisting of a list of component values, and a block consisting of a simple {} block. + // Note: Most qualified rules will be style rules, where the prelude is a selector and the block a list of declarations. + css_token_vector prelude; + css_token block; +}; - }; +class css +{ + css_selector::vector m_selectors; +public: - inline void litehtml::css::add_selector( const css_selector::ptr& selector ) + const css_selector::vector& selectors() const { - selector->m_order = (int) m_selectors.size(); - m_selectors.push_back(selector); + return m_selectors; } + template<class Input> + void parse_css_stylesheet(const Input& input, string baseurl, shared_ptr<document> doc, media_query_list_list::ptr media = nullptr, bool top_level = true); + + void sort_selectors(); + +private: + bool parse_style_rule(raw_rule::ptr rule, string baseurl, shared_ptr<document> doc, media_query_list_list::ptr media); + void parse_import_rule(raw_rule::ptr rule, string baseurl, shared_ptr<document> doc, media_query_list_list::ptr media); + void add_selector(const css_selector::ptr& selector); +}; + +inline void css::add_selector(const css_selector::ptr& selector) +{ + selector->m_order = (int)m_selectors.size(); + m_selectors.push_back(selector); } + +} // namespace litehtml + #endif // LH_STYLESHEET_H diff --git a/include/litehtml/table.h b/include/litehtml/table.h index d738b63eb..2d251d614 100644 --- a/include/litehtml/table.h +++ b/include/litehtml/table.h @@ -7,16 +7,16 @@ namespace litehtml { - class render_item; + class render_item; struct table_row { - typedef std::vector<table_row> vector; + using vector = std::vector<table_row>; int height; int border_top; int border_bottom; - std::shared_ptr<render_item> el_row; + std::shared_ptr<render_item> el_row; int top; int bottom; css_length css_height; @@ -63,7 +63,7 @@ namespace litehtml struct table_column { - typedef std::vector<table_column> vector; + using vector = std::vector<table_column>; int min_width; int max_width; @@ -140,7 +140,7 @@ namespace litehtml struct table_cell { - std::shared_ptr<render_item> el; + std::shared_ptr<render_item> el; int colspan; int rowspan; int min_width; @@ -179,7 +179,7 @@ namespace litehtml } table_cell(table_cell&& val) noexcept - { + { el = std::move(val.el); colspan = val.colspan; rowspan = val.rowspan; @@ -196,7 +196,7 @@ namespace litehtml class table_grid { public: - typedef std::vector< std::vector<table_cell> > rows; + using rows = std::vector<std::vector<table_cell>>; private: int m_rows_count; int m_cols_count; @@ -224,7 +224,7 @@ namespace litehtml table_cell* cell(int t_col, int t_row); table_column& column(int c) { return m_columns[c]; } table_row& row(int r) { return m_rows[r]; } - std::vector<std::shared_ptr<render_item>>& captions() { return m_captions; } + std::vector<std::shared_ptr<render_item>>& captions() { return m_captions; } int rows_count() const { return m_rows_count; } int cols_count() const { return m_cols_count; } diff --git a/include/litehtml/tstring_view.h b/include/litehtml/tstring_view.h index 384c8d7df..9c3c74f5f 100644 --- a/include/litehtml/tstring_view.h +++ b/include/litehtml/tstring_view.h @@ -33,8 +33,6 @@ #include <cstddef> #include <ostream> -#include "os_types.h" - namespace litehtml { // tstring_view is a string reference type that provides a view into a string diff --git a/include/litehtml/types.h b/include/litehtml/types.h index bfcaa6c3a..43cafc3bf 100644 --- a/include/litehtml/types.h +++ b/include/litehtml/types.h @@ -1,29 +1,97 @@ #ifndef LH_TYPES_H #define LH_TYPES_H -#include <cstdlib> +#include <cstdint> #include <memory> -#include <map> +#include <string> #include <vector> +#include <map> #include <list> +#include <variant> +#include <optional> +#include <algorithm> namespace litehtml { + using uint_ptr = std::uintptr_t; + using std::string; + using std::vector; + using std::shared_ptr; + using std::make_shared; + using std::optional; + using std::min; + using std::max; + using std::swap; + using std::abs; + class document; class element; - typedef std::map<string, string> string_map; - typedef std::list< std::shared_ptr<element> > elements_list; - typedef std::vector<int> int_vector; - typedef std::vector<string> string_vector; + using string_map = std::map<string, string>; + using elements_list = std::list<std::shared_ptr<element>>; + using int_vector = std::vector<int>; + using string_vector = std::vector<string>; + + template <class... Types> + struct variant : std::variant<Types...> + { + using base = variant<Types...>; // for derived class ctors + using std::variant<Types...>::variant; // inherit ctors + template<class T> bool is() const { return std::holds_alternative<T>(*this); } + template<class T> const T& get() const { return std::get<T>(*this); } + template<class T> T& get() { return std::get<T>(*this); } + }; + + enum document_mode + { + no_quirks_mode, + quirks_mode, + limited_quirks_mode + }; + + #define style_text_decoration_line_strings "none;underline;overline;line-through" + + enum text_decoration_line + { + text_decoration_line_none = 0x00, + text_decoration_line_underline = 0x01, + text_decoration_line_overline = 0x02, + text_decoration_line_line_through = 0x04, + }; + + #define style_text_decoration_style_strings "solid;double;dotted;dashed;wavy" - const unsigned int font_decoration_none = 0x00; - const unsigned int font_decoration_underline = 0x01; - const unsigned int font_decoration_linethrough = 0x02; - const unsigned int font_decoration_overline = 0x04; + enum text_decoration_style + { + text_decoration_style_solid, + text_decoration_style_double, + text_decoration_style_dotted, + text_decoration_style_dashed, + text_decoration_style_wavy, + text_decoration_style_max, + }; + + #define style_text_decoration_thickness_strings "auto;from-font" + + enum text_decoration_thickness + { + text_decoration_thickness_auto, + text_decoration_thickness_from_font, + }; + + #define style_text_emphasis_position_strings "over;under;left;right" - typedef unsigned char byte; - typedef unsigned int ucode_t; + enum text_emphasis_position + { + text_emphasis_position_over = 0x00, + text_emphasis_position_under = 0x01, + text_emphasis_position_left = 0x02, + text_emphasis_position_right = 0x04, + }; + + + using byte = unsigned char; + using ucode_t = unsigned int; struct margins { @@ -37,8 +105,19 @@ namespace litehtml left = right = top = bottom = 0; } - int width() const { return left + right; } - int height() const { return top + bottom; } + int width() const { return left + right; } + int height() const { return top + bottom; } + }; + + struct pointF + { + float x; + float y; + + pointF() : x(0), y(0) {} + pointF(float _x, float _y) : x(_x), y(_y) {} + + void set(float _x, float _y) { x = _x; y = _y; } }; struct size @@ -57,25 +136,21 @@ namespace litehtml struct position { - typedef std::vector<position> vector; + using vector = std::vector<position>; - int x; - int y; - int width; - int height; + int x = 0; + int y = 0; + int width = 0; + int height = 0; - position() - { - x = y = width = height = 0; - } + position() = default; - position(int x, int y, int width, int height) - { - this->x = x; - this->y = y; - this->width = width; - this->height = height; - } + position(int _x, int _y, int _width, int _height) : + x(_x), + y(_y), + width(_width), + height(_height) + {} int right() const { return x + width; } int bottom() const { return y + height; } @@ -108,63 +183,84 @@ namespace litehtml height = sz.height; } - void move_to(int x, int y) + bool operator==(const position& val) + { + return x == val.x && y == val.y && width == val.width && height == val.height; + } + + void move_to(int _x, int _y) { - this->x = x; - this->y = y; + x = _x; + y = _y; } + [[nodiscard]] bool does_intersect(const position* val) const { if(!val) return true; return ( - left() <= val->right() && - right() >= val->left() && - bottom() >= val->top() && + left() <= val->right() && + right() >= val->left() && + bottom() >= val->top() && top() <= val->bottom() ) || ( - val->left() <= right() && - val->right() >= left() && - val->bottom() >= top() && + val->left() <= right() && + val->right() >= left() && + val->bottom() >= top() && val->top() <= bottom() ); } - bool empty() const + [[nodiscard]] + position intersect(const position& src) const { - if(!width && !height) + position dest; + int dest_x = std::max(src.x, x); + int dest_y = std::max(src.y, y); + int dest_x2 = std::min(src.right(), right()); + int dest_y2 = std::min(src.bottom(), bottom()); + + if (dest_x2 > dest_x && dest_y2 > dest_y) { - return true; + dest.x = dest_x; + dest.y = dest_y; + dest.width = dest_x2 - dest_x; + dest.height = dest_y2 - dest_y; } - return false; + else + { + dest.width = 0; + dest.height = 0; + } + + return dest; } - bool is_point_inside(int x, int y) const + [[nodiscard]] + bool empty() const { - if(x >= left() && x <= right() && y >= top() && y <= bottom()) - { - return true; - } - return false; + return !width && !height; + } + + [[nodiscard]] + bool is_point_inside(int _x, int _y) const + { + return (_x >= left() && _x < right() && _y >= top() && _y < bottom()); } }; struct font_metrics { - int height; - int ascent; - int descent; - int x_height; - bool draw_spaces; + int font_size = 0; // Font size in pixels. The same as size argument of the create_font function + int height = 0; // Font height in pixels. + int ascent = 0; // The distance from the baseline to the top of a line of text. + int descent = 0; // The distance from the baseline to the bottom of a line of text. + int x_height = 0; // Height of the symbol x + int ch_width = 0; // Height of the symbol 0 + bool draw_spaces = true; // True to call draw text function for spaces. If False, just use space width without draw. + int sub_shift = 0; // The baseline shift for subscripts. + int super_shift = 0; // The baseline shift for superscripts. - font_metrics() - { - height = 0; - ascent = 0; - descent = 0; - x_height = 0; - draw_spaces = true; - } int base_line() const { return descent; } }; @@ -174,7 +270,7 @@ namespace litehtml font_metrics metrics; }; - typedef std::map<string, font_item> fonts_map; + using fonts_map = std::map<string, font_item>; enum draw_flag { @@ -208,6 +304,8 @@ namespace litehtml int value; cbc_value_type type; + typed_int(const typed_int& v) = default; + typed_int(int val, cbc_value_type tp) { value = val; @@ -225,12 +323,7 @@ namespace litehtml return *this; } - typed_int& operator=(const typed_int& v) - { - value = v.value; - type = v.type; - return *this; - } + typed_int& operator=(const typed_int& v) = default; }; typed_int width; // width of the containing block @@ -331,6 +424,8 @@ namespace litehtml font_style_italic }; +#define font_system_family_name_strings "caption;icon;menu;message-box;small-caption;status-bar" + #define font_variant_strings "normal;small-caps" enum font_variant @@ -339,7 +434,7 @@ namespace litehtml font_variant_small_caps }; -#define font_weight_strings "normal;bold;bolder;lighter;100;200;300;400;500;600;700;800;900" +#define font_weight_strings "normal;bold;bolder;lighter" enum font_weight { @@ -347,15 +442,6 @@ namespace litehtml font_weight_bold, font_weight_bolder, font_weight_lighter, - font_weight_100, - font_weight_200, - font_weight_300, - font_weight_400, - font_weight_500, - font_weight_600, - font_weight_700, - font_weight_800, - font_weight_900 }; #define list_style_type_strings "none;circle;disc;square;armenian;cjk-ideographic;decimal;decimal-leading-zero;georgian;hebrew;hiragana;hiragana-iroha;katakana;katakana-iroha;lower-alpha;lower-greek;lower-latin;lower-roman;upper-alpha;upper-latin;upper-roman" @@ -456,9 +542,9 @@ namespace litehtml clear_both }; -#define css_units_strings "none;%;in;cm;mm;em;ex;pt;pc;px;dpi;dpcm;vw;vh;vmin;vmax;rem" +#define css_units_strings "none;%;in;cm;mm;em;ex;pt;pc;px;vw;vh;vmin;vmax;rem;ch" - enum css_units : byte + enum css_units : byte // see css_length { css_units_none, css_units_percentage, @@ -470,13 +556,12 @@ namespace litehtml css_units_pt, css_units_pc, css_units_px, - css_units_dpi, - css_units_dpcm, css_units_vw, css_units_vh, css_units_vmin, css_units_vmax, css_units_rem, + css_units_ch, }; #define background_attachment_strings "scroll;fixed" @@ -497,6 +582,7 @@ namespace litehtml background_repeat_no_repeat }; +// https://drafts.csswg.org/css-box-4/#typedef-visual-box #define background_box_strings "border-box;padding-box;content-box" enum background_box @@ -506,14 +592,15 @@ namespace litehtml background_box_content }; -#define background_position_strings "top;bottom;left;right;center" +#define background_position_strings "left;right;top;bottom;center" + const float background_position_percentages[] = {0, 100, 0, 100, 50}; enum background_position { - background_position_top, - background_position_bottom, background_position_left, background_position_right, + background_position_top, + background_position_bottom, background_position_center, }; @@ -574,7 +661,7 @@ namespace litehtml enum background_size { - background_size_auto, + background_size_auto, // must be first, see parse_bg_size background_size_cover, background_size_contain, }; @@ -629,16 +716,9 @@ namespace litehtml context = val.context; min_width = val.min_width; } - floated_box& operator=(const floated_box& val) - { - pos = val.pos; - float_side = val.float_side; - clear_floats = val.clear_floats; - el = val.el; - context = val.context; - min_width = val.min_width; - return *this; - } + + floated_box& operator=(const floated_box& val) = default; + floated_box(floated_box&& val) { pos = val.pos; @@ -706,6 +786,11 @@ namespace litehtml m_is_default = true; m_val = def_val; } + def_value(const def_value<T>& val) + { + m_is_default = val.m_is_default; + m_val = val.m_val; + } void reset(T def_val) { m_is_default = true; @@ -721,6 +806,12 @@ namespace litehtml m_is_default = false; return m_val; } + def_value<T>& operator=(const def_value<T>& val) + { + m_is_default = val.m_is_default; + m_val = val.m_val; + return *this; + } operator T() const { return m_val; @@ -787,62 +878,26 @@ namespace litehtml _baseline_type m_type; }; +#define appearance_strings "none;auto;menulist-button;textfield;button;checkbox;listbox;menulist;meter;progress-bar;push-button;radio;searchfield;slider-horizontal;square-button;textarea" -#define media_orientation_strings "portrait;landscape" - - enum media_orientation + enum appearance { - media_orientation_portrait, - media_orientation_landscape, - }; - -#define media_feature_strings "none;width;min-width;max-width;height;min-height;max-height;device-width;min-device-width;max-device-width;device-height;min-device-height;max-device-height;orientation;aspect-ratio;min-aspect-ratio;max-aspect-ratio;device-aspect-ratio;min-device-aspect-ratio;max-device-aspect-ratio;color;min-color;max-color;color-index;min-color-index;max-color-index;monochrome;min-monochrome;max-monochrome;resolution;min-resolution;max-resolution" - - enum media_feature - { - media_feature_none, - - media_feature_width, - media_feature_min_width, - media_feature_max_width, - - media_feature_height, - media_feature_min_height, - media_feature_max_height, - - media_feature_device_width, - media_feature_min_device_width, - media_feature_max_device_width, - - media_feature_device_height, - media_feature_min_device_height, - media_feature_max_device_height, - - media_feature_orientation, - - media_feature_aspect_ratio, - media_feature_min_aspect_ratio, - media_feature_max_aspect_ratio, - - media_feature_device_aspect_ratio, - media_feature_min_device_aspect_ratio, - media_feature_max_device_aspect_ratio, - - media_feature_color, - media_feature_min_color, - media_feature_max_color, - - media_feature_color_index, - media_feature_min_color_index, - media_feature_max_color_index, - - media_feature_monochrome, - media_feature_min_monochrome, - media_feature_max_monochrome, - - media_feature_resolution, - media_feature_min_resolution, - media_feature_max_resolution, + appearance_none, + appearance_auto, + appearance_menulist_button, + appearance_textfield, + appearance_button, + appearance_checkbox, + appearance_listbox, + appearance_menulist, + appearance_meter, + appearance_progress_bar, + appearance_push_button, + appearance_radio, + appearance_searchfield, + appearance_slider_horizontal, + appearance_square_button, + appearance_textarea, }; #define box_sizing_strings "content-box;border-box" @@ -853,22 +908,18 @@ namespace litehtml box_sizing_border_box, }; - -#define media_type_strings "none;all;screen;print;braille;embossed;handheld;projection;speech;tty;tv" +// https://drafts.csswg.org/mediaqueries/#media-types +// User agents must recognize the following media types as valid, but must make them match nothing. +#define deprecated_media_type_strings "tty;tv;projection;handheld;braille;embossed;aural;speech" +#define media_type_strings "all;print;screen;" deprecated_media_type_strings enum media_type { - media_type_none, + media_type_unknown, media_type_all, - media_type_screen, media_type_print, - media_type_braille, - media_type_embossed, - media_type_handheld, - media_type_projection, - media_type_speech, - media_type_tty, - media_type_tv, + media_type_screen, + media_type_first_deprecated }; struct media_features @@ -885,8 +936,8 @@ namespace litehtml media_features() { - type = media_type::media_type_none, - width =0; + type = media_type_unknown; + width = 0; height = 0; device_width = 0; device_height = 0; @@ -904,6 +955,8 @@ namespace litehtml render_fixed_only, }; + const char* const split_delims_spaces = " \t\r\n\f\v"; + // List of the Void Elements (can't have any contents) const char* const void_elements = "area;base;br;col;command;embed;hr;img;input;keygen;link;meta;param;source;track;wbr"; @@ -944,19 +997,24 @@ namespace litehtml flex_justify_content_stretch, }; -#define flex_align_items_strings "normal;flex-start;flex-end;center;start;end;baseline;stretch;auto" +#define self_position_strings "center;start;end;self-start;self-end;flex-start;flex-end" +#define flex_align_items_strings "auto;normal;stretch;baseline;" self_position_strings enum flex_align_items { - flex_align_items_flex_normal, - flex_align_items_flex_start, - flex_align_items_flex_end, + flex_align_items_auto, // used for align-self property only + flex_align_items_normal, + flex_align_items_stretch, + flex_align_items_baseline, + flex_align_items_center, flex_align_items_start, flex_align_items_end, - flex_align_items_baseline, - flex_align_items_stretch, - flex_align_items_auto, // used for align-self property only + flex_align_items_self_start, + flex_align_items_self_end, + flex_align_items_flex_start, + flex_align_items_flex_end, + flex_align_items_first = 0x100, flex_align_items_last = 0x200, flex_align_items_unsafe = 0x400, diff --git a/include/litehtml/url.h b/include/litehtml/url.h index bd0cdc63c..d651b7c3d 100644 --- a/include/litehtml/url.h +++ b/include/litehtml/url.h @@ -30,9 +30,7 @@ #ifndef LITEHTML_URL_H__ #define LITEHTML_URL_H__ -#include <ostream> - -#include "os_types.h" +#include "types.h" // https://datatracker.ietf.org/doc/html/rfc3986 @@ -105,6 +103,9 @@ class url { return !fragment_.empty(); } + static string encode(const string& str); + static string decode(const string& str); + protected: string str_; diff --git a/include/litehtml/url_path.h b/include/litehtml/url_path.h index 6b102139d..f93665918 100644 --- a/include/litehtml/url_path.h +++ b/include/litehtml/url_path.h @@ -30,9 +30,7 @@ #ifndef LITEHTML_URL_PATH_H__ #define LITEHTML_URL_PATH_H__ -#include <ostream> - -#include "os_types.h" +#include "types.h" namespace litehtml { diff --git a/include/litehtml/utf8_strings.h b/include/litehtml/utf8_strings.h index 72969fd3d..7930a437a 100644 --- a/include/litehtml/utf8_strings.h +++ b/include/litehtml/utf8_strings.h @@ -1,52 +1,43 @@ #ifndef LH_UTF8_STRINGS_H #define LH_UTF8_STRINGS_H -#include "os_types.h" #include "types.h" namespace litehtml { - class utf8_to_wchar + // converts UTF-32 ch to UTF-8 and appends it to str + void append_char(string& str, char32_t ch); + char32_t read_utf8_char(const string& str, int& index); + void prev_utf8_char(const string& str, int& index); + + class utf8_to_utf32 { - const byte* m_utf8; - std::wstring m_str; + std::u32string m_str; public: - utf8_to_wchar(const char* val); - operator const wchar_t*() const + utf8_to_utf32(const string& val); + operator const char32_t*() const { return m_str.c_str(); } - private: - ucode_t getb() - { - if (!(*m_utf8)) return 0; - return *m_utf8++; - } - ucode_t get_next_utf8(ucode_t val) - { - return (val & 0x3f); - } - ucode_t get_char(); }; - class wchar_to_utf8 + class utf32_to_utf8 { - std::string m_str; + string m_str; public: - wchar_to_utf8(const std::wstring& val); + utf32_to_utf8(const std::u32string& val); operator const char*() const { return m_str.c_str(); } - const char* c_str() const { return m_str.c_str(); } }; -#define litehtml_from_wchar(str) litehtml::wchar_to_utf8(str) -#define litehtml_to_wchar(str) litehtml::utf8_to_wchar(str) +#define litehtml_from_utf32(str) litehtml::utf32_to_utf8(str) +#define litehtml_to_utf32(str) litehtml::utf8_to_utf32(str) } #endif // LH_UTF8_STRINGS_H diff --git a/include/litehtml/web_color.h b/include/litehtml/web_color.h index a42b416ca..ba59ae66d 100644 --- a/include/litehtml/web_color.h +++ b/include/litehtml/web_color.h @@ -1,53 +1,38 @@ #ifndef LH_WEB_COLOR_H #define LH_WEB_COLOR_H +#include "css_tokenizer.h" +#include "types.h" + namespace litehtml { - struct def_color - { - const char* name; - const char* rgb; - }; - - extern def_color g_def_colors[]; - class document_container; struct web_color { - byte red; - byte green; - byte blue; - byte alpha; + byte red = 0; + byte green = 0; + byte blue = 0; + byte alpha = 255; + bool is_current_color = false; static const web_color transparent; static const web_color black; static const web_color white; + static const web_color current_color; - web_color(byte r, byte g, byte b, byte a = 255) - { - red = r; - green = g; - blue = b; - alpha = a; - } - - web_color() - { - red = 0; - green = 0; - blue = 0; - alpha = 0xFF; - } + web_color() {} + web_color(byte r, byte g, byte b, byte a = 255) : red(r), green(g), blue(b), alpha(a) {} + web_color(bool is_current_color) : is_current_color(is_current_color) {} bool operator==(web_color color) const { return red == color.red && green == color.green && blue == color.blue && alpha == color.alpha; } bool operator!=(web_color color) const { return !(*this == color); } + web_color darken(double fraction) const; string to_string() const; - static web_color from_string(const string& str, document_container* callback); - static string resolve_name(const string& name, document_container* callback); - static bool is_color(const string& str, document_container* callback); }; + + bool parse_color(const css_token& token, web_color& color, document_container* container); } #endif // LH_WEB_COLOR_H diff --git a/litehtml.vcxproj b/litehtml.vcxproj index 20b94bf10..f9f82f475 100644 --- a/litehtml.vcxproj +++ b/litehtml.vcxproj @@ -88,7 +88,7 @@ </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> - <AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions> + <AdditionalOptions>/MP /utf-8 %(AdditionalOptions)</AdditionalOptions> <Optimization>Disabled</Optimization> <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>false</MinimalRebuild> @@ -98,6 +98,8 @@ <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>EditAndContinue</DebugInformationFormat> <AdditionalIncludeDirectories>./src/gumbo/visualc/include;./src/gumbo/include/gumbo;./src/gumbo/include;./include/litehtml</AdditionalIncludeDirectories> + <LanguageStandard>stdcpp17</LanguageStandard> + <ConformanceMode>true</ConformanceMode> </ClCompile> <Lib> <OutputFile>$(OutDir)$(ProjectName).lib</OutputFile> @@ -108,7 +110,7 @@ <TargetEnvironment>X64</TargetEnvironment> </Midl> <ClCompile> - <AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions> + <AdditionalOptions>/MP /utf-8 %(AdditionalOptions)</AdditionalOptions> <Optimization>Disabled</Optimization> <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>false</MinimalRebuild> @@ -118,6 +120,8 @@ <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> <AdditionalIncludeDirectories>./src/gumbo/visualc/include;./src/gumbo/include/gumbo;./src/gumbo/include;./include/litehtml</AdditionalIncludeDirectories> + <LanguageStandard>stdcpp17</LanguageStandard> + <ConformanceMode>true</ConformanceMode> </ClCompile> <Lib> <OutputFile>$(OutDir)$(ProjectName).lib</OutputFile> @@ -125,7 +129,7 @@ </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> - <AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions> + <AdditionalOptions>/MP /utf-8 %(AdditionalOptions)</AdditionalOptions> <Optimization>MaxSpeed</Optimization> <IntrinsicFunctions>true</IntrinsicFunctions> <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> @@ -136,6 +140,8 @@ <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>OldStyle</DebugInformationFormat> <AdditionalIncludeDirectories>./src/gumbo/visualc/include;./src/gumbo/include/gumbo;./src/gumbo/include;./include/litehtml</AdditionalIncludeDirectories> + <LanguageStandard>stdcpp17</LanguageStandard> + <ConformanceMode>true</ConformanceMode> </ClCompile> <Lib> <OutputFile>$(OutDir)$(ProjectName).lib</OutputFile> @@ -146,7 +152,7 @@ <TargetEnvironment>X64</TargetEnvironment> </Midl> <ClCompile> - <AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions> + <AdditionalOptions>/MP /utf-8 %(AdditionalOptions)</AdditionalOptions> <Optimization>MaxSpeed</Optimization> <IntrinsicFunctions>true</IntrinsicFunctions> <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> @@ -157,17 +163,22 @@ <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> <AdditionalIncludeDirectories>./src/gumbo/visualc/include;./src/gumbo/include/gumbo;./src/gumbo/include;./include/litehtml</AdditionalIncludeDirectories> + <LanguageStandard>stdcpp17</LanguageStandard> + <ConformanceMode>true</ConformanceMode> </ClCompile> <Lib> <OutputFile>$(OutDir)$(ProjectName).lib</OutputFile> </Lib> </ItemDefinitionGroup> <ItemGroup> + <ClCompile Include="src\background.cpp" /> <ClCompile Include="src\codepoint.cpp" /> <ClCompile Include="src\css_borders.cpp" /> <ClCompile Include="src\css_length.cpp" /> + <ClCompile Include="src\css_parser.cpp" /> <ClCompile Include="src\css_properties.cpp" /> <ClCompile Include="src\css_selector.cpp" /> + <ClCompile Include="src\css_tokenizer.cpp" /> <ClCompile Include="src\document.cpp" /> <ClCompile Include="src\document_container.cpp" /> <ClCompile Include="src\element.cpp" /> @@ -191,19 +202,79 @@ <ClCompile Include="src\el_text.cpp" /> <ClCompile Include="src\el_title.cpp" /> <ClCompile Include="src\el_tr.cpp" /> + <ClCompile Include="src\encodings.cpp" /> + <ClCompile Include="src\flex_item.cpp" /> + <ClCompile Include="src\flex_line.cpp" /> <ClCompile Include="src\formatting_context.cpp" /> - <ClCompile Include="src\gumbo\attribute.c" /> - <ClCompile Include="src\gumbo\char_ref.c" /> - <ClCompile Include="src\gumbo\error.c" /> - <ClCompile Include="src\gumbo\parser.c" /> - <ClCompile Include="src\gumbo\string_buffer.c" /> - <ClCompile Include="src\gumbo\string_piece.c" /> - <ClCompile Include="src\gumbo\tag.c" /> - <ClCompile Include="src\gumbo\tokenizer.c" /> - <ClCompile Include="src\gumbo\utf8.c" /> - <ClCompile Include="src\gumbo\util.c" /> - <ClCompile Include="src\gumbo\vector.c" /> + <ClCompile Include="src\gradient.cpp" /> + <ClCompile Include="src\gumbo\attribute.c"> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">4244;4267</DisableSpecificWarnings> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">4244;4267</DisableSpecificWarnings> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">4244;4267</DisableSpecificWarnings> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|x64'">4244;4267</DisableSpecificWarnings> + </ClCompile> + <ClCompile Include="src\gumbo\char_ref.c"> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">4244;4267</DisableSpecificWarnings> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">4244;4267</DisableSpecificWarnings> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">4244;4267</DisableSpecificWarnings> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|x64'">4244;4267</DisableSpecificWarnings> + </ClCompile> + <ClCompile Include="src\gumbo\error.c"> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">4244;4267</DisableSpecificWarnings> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">4244;4267</DisableSpecificWarnings> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">4244;4267</DisableSpecificWarnings> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|x64'">4244;4267</DisableSpecificWarnings> + </ClCompile> + <ClCompile Include="src\gumbo\parser.c"> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">4244;4267</DisableSpecificWarnings> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">4244;4267</DisableSpecificWarnings> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">4244;4267</DisableSpecificWarnings> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|x64'">4244;4267</DisableSpecificWarnings> + </ClCompile> + <ClCompile Include="src\gumbo\string_buffer.c"> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">4244;4267</DisableSpecificWarnings> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">4244;4267</DisableSpecificWarnings> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">4244;4267</DisableSpecificWarnings> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|x64'">4244;4267</DisableSpecificWarnings> + </ClCompile> + <ClCompile Include="src\gumbo\string_piece.c"> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">4244;4267</DisableSpecificWarnings> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">4244;4267</DisableSpecificWarnings> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">4244;4267</DisableSpecificWarnings> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|x64'">4244;4267</DisableSpecificWarnings> + </ClCompile> + <ClCompile Include="src\gumbo\tag.c"> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">4244;4267</DisableSpecificWarnings> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">4244;4267</DisableSpecificWarnings> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">4244;4267</DisableSpecificWarnings> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|x64'">4244;4267</DisableSpecificWarnings> + </ClCompile> + <ClCompile Include="src\gumbo\tokenizer.c"> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">4244;4267</DisableSpecificWarnings> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">4244;4267</DisableSpecificWarnings> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">4244;4267</DisableSpecificWarnings> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|x64'">4244;4267</DisableSpecificWarnings> + </ClCompile> + <ClCompile Include="src\gumbo\utf8.c"> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">4244;4267</DisableSpecificWarnings> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">4244;4267</DisableSpecificWarnings> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">4244;4267</DisableSpecificWarnings> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|x64'">4244;4267</DisableSpecificWarnings> + </ClCompile> + <ClCompile Include="src\gumbo\util.c"> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">4244;4267</DisableSpecificWarnings> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">4244;4267</DisableSpecificWarnings> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">4244;4267</DisableSpecificWarnings> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|x64'">4244;4267</DisableSpecificWarnings> + </ClCompile> + <ClCompile Include="src\gumbo\vector.c"> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">4244;4267</DisableSpecificWarnings> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">4244;4267</DisableSpecificWarnings> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">4244;4267</DisableSpecificWarnings> + <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|x64'">4244;4267</DisableSpecificWarnings> + </ClCompile> <ClCompile Include="src\html.cpp" /> + <ClCompile Include="src\html_microsyntaxes.cpp" /> <ClCompile Include="src\html_tag.cpp" /> <ClCompile Include="src\iterators.cpp" /> <ClCompile Include="src\line_box.cpp" /> @@ -234,8 +305,10 @@ <ClInclude Include="include\litehtml\css_length.h" /> <ClInclude Include="include\litehtml\css_margins.h" /> <ClInclude Include="include\litehtml\css_offsets.h" /> + <ClInclude Include="include\litehtml\css_parser.h" /> <ClInclude Include="include\litehtml\css_position.h" /> <ClInclude Include="include\litehtml\css_selector.h" /> + <ClInclude Include="include\litehtml\css_tokenizer.h" /> <ClInclude Include="include\litehtml\document.h" /> <ClInclude Include="include\litehtml\document_container.h" /> <ClInclude Include="include\litehtml\element.h" /> @@ -259,6 +332,10 @@ <ClInclude Include="include\litehtml\el_text.h" /> <ClInclude Include="include\litehtml\el_title.h" /> <ClInclude Include="include\litehtml\el_tr.h" /> + <ClInclude Include="include\litehtml\encodings.h" /> + <ClInclude Include="include\litehtml\flex_item.h" /> + <ClInclude Include="include\litehtml\flex_line.h" /> + <ClInclude Include="include\litehtml\html_microsyntaxes.h" /> <ClInclude Include="include\litehtml\master_css.h" /> <ClInclude Include="include\litehtml\num_cvt.h" /> <ClInclude Include="include\litehtml\string_id.h" /> diff --git a/litehtml.vcxproj.filters b/litehtml.vcxproj.filters index f4a16a22b..8c3f930f1 100644 --- a/litehtml.vcxproj.filters +++ b/litehtml.vcxproj.filters @@ -212,6 +212,30 @@ <ClCompile Include="src\url_path.cpp"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="src\flex_item.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\flex_line.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\encodings.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\background.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\gradient.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\css_parser.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\css_tokenizer.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\html_microsyntaxes.cpp"> + <Filter>Source Files</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="include\litehtml\background.h"> @@ -406,5 +430,23 @@ <ClInclude Include="include\litehtml\string_id.h"> <Filter>Header Files</Filter> </ClInclude> + <ClInclude Include="include\litehtml\flex_item.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="include\litehtml\flex_line.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="include\litehtml\encodings.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="include\litehtml\css_parser.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="include\litehtml\css_tokenizer.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="include\litehtml\html_microsyntaxes.h"> + <Filter>Header Files</Filter> + </ClInclude> </ItemGroup> </Project> \ No newline at end of file diff --git a/src/background.cpp b/src/background.cpp new file mode 100644 index 000000000..12198e5af --- /dev/null +++ b/src/background.cpp @@ -0,0 +1,916 @@ +#include <cmath> + +#include "background.h" +#include "render_item.h" +#include "document.h" +#include "document_container.h" + +#ifndef M_PI +# define M_PI 3.14159265358979323846 +#endif + +namespace litehtml +{ + +bool litehtml::background::get_layer(int idx, position pos, const element* el, const std::shared_ptr<render_item>& ri, background_layer& layer) const +{ + if(idx < 0 || idx >= get_layers_number()) + { + return false; + } + + position content_box = pos; + position padding_box = pos; + padding_box += ri->get_paddings(); + position border_box = padding_box; + border_box += ri->get_borders(); + + layer.border_radius = el->css().get_borders().radius.calc_percents(border_box.width, border_box.height); + layer.border_box = border_box; + layer.is_root = el->is_root(); + + int clip; + css_size size; + css_length position_x; + css_length position_y; + + if(idx == (int) m_image.size()) + { + if(m_image.empty()) + { + clip = !m_clip.empty() ? m_clip.front() : background_box_border; + } else + { + clip = m_clip.empty() ? background_box_border : + m_clip[(idx - 1) % m_clip.size()]; + } + } else + { + layer.attachment = m_attachment.empty() ? background_attachment_scroll : + (background_attachment) m_attachment[idx % m_attachment.size()]; + layer.repeat = m_repeat.empty() ? background_repeat_repeat : + (background_repeat) m_repeat[idx % m_repeat.size()]; + clip = m_clip.empty() ? background_box_border : + m_clip[idx % m_clip.size()]; + int origin = m_origin.empty() ? background_box_padding : + m_origin[idx % m_origin.size()]; + const css_size auto_auto(css_length::predef_value(background_size_auto), + css_length::predef_value(background_size_auto)); + size = m_size.empty() ? auto_auto : + m_size[idx % m_size.size()]; + position_x = m_position_x.empty() ? css_length(0, css_units_percentage) : + m_position_x[idx % m_position_x.size()]; + position_y = m_position_y.empty() ? css_length(0, css_units_percentage) : + m_position_y[idx % m_position_y.size()]; + + switch(origin) + { + case background_box_border: + layer.origin_box = border_box; + break; + case background_box_content: + layer.origin_box = content_box; + break; + default: + layer.origin_box = padding_box; + break; + } + } + + switch(clip) + { + case background_box_padding: + layer.clip_box = padding_box; + break; + case background_box_content: + layer.clip_box = content_box; + break; + default: + layer.clip_box = border_box; + break; + } + + litehtml::size bg_size(layer.origin_box.width, layer.origin_box.height); + + if(get_layer_type(idx) == type_image) + { + auto image_layer = get_image_layer(idx); + if(image_layer) + { + litehtml::size img_size; + el->get_document()->container()->get_image_size(image_layer->url.c_str(), image_layer->base_url.c_str(), + img_size); + if (img_size.width && img_size.height) + { + litehtml::size img_new_sz = img_size; + double img_ar_width = (double) img_size.width / (double) img_size.height; + double img_ar_height = (double) img_size.height / (double) img_size.width; + + if (size.width.is_predefined()) + { + switch (size.width.predef()) + { + case background_size_contain: + if ((int) ((double) layer.origin_box.width * img_ar_height) <= layer.origin_box.height) + { + img_new_sz.width = layer.origin_box.width; + img_new_sz.height = (int) ((double) layer.origin_box.width * img_ar_height); + } else + { + img_new_sz.height = layer.origin_box.height; + img_new_sz.width = (int) ((double) layer.origin_box.height * img_ar_width); + } + break; + case background_size_cover: + if ((int) ((double) layer.origin_box.width * img_ar_height) >= layer.origin_box.height) + { + img_new_sz.width = layer.origin_box.width; + img_new_sz.height = (int) ((double) layer.origin_box.width * img_ar_height); + } else + { + img_new_sz.height = layer.origin_box.height; + img_new_sz.width = (int) ((double) layer.origin_box.height * img_ar_width); + } + break; + case background_size_auto: + if (!size.height.is_predefined()) + { + img_new_sz.height = size.height.calc_percent(layer.origin_box.height); + img_new_sz.width = (int) ((double) img_new_sz.height * img_ar_width); + } + break; + } + } else + { + img_new_sz.width = size.width.calc_percent(layer.origin_box.width); + if (size.height.is_predefined()) + { + img_new_sz.height = (int) ((double) img_new_sz.width * img_ar_height); + } else + { + img_new_sz.height = size.height.calc_percent(layer.origin_box.height); + } + } + bg_size = img_new_sz; + } + } + } else + { + if(!size.width.is_predefined()) + { + bg_size.width = size.width.calc_percent(layer.origin_box.width); + } + if(!size.height.is_predefined()) + { + bg_size.height = size.height.calc_percent(layer.origin_box.height); + } + } + + position new_origin_box; + new_origin_box.width = bg_size.width; + new_origin_box.height = bg_size.height; + new_origin_box.x = layer.origin_box.x + (int) position_x.calc_percent(layer.origin_box.width - bg_size.width); + new_origin_box.y = layer.origin_box.y + (int) position_y.calc_percent(layer.origin_box.height - bg_size.height); + layer.origin_box = new_origin_box; + + return true; +} + +std::unique_ptr<litehtml::background_layer::image> litehtml::background::get_image_layer(int idx) const +{ + if(idx >= 0 && idx < (int) m_image.size()) + { + if(m_image[idx].type == image::type_url) + { + auto ret = std::make_unique<background_layer::image>(); + ret->url = m_image[idx].url; + ret->base_url = m_baseurl; + return ret; + } + } + return {}; +} + +std::unique_ptr<litehtml::background_layer::color> litehtml::background::get_color_layer(int idx) const +{ + if(idx == (int) m_image.size()) + { + auto ret = std::make_unique<background_layer::color>(); + ret->color = m_color; + return ret; + } + return {}; +} + +// Compute the endpoints so that a gradient of the given angle covers a box of +// the given size. +// https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/css/css_gradient_value.cc;drc=7061f1585ab97cc3358d1e0fc9e950e5a107a7f9;l=1070 +static void EndPointsFromAngle(float angle_deg, + const litehtml::size& size, + litehtml::pointF& first_point, + litehtml::pointF& second_point) +{ + angle_deg = fmodf(angle_deg, 360); + if (angle_deg < 0) + angle_deg += 360; + + if (angle_deg == 0) + { + first_point.set(0, (float) size.height); + second_point.set(0, 0); + return; + } + + if (angle_deg == 90) + { + first_point.set(0, 0); + second_point.set((float) size.width, 0); + return; + } + + if (angle_deg == 180) + { + first_point.set(0, 0); + second_point.set(0, (float) size.height); + return; + } + + if (angle_deg == 270) + { + first_point.set((float) size.width, 0); + second_point.set(0, 0); + return; + } + + // angleDeg is a "bearing angle" (0deg = N, 90deg = E), + // but tan expects 0deg = E, 90deg = N. + auto slope = (float) tan((90.0 - angle_deg) * M_PI / 180.0); + + // We find the endpoint by computing the intersection of the line formed by + // the slope, and a line perpendicular to it that intersects the corner. + float perpendicular_slope = -1 / slope; + + // Compute start corner relative to center, in Cartesian space (+y = up). + float half_height = (float) size.height / 2.0f; + float half_width = (float) size.width / 2.0f; + litehtml::pointF end_corner; + if (angle_deg < 90) + end_corner.set(half_width, half_height); + else if (angle_deg < 180) + end_corner.set(half_width, -half_height); + else if (angle_deg < 270) + end_corner.set(-half_width, -half_height); + else + end_corner.set(-half_width, half_height); + + // Compute c (of y = mx + c) using the corner point. + float c = end_corner.y - perpendicular_slope * end_corner.x; + float end_x = c / (slope - perpendicular_slope); + float end_y = perpendicular_slope * end_x + c; + + // We computed the end point, so set the second point, taking into account the + // moved origin and the fact that we're in drawing space (+y = down). + second_point.set(half_width + end_x, half_height - end_y); + // Reflect around the center for the start point. + first_point.set(half_width - end_x, half_height + end_y); +} + +static float distance(const litehtml::pointF& p1, const litehtml::pointF& p2) +{ + double dx = p2.x - p1.x; + double dy = p2.y - p1.y; + return (float) sqrt(dx * dx + dy * dy); +} + +std::unique_ptr<litehtml::background_layer::linear_gradient> litehtml::background::get_linear_gradient_layer(int idx, const background_layer& layer) const +{ + if(idx < 0 || idx >= (int) m_image.size()) return {}; + if(m_image[idx].type != image::type_gradient) return {}; + if(m_image[idx].m_gradient.m_type != _linear_gradient_ && + m_image[idx].m_gradient.m_type != _repeating_linear_gradient_) return {}; + + auto ret = std::make_unique<background_layer::linear_gradient>(); + float angle; + if(m_image[idx].m_gradient.m_side == 0) + { + angle = m_image[idx].m_gradient.angle; + } else + { + auto rise = (float) layer.origin_box.width; + auto run = (float) layer.origin_box.height; + if(m_image[idx].m_gradient.m_side & gradient_side_left) + { + run *= -1; + } + if(m_image[idx].m_gradient.m_side & gradient_side_bottom) + { + rise *= -1; + } + angle = (float) (90 - atan2(rise, run) * 180 / M_PI); + } + EndPointsFromAngle(angle, {layer.origin_box.width, layer.origin_box.height}, ret->start, + ret->end); + ret->start.x += (float) layer.origin_box.x; + ret->start.y += (float) layer.origin_box.y; + ret->end.x += (float) layer.origin_box.x; + ret->end.y += (float) layer.origin_box.y; + + auto line_len = distance(ret->start, ret->end); + + if(!ret->prepare_color_points(line_len, m_image[idx].m_gradient.m_type, m_image[idx].m_gradient.m_colors)) + { + return {}; + } + + return ret; +} + +static inline litehtml::pointF calc_ellipse_radius(const litehtml::pointF& offset, float aspect_ratio) +{ + // If the aspectRatio is 0 or infinite, the ellipse is completely flat. + // (If it is NaN, the ellipse is 0x0, and should be handled as zero width.) + if (!std::isfinite(aspect_ratio) || aspect_ratio == 0) + { + return {0, 0}; + } + + // x^2/a^2 + y^2/b^2 = 1 + // a/b = aspectRatio, b = a/aspectRatio + // a = sqrt(x^2 + y^2/(1/aspect_ratio^2)) + float a = sqrtf(offset.x * offset.x + + offset.y * offset.y * + aspect_ratio * aspect_ratio); + return {a, a / aspect_ratio}; +} + +static inline litehtml::pointF find_corner(const litehtml::pointF& center, const litehtml::position& box, bool farthest) +{ + struct descr + { + float distance; + float x; + float y; + + descr(float _distance, float _x, float _y) : distance(_distance), x(_x), y(_y) {} + }; + + litehtml::pointF ret; + + // Default is left-top corner + ret.x = (float) box.left(); + ret.y = (float) box.top(); + auto dist = distance(center, {(float) box.left(), (float) box.top()}); + + // Check right-top corner + auto next_dist = distance(center, {(float) box.right(), (float) box.top()}); + if((farthest && next_dist > dist) || (!farthest && next_dist < dist)) + { + ret.x = (float) box.right(); + ret.y = (float) box.top(); + dist = next_dist; + } + + // Check right-bottom corner + next_dist = distance(center, {(float) box.right(), (float) box.bottom()}); + if((farthest && next_dist > dist) || (!farthest && next_dist < dist)) + { + ret.x = (float) box.right(); + ret.y = (float) box.bottom(); + dist = next_dist; + } + + // Check left-bottom corner + next_dist = distance(center, {(float) box.left(), (float) box.bottom()}); + if((farthest && next_dist > dist) || (!farthest && next_dist < dist)) + { + ret.x = (float) box.left(); + ret.y = (float) box.bottom(); + dist = next_dist; + } + + ret.x -= center.x; + ret.y -= center.y; + + return ret; +} + +std::unique_ptr<litehtml::background_layer::radial_gradient> litehtml::background::get_radial_gradient_layer(int idx, const background_layer& layer) const +{ + if(idx < 0 || idx >= (int) m_image.size()) return {}; + if(m_image[idx].type != image::type_gradient) return {}; + if(m_image[idx].m_gradient.m_type != _radial_gradient_ && + m_image[idx].m_gradient.m_type != _repeating_radial_gradient_) return {}; + + auto ret = std::make_unique<background_layer::radial_gradient>(); + + ret->position.x = (float) layer.origin_box.x + (float) layer.origin_box.width / 2.0f; + ret->position.y = (float) layer.origin_box.y + (float) layer.origin_box.height / 2.0f; + + if(m_image[idx].m_gradient.m_side & gradient_side_left) + { + ret->position.x = (float) layer.origin_box.left(); + } else if(m_image[idx].m_gradient.m_side & gradient_side_right) + { + ret->position.x = (float) layer.origin_box.right(); + } else if(m_image[idx].m_gradient.m_side & gradient_side_x_center) + { + ret->position.x = (float) layer.origin_box.left() + (float) layer.origin_box.width / 2.0f; + } else if(m_image[idx].m_gradient.m_side & gradient_side_x_length) + { + ret->position.x = (float) layer.origin_box.left() + (float) m_image[idx].m_gradient.position_x.calc_percent(layer.origin_box.width); + } + + if(m_image[idx].m_gradient.m_side & gradient_side_top) + { + ret->position.y = (float) layer.origin_box.top(); + } else if(m_image[idx].m_gradient.m_side & gradient_side_bottom) + { + ret->position.y = (float) layer.origin_box.bottom(); + } else if(m_image[idx].m_gradient.m_side & gradient_side_y_center) + { + ret->position.y = (float) layer.origin_box.top() + (float) layer.origin_box.height / 2.0f; + } else if(m_image[idx].m_gradient.m_side & gradient_side_y_length) + { + ret->position.y = (float) layer.origin_box.top() + (float) m_image[idx].m_gradient.position_y.calc_percent(layer.origin_box.height); + } + + if(m_image[idx].m_gradient.radial_extent) + { + switch (m_image[idx].m_gradient.radial_extent) + { + + case radial_extent_closest_corner: + { + if (m_image[idx].m_gradient.radial_shape == radial_shape_circle) + { + float corner1 = distance(ret->position, {(float) layer.origin_box.left(), (float) layer.origin_box.top()}); + float corner2 = distance(ret->position, {(float) layer.origin_box.right(), (float) layer.origin_box.top()}); + float corner3 = distance(ret->position, {(float) layer.origin_box.left(), (float) layer.origin_box.bottom()}); + float corner4 = distance(ret->position, {(float) layer.origin_box.right(), (float) layer.origin_box.bottom()}); + ret->radius.x = ret->radius.y = std::min({corner1, corner2, corner3, corner4}); + } else + { + // Aspect ratio is the same as for radial_extent_closest_side + float aspect_ration = std::min( + std::abs(ret->position.x - (float) layer.origin_box.left()), + std::abs(ret->position.x - (float) layer.origin_box.right()) + ) / std::min( + std::abs(ret->position.y - (float) layer.origin_box.top()), + std::abs(ret->position.y - (float) layer.origin_box.bottom()) + ); + + auto corner = find_corner(ret->position, layer.origin_box, false); + auto radius = calc_ellipse_radius(corner, aspect_ration); + ret->radius.x = radius.x; + ret->radius.y = radius.y; + } + } + break; + case radial_extent_closest_side: + if (m_image[idx].m_gradient.radial_shape == radial_shape_circle) + { + ret->radius.x = ret->radius.y = std::min( + { + std::abs(ret->position.x - (float) layer.origin_box.left()), + std::abs(ret->position.x - (float) layer.origin_box.right()), + std::abs(ret->position.y - (float) layer.origin_box.top()), + std::abs(ret->position.y - (float) layer.origin_box.bottom()), + }); + } else + { + ret->radius.x = std::min( + std::abs(ret->position.x - (float) layer.origin_box.left()), + std::abs(ret->position.x - (float) layer.origin_box.right()) + ); + ret->radius.y = std::min( + std::abs(ret->position.y - (float) layer.origin_box.top()), + std::abs(ret->position.y - (float) layer.origin_box.bottom()) + ); + } + break; + case radial_extent_farthest_corner: + { + if (m_image[idx].m_gradient.radial_shape == radial_shape_circle) + { + float corner1 = distance(ret->position, {(float) layer.origin_box.left(), (float) layer.origin_box.top()}); + float corner2 = distance(ret->position, {(float) layer.origin_box.right(), (float) layer.origin_box.top()}); + float corner3 = distance(ret->position, {(float) layer.origin_box.left(), (float) layer.origin_box.bottom()}); + float corner4 = distance(ret->position, {(float) layer.origin_box.right(), (float) layer.origin_box.bottom()}); + ret->radius.x = ret->radius.y = std::max({corner1, corner2, corner3, corner4}); + } else + { + // Aspect ratio is the same as for radial_extent_farthest_side + float aspect_ration = std::max( + std::abs(ret->position.x - (float) layer.origin_box.left()), + std::abs(ret->position.x - (float) layer.origin_box.right()) + ) / std::max( + std::abs(ret->position.y - (float) layer.origin_box.top()), + std::abs(ret->position.y - (float) layer.origin_box.bottom()) + ); + + auto corner = find_corner(ret->position, layer.origin_box, true); + auto radius = calc_ellipse_radius(corner, aspect_ration); + ret->radius.x = radius.x; + ret->radius.y = radius.y; + } + } + break; + case radial_extent_farthest_side: + if (m_image[idx].m_gradient.radial_shape == radial_shape_circle) + { + ret->radius.x = ret->radius.y = std::max( + { + std::abs(ret->position.x - (float) layer.origin_box.left()), + std::abs(ret->position.x - (float) layer.origin_box.right()), + std::abs(ret->position.y - (float) layer.origin_box.top()), + std::abs(ret->position.y - (float) layer.origin_box.bottom()), + }); + } else + { + ret->radius.x = std::max( + std::abs(ret->position.x - (float) layer.origin_box.left()), + std::abs(ret->position.x - (float) layer.origin_box.right()) + ); + ret->radius.y = std::max( + std::abs(ret->position.y - (float) layer.origin_box.top()), + std::abs(ret->position.y - (float) layer.origin_box.bottom()) + ); + } + break; + default: + break; + } + } + if(!m_image[idx].m_gradient.radial_radius_x.is_predefined()) + { + ret->radius.x = (float) m_image[idx].m_gradient.radial_radius_x.calc_percent(layer.origin_box.width); + } + if(!m_image[idx].m_gradient.radial_radius_y.is_predefined()) + { + ret->radius.y = (float) m_image[idx].m_gradient.radial_radius_y.calc_percent(layer.origin_box.height); + } + + if(ret->prepare_color_points(ret->radius.x, m_image[idx].m_gradient.m_type, m_image[idx].m_gradient.m_colors)) + { + return ret; + } + + return {}; +} + +std::unique_ptr<litehtml::background_layer::conic_gradient> litehtml::background::get_conic_gradient_layer(int idx, const background_layer& layer) const +{ + if(idx < 0 || idx >= (int) m_image.size()) return {}; + if(m_image[idx].type != image::type_gradient) return {}; + if (m_image[idx].m_gradient.m_type != _conic_gradient_ && + m_image[idx].m_gradient.m_type != _repeating_conic_gradient_) return {}; + + auto ret = std::make_unique<background_layer::conic_gradient>(); + + ret->position.x = (float) layer.origin_box.x + (float) layer.origin_box.width / 2.0f; + ret->position.y = (float) layer.origin_box.y + (float) layer.origin_box.height / 2.0f; + + if(m_image[idx].m_gradient.m_side & gradient_side_left) + { + ret->position.x = (float) layer.origin_box.left(); + } else if(m_image[idx].m_gradient.m_side & gradient_side_right) + { + ret->position.x = (float) layer.origin_box.right(); + } else if(m_image[idx].m_gradient.m_side & gradient_side_x_center) + { + ret->position.x = (float) layer.origin_box.left() + (float) layer.origin_box.width / 2.0f; + } else if(m_image[idx].m_gradient.m_side & gradient_side_x_length) + { + ret->position.x = (float) layer.origin_box.left() + (float) m_image[idx].m_gradient.position_x.calc_percent(layer.origin_box.width); + } + + if(m_image[idx].m_gradient.m_side & gradient_side_top) + { + ret->position.y = (float) layer.origin_box.top(); + } else if(m_image[idx].m_gradient.m_side & gradient_side_bottom) + { + ret->position.y = (float) layer.origin_box.bottom(); + } else if(m_image[idx].m_gradient.m_side & gradient_side_y_center) + { + ret->position.y = (float) layer.origin_box.top() + (float) layer.origin_box.height / 2.0f; + } else if(m_image[idx].m_gradient.m_side & gradient_side_y_length) + { + ret->position.y = (float) layer.origin_box.top() + (float) m_image[idx].m_gradient.position_y.calc_percent(layer.origin_box.height); + } + + ret->angle = m_image[idx].m_gradient.conic_from_angle; + ret->color_space = m_image[idx].m_gradient.color_space; + ret->hue_interpolation = m_image[idx].m_gradient.hue_interpolation; + + float corner1 = distance(ret->position, {(float) layer.origin_box.left(), (float) layer.origin_box.top()}); + float corner2 = distance(ret->position, {(float) layer.origin_box.right(), (float) layer.origin_box.top()}); + float corner3 = distance(ret->position, {(float) layer.origin_box.left(), (float) layer.origin_box.bottom()}); + float corner4 = distance(ret->position, {(float) layer.origin_box.right(), (float) layer.origin_box.bottom()}); + ret->radius = std::max({corner1, corner2, corner3, corner4}); + + if(ret->prepare_color_points(0, m_image[idx].m_gradient.m_type, m_image[idx].m_gradient.m_colors)) + { + return ret; + } + + return {}; +} + +litehtml::background::layer_type litehtml::background::get_layer_type(int idx) const +{ + if(idx >= 0 && idx < (int) m_image.size()) + { + switch (m_image[idx].type) + { + + case image::type_url: + return type_image; + case image::type_gradient: + switch (m_image[idx].m_gradient.m_type) + { + case _linear_gradient_: + case _repeating_linear_gradient_: + return type_linear_gradient; + case _radial_gradient_: + case _repeating_radial_gradient_: + return type_radial_gradient; + case _conic_gradient_: + case _repeating_conic_gradient_: + return type_conic_gradient; + default: + break; + } + break; + default: + break; + } + } else if(idx == (int) m_image.size()) + { + return type_color; + } + return type_none; +} + +void litehtml::background::draw_layer(uint_ptr hdc, int idx, const background_layer& layer, document_container* container) const +{ + switch (get_layer_type(idx)) + { + case background::type_color: + { + auto color_layer = get_color_layer(idx); + if(color_layer) + { + container->draw_solid_fill(hdc, layer, color_layer->color); + } + } + break; + case background::type_image: + if(layer.origin_box.width != 0 && layer.origin_box.height != 0) + { + auto image_layer = get_image_layer(idx); + if(image_layer) + { + container->draw_image(hdc, layer, image_layer->url, image_layer->base_url); + } + } + break; + case background::type_linear_gradient: + if(layer.origin_box.width != 0 && layer.origin_box.height != 0) + { + auto gradient_layer = get_linear_gradient_layer(idx, layer); + if(gradient_layer) + { + container->draw_linear_gradient(hdc, layer, *gradient_layer); + } + } + break; + case background::type_radial_gradient: + if(layer.origin_box.width != 0 && layer.origin_box.height != 0) + { + auto gradient_layer = get_radial_gradient_layer(idx, layer); + if(gradient_layer) + { + container->draw_radial_gradient(hdc, layer, *gradient_layer); + } + } + break; + case background::type_conic_gradient: + if(layer.origin_box.width != 0 && layer.origin_box.height != 0) + { + auto gradient_layer = get_conic_gradient_layer(idx, layer); + if(gradient_layer) + { + container->draw_conic_gradient(hdc, layer, *gradient_layer); + } + } + break; + default: + break; + } +} + +static void repeat_color_points(std::vector<litehtml::background_layer::color_point>& color_points) +{ + auto old_points = color_points; + if(color_points.back().offset < 1) + { + float gd_size = color_points.back().offset - old_points.front().offset; + auto iter = old_points.begin(); + while (color_points.back().offset < 1) + { + color_points.emplace_back(iter->offset + gd_size, iter->color); + std::advance(iter, 1); + if (iter == old_points.end()) + { + iter = old_points.begin(); + gd_size = color_points.back().offset - old_points.front().offset; + } + } + } + if(color_points.front().offset > 0) + { + float gd_size = color_points.front().offset; + auto iter = old_points.rbegin(); + while (color_points.front().offset > 0) + { + color_points.emplace(color_points.begin(), gd_size - (old_points.back().offset - iter->offset), iter->color); + std::advance(iter, 1); + if (iter == old_points.rend()) + { + iter = old_points.rbegin(); + gd_size = color_points.front().offset; + } + } + } +} + +void litehtml::background_layer::gradient_base::color_points_transparent_fix() +{ + for(int i = 0; i < (int) color_points.size(); i++) + { + if(color_points[i].color.alpha == 0) + { + if(i == 0) + { + if(i + 1 < (int) color_points.size()) + { + color_points[i].color = color_points[i + 1].color; + color_points[i].color.alpha = 0; + } + } else if(i + 1 == (int) color_points.size()) + { + if(i - 1 >= 0) + { + color_points[i].color = color_points[i - 1].color; + color_points[i].color.alpha = 0; + } + } else + { + color_points[i].color = color_points[i + 1].color; + color_points[i].color.alpha = 0; + background_layer::color_point cpt; + cpt.color = color_points[i - 1].color; + cpt.color.alpha = 0; + cpt.offset = color_points[i].offset; + color_points.emplace(std::next(color_points.begin(), i), cpt); + i++; + } + } + } +} + +// normalize length into value between 0 and 1 +float normalize_length(css_length length, float line_len) +{ + if (length.units() == css_units_percentage) + { + return length.val() / 100.0f; + } + else if (line_len != 0) + { + return length.val() / line_len; + } + return length.val(); +} + +bool litehtml::background_layer::gradient_base::prepare_color_points(float line_len, string_id g_type, const std::vector<gradient::color_stop> &colors) +{ + bool repeating; + if(g_type == _linear_gradient_ || g_type == _radial_gradient_ || g_type == _conic_gradient_) + { + repeating = false; + } else if(g_type == _repeating_linear_gradient_ || g_type == _repeating_radial_gradient_ || g_type == _repeating_conic_gradient_) + { + repeating = true; + } else + { + return false; + } + int none_units = 0; + bool has_transparent = false; + for(const auto& item : colors) + { + if (item.is_color_hint) + { + if (!color_points.empty()) + { + color_points.back().hint = item.length ? + normalize_length(*item.length, line_len) : + *item.angle / 360; + } + continue; + } + if (item.color.alpha == 0) + { + has_transparent = true; + } + if (item.length) + { + color_points.emplace_back(normalize_length(*item.length, line_len), item.color); + } + else if (item.angle) + { + color_points.emplace_back(*item.angle / 360, item.color); + } + else + { + if (!color_points.empty()) + { + none_units++; + } + color_points.emplace_back(0.0f, item.color); + } + } + if(color_points.empty()) + { + return false; + } + + if(!repeating) + { + // Add color point with offset 0 if not exists + if(color_points[0].offset != 0) + { + color_points.emplace(color_points.begin(), 0.0f, color_points[0].color); + } + // Add color point with offset 1.0 if not exists + if (color_points.back().offset < 1) + { + if (color_points.back().offset == 0) + { + color_points.back().offset = 1; + none_units--; + } else + { + color_points.emplace_back(1.0f, color_points.back().color); + } + } + } else + { + // Add color point with offset 1.0 if not exists + if (color_points.back().offset == 0) + { + color_points.back().offset = 1; + none_units--; + } + } + + if(none_units > 0) + { + size_t i = 1; + while(i < color_points.size()) + { + if(color_points[i].offset != 0) + { + i++; + continue; + } + // Find next defined offset + size_t j = i + 1; + while (color_points[j].offset == 0) j++; + size_t num = j - i; + float sum = color_points[i - 1].offset + color_points[j].offset; + float offset = sum / (float) (num + 1); + while(i < j) + { + color_points[i].offset = color_points[i - 1].offset + offset; + i++; + } + } + } + + // process transparent + if(has_transparent) + { + color_points_transparent_fix(); + } + + if(repeating) + { + repeat_color_points(color_points); + } + + return true; +} + +} // namespace litehtml \ No newline at end of file diff --git a/src/codepoint.cpp b/src/codepoint.cpp index a89057022..d93230e41 100644 --- a/src/codepoint.cpp +++ b/src/codepoint.cpp @@ -29,7 +29,7 @@ #include "codepoint.h" -#include <iostream> +#include <cstdint> namespace { @@ -44,7 +44,7 @@ namespace litehtml { bool is_ascii_codepoint(char c) { - return (c < 128); + return ((unsigned char) c < 128); } // https://datatracker.ietf.org/doc/html/rfc3986#section-2.2 diff --git a/src/css_borders.cpp b/src/css_borders.cpp index 478e9df5b..2b0fe6d84 100644 --- a/src/css_borders.cpp +++ b/src/css_borders.cpp @@ -3,5 +3,5 @@ litehtml::string litehtml::css_border::to_string() const { - return width.to_string() + "/" + index_value(style, border_style_strings) + "/" + color.to_string(); + return width.to_string() + "/" + index_value(style, border_style_strings) + "/" + color.to_string(); } diff --git a/src/css_length.cpp b/src/css_length.cpp index d1c69d261..798eb0bd3 100644 --- a/src/css_length.cpp +++ b/src/css_length.cpp @@ -1,77 +1,78 @@ #include "html.h" #include "css_length.h" -void litehtml::css_length::fromString( const string& str, const string& predefs, int defValue ) +namespace litehtml { - // TODO: Make support for calc - if(str.substr(0, 4) == "calc") + +bool css_length::from_token(const css_token& token, int options, const string& keywords) +{ + if ((options & f_positive) && is_one_of(token.type, NUMBER, DIMENSION, PERCENTAGE) && token.n.number < 0) + return false; + + if (token.type == IDENT) { + int idx = value_index(lowcase(token.name), keywords); + if (idx == -1) return false; + m_predef = idx; m_is_predefined = true; - m_predef = defValue; - return; + return true; } + else if (token.type == DIMENSION) + { + if (!(options & f_length)) return false; - int predef = value_index(str, predefs, -1); - if(predef >= 0) + int idx = value_index(lowcase(token.unit), css_units_strings); + // note: 1none and 1\% are invalid + if (idx == -1 || idx == css_units_none || idx == css_units_percentage) + return false; + + m_value = token.n.number; + m_units = (css_units)idx; + m_is_predefined = false; + return true; + } + else if (token.type == PERCENTAGE) { - m_is_predefined = true; - m_predef = predef; - } else + if (!(options & f_percentage)) return false; + + m_value = token.n.number; + m_units = css_units_percentage; + m_is_predefined = false; + return true; + } + else if (token.type == NUMBER) { + // if token is a nonzero number and neither f_number nor f_integer is specified in the options + if (!(options & (f_number | f_integer)) && token.n.number != 0) + return false; + // if token is a zero number and neither of f_number, f_integer or f_length are specified in the options + if (!(options & (f_number | f_integer | f_length)) && token.n.number == 0) + return false; + if ((options & f_integer) && token.n.number_type != css_number_integer) + return false; + + m_value = token.n.number; + m_units = css_units_none; m_is_predefined = false; - - string num; - string un; - bool is_unit = false; - for(char chr : str) - { - if(!is_unit) - { - if(t_isdigit(chr) || chr == '.' || chr == '+' || chr == '-') - { - num += chr; - } else - { - is_unit = true; - } - } - if(is_unit) - { - un += chr; - } - } - if(!num.empty()) - { - m_value = t_strtof(num); - m_units = (css_units) value_index(un, css_units_strings, css_units_none); - } else - { - // not a number so it is predefined - m_is_predefined = true; - m_predef = defValue; - } + return true; } + return false; } -litehtml::css_length litehtml::css_length::from_string(const string& str, const string& predefs, int defValue) +string css_length::to_string() const { - css_length len; - len.fromString(str, predefs, defValue); - return len; -} - -litehtml::string litehtml::css_length::to_string() const -{ - if(m_is_predefined) - { - return "def(" + std::to_string(m_predef) + ")"; - } - return std::to_string(m_value) + "{" + index_value(m_units, css_units_strings) + "}"; + if(m_is_predefined) + { + return "def(" + std::to_string(m_predef) + ")"; + } + return std::to_string(m_value) + "{" + index_value(m_units, css_units_strings) + "}"; } -litehtml::css_length litehtml::css_length::predef_value(int val) +css_length css_length::predef_value(int val) { css_length len; len.predef(val); return len; } + +} // namespace litehtml \ No newline at end of file diff --git a/src/css_parser.cpp b/src/css_parser.cpp new file mode 100644 index 000000000..3a78b0064 --- /dev/null +++ b/src/css_parser.cpp @@ -0,0 +1,570 @@ +#include "encodings.h" +#include "html.h" +#include "css_parser.h" + +namespace litehtml +{ + +// https://www.w3.org/TR/css-syntax-3/#css-filter-code-points +void filter_code_points(string& input) +{ + const char* xFFFD = "\xEF\xBF\xBD"; + + size_t null_count = std::count(input.begin(), input.end(), 0); + + string result(input.size() + 2 * null_count, 0); + + for (int i = 0, j = 0; i < (int)input.size(); i++) + { + switch (input[i]) + { + case '\r': + result[j++] = '\n'; + if (i + 1 < (int)input.size() && input[i + 1] == '\n') i++; // skip \n after \r + break; + case '\f': + result[j++] = '\n'; + break; + case 0: + memcpy(&result[j], xFFFD, 3); + j += 3; + break; + default: + result[j++] = input[i]; + } + } + + // trim trailing NULs + result.resize(strlen(result.c_str())); + input = result; +} + +static const size_t kLargeSize = 50; +static void remove_whitespace_large(css_token_vector& tokens, keep_whitespace_fn keep_whitespace); +static void remove_whitespace_small(css_token_vector& tokens, keep_whitespace_fn keep_whitespace); + +void remove_whitespace_large(css_token_vector& tokens, keep_whitespace_fn keep_whitespace) +{ + std::vector<int> keep_idx; + keep_idx.reserve(tokens.size()); + for (int i = 0; i < static_cast<int>(tokens.size()); ++i) + { + auto &tok = tokens[i]; + bool keep = true; + if (tok.type == ' ') + { + const auto &left = i > 0 ? tokens[i - 1] : css_token(); + const auto &right = at(tokens, i + 1); + keep = keep_whitespace && keep_whitespace(left, right); + } + else if (tok.is_component_value()) + { + if (tok.value.size() > kLargeSize) + remove_whitespace_large(tok.value, keep_whitespace); + else + remove_whitespace_small(tok.value, keep_whitespace); + } + if (keep) + keep_idx.push_back(i); + } + + if (keep_idx.size() == tokens.size()) + return; + else + { + css_token_vector tmp; + tmp.reserve(keep_idx.size()); + for (auto idx : keep_idx) + tmp.push_back(tokens[idx]); + tokens.swap(tmp); + } +} + +void remove_whitespace_small(css_token_vector& tokens, keep_whitespace_fn keep_whitespace) +{ + for (int i = 0; i < (int)tokens.size(); i++) + { + auto& tok = tokens[i]; + if (tok.type == ' ') + { + const auto& left = i > 0 ? tokens[i - 1] : css_token(); + const auto& right = at(tokens, i + 1); + bool keep = keep_whitespace && keep_whitespace(left, right); + if (!keep) + remove(tokens, i), i--; + } + else if (tok.is_component_value()) + { + if (tok.value.size() > kLargeSize) + remove_whitespace_large(tok.value, keep_whitespace); + else + remove_whitespace_small(tok.value, keep_whitespace); + } + } +} + +void remove_whitespace(css_token_vector& tokens, keep_whitespace_fn keep_whitespace) +{ + if (tokens.size() > kLargeSize) + remove_whitespace_large(tokens, keep_whitespace); + else + remove_whitespace_small(tokens, keep_whitespace); +} + +void componentize(css_token_vector& tokens) +{ + css_parser parser(tokens); + css_token_vector result; + while (true) + { + css_token tok = parser.consume_component_value(); + if (tok.type == EOF) break; + result.push_back(tok); + } + tokens = result; +} + +// https://www.w3.org/TR/css-syntax-3/#normalize-into-a-token-stream +template<> +css_token_vector normalize(css_token_vector input, int options, keep_whitespace_fn keep_whitespace) +{ + if (options & f_componentize) componentize(input); + if (options & f_remove_whitespace) remove_whitespace(input, keep_whitespace); + return input; +} +template<> +css_token_vector normalize(string input, int options, keep_whitespace_fn keep_whitespace) +{ + filter_code_points(input); + auto tokens = tokenize(input); + return normalize(tokens, options, keep_whitespace); +} + +// https://www.w3.org/TR/css-syntax-3/#parse-stylesheet +// I don't create a stylesheet because its only perpose is to pass a list of rules to +// parse_css_stylesheet. I just return the list of rules directly instead. +raw_rule::vector css_parser::parse_stylesheet(const string& input, bool top_level) +{ + // 1. If input is a byte stream for stylesheet, decode bytes from input, and set input to the result. + // not implemented, utf-8 is always assumed + string str = decode(input, encoding::utf_8); // decoding potentially broken UTF-8 into valid UTF-8 + + // 2. Normalize input, and set input to the result. + auto tokens = normalize(str); + + return parse_stylesheet(tokens, top_level); +} +raw_rule::vector css_parser::parse_stylesheet(const css_token_vector& input, bool top_level) +{ + // 3. Create a new stylesheet, with its location set to location. + // 4. Consume a list of rules from input, with the top-level flag set, and set the stylesheet’s value to the result. + // 5. Return the stylesheet. + return css_parser(input).consume_list_of_rules(top_level); +} + +// https://www.w3.org/TR/css-syntax-3/#consume-the-next-input-token +css_token css_parser::next_token() +{ + if (m_index == (int)m_tokens.size()) + return css_token_type(EOF); + else + return m_tokens[m_index++]; +} + +css_token css_parser::peek_token() +{ + if (m_index == (int)m_tokens.size()) + return css_token_type(EOF); + else + return m_tokens[m_index]; +} + +// https://www.w3.org/TR/css-syntax-3/#consume-list-of-rules +raw_rule::vector css_parser::consume_list_of_rules(bool top_level) +{ + raw_rule::vector rules; + raw_rule::ptr rule; + + while (true) + { + // Repeatedly consume the next input token: + css_token token = next_token(); + + switch (token.type) + { + case WHITESPACE: + break; // Do nothing. + + case EOF: + return rules; // Return the list of rules. + + case CDO: + case CDC: + // If the top-level flag is set, do nothing. + if (top_level) break; + + // Otherwise, reconsume the current input token. Consume a qualified rule. + // If anything is returned, append it to the list of rules. + m_index--; + rule = consume_qualified_rule(); + if (rule) rules.push_back(rule); + break; + + case AT_KEYWORD: + // Reconsume the current input token. Consume an at-rule, and append the returned value to the list of rules. + m_index--; + rule = consume_at_rule(); + if (rule) rules.push_back(rule); + break; + + default: + // Reconsume the current input token. Consume a qualified rule. If anything is returned, append it to the list of rules. + m_index--; + rule = consume_qualified_rule(); + if (rule) rules.push_back(rule); + break; + } + } +} + +// https://www.w3.org/TR/css-syntax-3/#consume-qualified-rule +raw_rule::ptr css_parser::consume_qualified_rule() +{ + // Create a new qualified rule with its prelude initially set to an empty list, and its value initially set to nothing. + raw_rule::ptr rule = make_shared<raw_rule>(raw_rule::qualified); + + while (true) + { + // Repeatedly consume the next input token: + css_token token = next_token(); + + switch (token.type) + { + case EOF: + // This is a parse error. Return nothing. + css_parse_error("eof in qualified rule"); + return nullptr; + case '{': + // Consume a simple block and assign it to the qualified rule’s block. Return the qualified rule. + rule->block = consume_simple_block('{'); + return rule; + case CURLY_BLOCK: + // Assign the block to the qualified rule’s block. Return the qualified rule. + rule->block = token; + return rule; + default: + // Reconsume the current input token. Consume a component value. Append the returned value to the qualified rule’s prelude. + m_index--; + css_token value = consume_component_value(); + rule->prelude.push_back(value); + } + } +} + +// https://www.w3.org/TR/css-syntax-3/#consume-at-rule +raw_rule::ptr css_parser::consume_at_rule() +{ + // Consume the next input token. Create a new at-rule with its name set to the value of the current input token, + // its prelude initially set to an empty list, and its value initially set to nothing. + css_token token = next_token(); + raw_rule::ptr rule = make_shared<raw_rule>(raw_rule::at, token.str); + + while (true) + { + // Repeatedly consume the next input token: + token = next_token(); + + switch (token.type) + { + case ';': + return rule; + case EOF: + // This is a parse error. Return the at-rule. + css_parse_error("eof in at-rule"); + return rule; + case '{': + // Consume a simple block and assign it to the at-rule’s block. Return the at-rule. + rule->block = consume_simple_block('{'); + return rule; + case CURLY_BLOCK: + // Assign the block to the at-rule’s block. Return the at-rule. + rule->block = token; + return rule; + default: + // Reconsume the current input token. Consume a component value. Append the returned value to the at-rule’s prelude. + m_index--; + css_token value = consume_component_value(); + rule->prelude.push_back(value); + } + } +} + +char mirror(char c); + +// https://www.w3.org/TR/css-syntax-3/#consume-simple-block +css_token css_parser::consume_simple_block(char opening_bracket) +{ + // Create a simple block with its associated token set to the current input token and with its value initially set to an empty list. + auto block_type = css_token_type(-100 - opening_bracket); // see css_token_type + css_token block(block_type); + + char closing_bracket = mirror(opening_bracket); + + while (true) + { + // Repeatedly consume the next input token and process it as follows: + css_token token = next_token(); + + if (token.type == closing_bracket) + { + return block; + } + else if (token.type == EOF) + { + css_parse_error("eof in simple block"); + return block; + } + else + { + // Reconsume the current input token. Consume a component value and append it to the value of the block. + m_index--; + css_token val = consume_component_value(); + block.value.push_back(val); + } + } +} + +// https://www.w3.org/TR/css-syntax-3/#consume-component-value +css_token css_parser::consume_component_value() +{ + // Consume the next input token. + css_token token = next_token(); + + switch (token.type) + { + // If the current input token is a <{-token>, <[-token>, or <(-token>, consume a simple block and return it. + case '{': case '[': case '(': + return consume_simple_block((char)token.ch); + + // Otherwise, if the current input token is a <function-token>, consume a function and return it. + case FUNCTION: + return consume_function(token.name); + + // Otherwise, return the current input token. + default: + return token; + } +} + +// https://www.w3.org/TR/css-syntax-3/#consume-function +css_token css_parser::consume_function(const string& name) +{ + // Create a function with its name equal to the value of the current input token and with its value initially set to an empty list. + css_token function(CV_FUNCTION, name); + + while (true) + { + // Repeatedly consume the next input token and process it as follows: + css_token token = next_token(); + + switch (token.type) + { + case ')': + return function; + + case EOF: + css_parse_error("eof in function"); + return function; + + default: + // Reconsume the current input token. Consume a component value and append the returned value to the function’s value. + m_index--; + css_token val = consume_component_value(); + function.value.push_back(val); + } + } +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +void trim_whitespace(css_token_vector& tokens) +{ + while (at(tokens, 0).type == ' ') remove(tokens, 0); + while (at(tokens, -1).type == ' ') remove(tokens, -1); +} + +// https://www.w3.org/TR/css-syntax-3/#consume-declaration +// next token is guaranteed to be IDENT +raw_declaration css_parser::consume_declaration() +{ + // Consume the next input token. Create a new declaration with its name set to the value of + // the current input token and its value initially set to an empty list. + css_token token = next_token(); + raw_declaration decl = {token.name}; + auto& value = decl.value; + + // 1. While the next input token is a <whitespace-token>, consume the next input token. + while (peek_token().type == ' ') next_token(); + + // 2. If the next input token is anything other than a <colon-token>, this is a parse error. Return nothing. + if (peek_token().ch != ':') + { + css_parse_error("consume_declaration: ':' not found"); + return {}; + } + // Otherwise, consume the next input token. + next_token(); + + // 3. While the next input token is a <whitespace-token>, consume the next input token. + while (peek_token().type == ' ') next_token(); + + // 4. As long as the next input token is anything other than an <EOF-token>, + // consume a component value and append it to the declaration’s value. + while (peek_token().type != EOF) + value.push_back(consume_component_value()); + + // 5. If the last two non-<whitespace-token>s in the declaration’s value are a <delim-token> with the value "!" + // followed by an <ident-token> with a value that is an ASCII case-insensitive match for "important", + // remove them from the declaration’s value and set the declaration’s important flag to true. + + trim_whitespace(value); // deviation from standard: removing leading whitespace as well + + if (at(value, -1).ident() == "important" && at(value, -2).ch == '!') + { + remove(value, -2, 2); + decl.important = true; + } + + // 6. While the last token in the declaration’s value is a <whitespace-token>, remove that token. + trim_whitespace(value); + + // 7. Return the declaration. + return decl; +} + +// https://www.w3.org/TR/css-syntax-3/#consume-style-block +void css_parser::consume_style_block_contents(/*out*/ raw_declaration::vector& decls, /*out*/ raw_rule::vector& rules) +{ + while (true) + { + // Repeatedly consume the next input token: + css_token token = next_token(); + + switch (token.type) + { + case WHITESPACE: + case ';': + break; // Do nothing. + + case EOF: + // "Extend decls with rules, then return decls." + // NOTE: I just return decls and rules separately + return; + + case AT_KEYWORD: { + // Reconsume the current input token. Consume an at-rule, and append the result to rules. + m_index--; + auto rule = consume_at_rule(); + if (rule) rules.push_back(rule); + break; + } + case IDENT: { + // Initialize a temporary list initially filled with the current input token. + css_token_vector temp = { token }; + // As long as the next input token is anything other than a <semicolon-token> or <EOF-token>, + // consume a component value and append it to the temporary list. + while (!is_one_of(peek_token().type, ';', EOF)) + temp.push_back(consume_component_value()); + + css_parser parser(temp); + // Consume a declaration from the temporary list. + auto decl = parser.consume_declaration(); + // If anything was returned, append it to decls. + if (decl) decls.push_back(decl); + break; + } + case '&': { + // Reconsume the current input token. Consume a qualified rule. If anything was returned, append it to rules. + m_index--; + auto rule = consume_qualified_rule(); + if (rule) rules.push_back(rule); + break; + } + default: + // This is a parse error. Reconsume the current input token. As long as the next input token is + // anything other than a <;> or <EOF>, consume a component value and throw away the returned value. + css_parse_error("unexpected token in a style block"); + m_index--; + while (!is_one_of(peek_token().type, ';', EOF)) + consume_component_value(); + break; + } + } +} + + +// https://www.w3.org/TR/css-syntax-3/#parse-comma-separated-list-of-component-values +// Note: result is never empty. If input is empty result is {{}}. +vector<css_token_vector> parse_comma_separated_list(const css_token_vector& tokens) +{ + vector<css_token_vector> result; + + css_token_vector list; + for (auto& tok : tokens) + { + if (tok.type == ',') // Note: EOF token is not stored in arrays + { + result.push_back(list); + list.clear(); + continue; + } + list.push_back(tok); + } + result.push_back(list); + + return result; +} + +// https://drafts.csswg.org/css-syntax-3/#typedef-any-value +// assumes that tokens have been componentized +bool is_any_value(const css_token_vector& tokens) +{ + if (tokens.empty()) return false; + for (auto& tok : tokens) + { + if (is_one_of(tok.type, BAD_STRING, BAD_URL, ')', ']', '}')) + return false; + else if (tok.is_component_value() && !is_any_value(tok.value)) + return false; + } + return true; +} + +// https://drafts.csswg.org/css-syntax-3/#typedef-declaration-value +// assumes that tokens have been componentized +bool is_declaration_value(const css_token_vector& tokens, int index) +{ + if (index >= (int)tokens.size()) return false; + for (int i = index; i < (int)tokens.size(); i++) + { + auto& tok = tokens[i]; + if (is_one_of(tok.type, BAD_STRING, BAD_URL, ')', ']', '}', ';', '!')) + return false; + // Note: ';' '!' inside component values are allowed, so using is_any_value here. + else if (tok.is_component_value() && !is_any_value(tok.value)) + return false; + } + return true; +} + +// Note: it is possible to have several whitespace tokens in a row: " /**/ /**/ " +bool skip_whitespace(const css_token_vector& tokens, int& index) +{ + int start = index; + while (at(tokens, index).type == ' ') index++; + return index != start; +} + +} // namespace litehtml diff --git a/src/css_properties.cpp b/src/css_properties.cpp index a3b458ebf..6014babb3 100644 --- a/src/css_properties.cpp +++ b/src/css_properties.cpp @@ -1,27 +1,30 @@ #include "html.h" #include "css_properties.h" #include <cmath> +#include "document.h" +#include "html_tag.h" +#include "document_container.h" #define offset(member) ((uint_ptr)&this->member - (uint_ptr)this) +//#define offset(func) [](const css_properties& css) { return css.func; } -void litehtml::css_properties::compute(const element* el, const document::ptr& doc) +void litehtml::css_properties::compute(const html_tag* el, const document::ptr& doc) { - compute_font(el, doc); - int font_size = get_font_size(); - m_color = el->get_color_property(_color_, true, web_color::black, offset(m_color)); - - m_el_position = (element_position) el->get_enum_property( _position_, false, element_position_static, offset(m_el_position)); - m_display = (style_display) el->get_enum_property( _display_, false, display_inline, offset(m_display)); - m_visibility = (visibility) el->get_enum_property( _visibility_, true, visibility_visible, offset(m_visibility)); - m_float = (element_float) el->get_enum_property( _float_, false, float_none, offset(m_float)); - m_clear = (element_clear) el->get_enum_property( _clear_, false, clear_none, offset(m_clear)); - m_box_sizing = (box_sizing) el->get_enum_property( _box_sizing_, false, box_sizing_content_box, offset(m_box_sizing)); - m_overflow = (overflow) el->get_enum_property( _overflow_, false, overflow_visible, offset(m_overflow)); - m_text_align = (text_align) el->get_enum_property( _text_align_, true, text_align_left, offset(m_text_align)); - m_vertical_align = (vertical_align) el->get_enum_property( _vertical_align_, false, va_baseline, offset(m_vertical_align)); - m_text_transform = (text_transform) el->get_enum_property( _text_transform_, true, text_transform_none, offset(m_text_transform)); - m_white_space = (white_space) el->get_enum_property( _white_space_, true, white_space_normal, offset(m_white_space)); - m_caption_side = (caption_side) el->get_enum_property( _caption_side_, true, caption_side_top, offset(m_caption_side)); + m_color = el->get_property<web_color>(_color_, true, web_color::black, offset(m_color)); + + m_el_position = (element_position) el->get_property<int>( _position_, false, element_position_static, offset(m_el_position)); + m_display = (style_display) el->get_property<int>( _display_, false, display_inline, offset(m_display)); + m_visibility = (visibility) el->get_property<int>( _visibility_, true, visibility_visible, offset(m_visibility)); + m_float = (element_float) el->get_property<int>( _float_, false, float_none, offset(m_float)); + m_clear = (element_clear) el->get_property<int>( _clear_, false, clear_none, offset(m_clear)); + m_appearance = (appearance) el->get_property<int>( _appearance_, false, appearance_none, offset(m_appearance)); + m_box_sizing = (box_sizing) el->get_property<int>( _box_sizing_, false, box_sizing_content_box, offset(m_box_sizing)); + m_overflow = (overflow) el->get_property<int>( _overflow_, false, overflow_visible, offset(m_overflow)); + m_text_align = (text_align) el->get_property<int>( _text_align_, true, text_align_left, offset(m_text_align)); + m_vertical_align = (vertical_align) el->get_property<int>( _vertical_align_, false, va_baseline, offset(m_vertical_align)); + m_text_transform = (text_transform) el->get_property<int>( _text_transform_, true, text_transform_none, offset(m_text_transform)); + m_white_space = (white_space) el->get_property<int>( _white_space_, true, white_space_normal, offset(m_white_space)); + m_caption_side = (caption_side) el->get_property<int>( _caption_side_, true, caption_side_top, offset(m_caption_side)); // https://www.w3.org/TR/CSS22/visuren.html#dis-pos-flo if (m_display == display_none) @@ -97,65 +100,71 @@ void litehtml::css_properties::compute(const element* el, const document::ptr& d { m_display = display_block; } + } else if(el->is_replaced() && m_display == display_inline) + { + m_display = display_inline_block; } } // 5. Otherwise, the remaining 'display' property values apply as specified. + compute_font(el, doc); + int font_size = get_font_size(); + const css_length _auto = css_length::predef_value(0); const css_length none = _auto, normal = _auto; - m_css_width = el->get_length_property(_width_, false, _auto, offset(m_css_width)); - m_css_height = el->get_length_property(_height_, false, _auto, offset(m_css_height)); + m_css_width = el->get_property<css_length>(_width_, false, _auto, offset(m_css_width)); + m_css_height = el->get_property<css_length>(_height_, false, _auto, offset(m_css_height)); - m_css_min_width = el->get_length_property(_min_width_, false, _auto, offset(m_css_min_width)); - m_css_min_height = el->get_length_property(_min_height_, false, _auto, offset(m_css_min_height)); + m_css_min_width = el->get_property<css_length>(_min_width_, false, _auto, offset(m_css_min_width)); + m_css_min_height = el->get_property<css_length>(_min_height_, false, _auto, offset(m_css_min_height)); - m_css_max_width = el->get_length_property(_max_width_, false, none, offset(m_css_max_width)); - m_css_max_height = el->get_length_property(_max_height_, false, none, offset(m_css_max_height)); + m_css_max_width = el->get_property<css_length>(_max_width_, false, none, offset(m_css_max_width)); + m_css_max_height = el->get_property<css_length>(_max_height_, false, none, offset(m_css_max_height)); - doc->cvt_units(m_css_width, font_size); - doc->cvt_units(m_css_height, font_size); + doc->cvt_units(m_css_width, m_font_metrics, 0); + doc->cvt_units(m_css_height, m_font_metrics, 0); - doc->cvt_units(m_css_min_width, font_size); - doc->cvt_units(m_css_min_height, font_size); + doc->cvt_units(m_css_min_width, m_font_metrics, 0); + doc->cvt_units(m_css_min_height, m_font_metrics, 0); - doc->cvt_units(m_css_max_width, font_size); - doc->cvt_units(m_css_max_height, font_size); + doc->cvt_units(m_css_max_width, m_font_metrics, 0); + doc->cvt_units(m_css_max_height, m_font_metrics, 0); - m_css_margins.left = el->get_length_property(_margin_left_, false, 0, offset(m_css_margins.left)); - m_css_margins.right = el->get_length_property(_margin_right_, false, 0, offset(m_css_margins.right)); - m_css_margins.top = el->get_length_property(_margin_top_, false, 0, offset(m_css_margins.top)); - m_css_margins.bottom = el->get_length_property(_margin_bottom_, false, 0, offset(m_css_margins.bottom)); + m_css_margins.left = el->get_property<css_length>(_margin_left_, false, 0, offset(m_css_margins.left)); + m_css_margins.right = el->get_property<css_length>(_margin_right_, false, 0, offset(m_css_margins.right)); + m_css_margins.top = el->get_property<css_length>(_margin_top_, false, 0, offset(m_css_margins.top)); + m_css_margins.bottom = el->get_property<css_length>(_margin_bottom_, false, 0, offset(m_css_margins.bottom)); - doc->cvt_units(m_css_margins.left, font_size); - doc->cvt_units(m_css_margins.right, font_size); - doc->cvt_units(m_css_margins.top, font_size); - doc->cvt_units(m_css_margins.bottom, font_size); + doc->cvt_units(m_css_margins.left, m_font_metrics, 0); + doc->cvt_units(m_css_margins.right, m_font_metrics, 0); + doc->cvt_units(m_css_margins.top, m_font_metrics, 0); + doc->cvt_units(m_css_margins.bottom, m_font_metrics, 0); - m_css_padding.left = el->get_length_property(_padding_left_, false, 0, offset(m_css_padding.left)); - m_css_padding.right = el->get_length_property(_padding_right_, false, 0, offset(m_css_padding.right)); - m_css_padding.top = el->get_length_property(_padding_top_, false, 0, offset(m_css_padding.top)); - m_css_padding.bottom = el->get_length_property(_padding_bottom_, false, 0, offset(m_css_padding.bottom)); + m_css_padding.left = el->get_property<css_length>(_padding_left_, false, 0, offset(m_css_padding.left)); + m_css_padding.right = el->get_property<css_length>(_padding_right_, false, 0, offset(m_css_padding.right)); + m_css_padding.top = el->get_property<css_length>(_padding_top_, false, 0, offset(m_css_padding.top)); + m_css_padding.bottom = el->get_property<css_length>(_padding_bottom_, false, 0, offset(m_css_padding.bottom)); - doc->cvt_units(m_css_padding.left, font_size); - doc->cvt_units(m_css_padding.right, font_size); - doc->cvt_units(m_css_padding.top, font_size); - doc->cvt_units(m_css_padding.bottom, font_size); + doc->cvt_units(m_css_padding.left, m_font_metrics, 0); + doc->cvt_units(m_css_padding.right, m_font_metrics, 0); + doc->cvt_units(m_css_padding.top, m_font_metrics, 0); + doc->cvt_units(m_css_padding.bottom, m_font_metrics, 0); - m_css_borders.left.color = el->get_color_property(_border_left_color_, false, m_color, offset(m_css_borders.left.color)); - m_css_borders.right.color = el->get_color_property(_border_right_color_, false, m_color, offset(m_css_borders.right.color)); - m_css_borders.top.color = el->get_color_property(_border_top_color_, false, m_color, offset(m_css_borders.top.color)); - m_css_borders.bottom.color = el->get_color_property(_border_bottom_color_, false, m_color, offset(m_css_borders.bottom.color)); + m_css_borders.left.color = get_color_property(el, _border_left_color_, false, m_color, offset(m_css_borders.left.color)); + m_css_borders.right.color = get_color_property(el, _border_right_color_, false, m_color, offset(m_css_borders.right.color)); + m_css_borders.top.color = get_color_property(el, _border_top_color_, false, m_color, offset(m_css_borders.top.color)); + m_css_borders.bottom.color = get_color_property(el, _border_bottom_color_, false, m_color, offset(m_css_borders.bottom.color)); - m_css_borders.left.style = (border_style) el->get_enum_property(_border_left_style_, false, border_style_none, offset(m_css_borders.left.style)); - m_css_borders.right.style = (border_style) el->get_enum_property(_border_right_style_, false, border_style_none, offset(m_css_borders.right.style)); - m_css_borders.top.style = (border_style) el->get_enum_property(_border_top_style_, false, border_style_none, offset(m_css_borders.top.style)); - m_css_borders.bottom.style = (border_style) el->get_enum_property(_border_bottom_style_, false, border_style_none, offset(m_css_borders.bottom.style)); + m_css_borders.left.style = (border_style) el->get_property<int>(_border_left_style_, false, border_style_none, offset(m_css_borders.left.style)); + m_css_borders.right.style = (border_style) el->get_property<int>(_border_right_style_, false, border_style_none, offset(m_css_borders.right.style)); + m_css_borders.top.style = (border_style) el->get_property<int>(_border_top_style_, false, border_style_none, offset(m_css_borders.top.style)); + m_css_borders.bottom.style = (border_style) el->get_property<int>(_border_bottom_style_, false, border_style_none, offset(m_css_borders.bottom.style)); - m_css_borders.left.width = el->get_length_property(_border_left_width_, false, border_width_medium_value, offset(m_css_borders.left.width)); - m_css_borders.right.width = el->get_length_property(_border_right_width_, false, border_width_medium_value, offset(m_css_borders.right.width)); - m_css_borders.top.width = el->get_length_property(_border_top_width_, false, border_width_medium_value, offset(m_css_borders.top.width)); - m_css_borders.bottom.width = el->get_length_property(_border_bottom_width_, false, border_width_medium_value, offset(m_css_borders.bottom.width)); + m_css_borders.left.width = el->get_property<css_length>(_border_left_width_, false, border_width_medium_value, offset(m_css_borders.left.width)); + m_css_borders.right.width = el->get_property<css_length>(_border_right_width_, false, border_width_medium_value, offset(m_css_borders.right.width)); + m_css_borders.top.width = el->get_property<css_length>(_border_top_width_, false, border_width_medium_value, offset(m_css_borders.top.width)); + m_css_borders.bottom.width = el->get_property<css_length>(_border_bottom_width_, false, border_width_medium_value, offset(m_css_borders.bottom.width)); if (m_css_borders.left.style == border_style_none || m_css_borders.left.style == border_style_hidden) m_css_borders.left.width = 0; @@ -166,86 +175,94 @@ void litehtml::css_properties::compute(const element* el, const document::ptr& d if (m_css_borders.bottom.style == border_style_none || m_css_borders.bottom.style == border_style_hidden) m_css_borders.bottom.width = 0; - doc->cvt_units(m_css_borders.left.width, font_size); - doc->cvt_units(m_css_borders.right.width, font_size); - doc->cvt_units(m_css_borders.top.width, font_size); - doc->cvt_units(m_css_borders.bottom.width, font_size); + doc->cvt_units(m_css_borders.left.width, m_font_metrics, 0); + doc->cvt_units(m_css_borders.right.width, m_font_metrics, 0); + doc->cvt_units(m_css_borders.top.width, m_font_metrics, 0); + doc->cvt_units(m_css_borders.bottom.width, m_font_metrics, 0); - m_css_borders.radius.top_left_x = el->get_length_property(_border_top_left_radius_x_, false, 0, offset(m_css_borders.radius.top_left_x)); - m_css_borders.radius.top_left_y = el->get_length_property(_border_top_left_radius_y_, false, 0, offset(m_css_borders.radius.top_left_y)); + m_css_borders.radius.top_left_x = el->get_property<css_length>(_border_top_left_radius_x_, false, 0, offset(m_css_borders.radius.top_left_x)); + m_css_borders.radius.top_left_y = el->get_property<css_length>(_border_top_left_radius_y_, false, 0, offset(m_css_borders.radius.top_left_y)); - m_css_borders.radius.top_right_x = el->get_length_property(_border_top_right_radius_x_, false, 0, offset(m_css_borders.radius.top_right_x)); - m_css_borders.radius.top_right_y = el->get_length_property(_border_top_right_radius_y_, false, 0, offset(m_css_borders.radius.top_right_y)); + m_css_borders.radius.top_right_x = el->get_property<css_length>(_border_top_right_radius_x_, false, 0, offset(m_css_borders.radius.top_right_x)); + m_css_borders.radius.top_right_y = el->get_property<css_length>(_border_top_right_radius_y_, false, 0, offset(m_css_borders.radius.top_right_y)); - m_css_borders.radius.bottom_left_x = el->get_length_property(_border_bottom_left_radius_x_, false, 0, offset(m_css_borders.radius.bottom_left_x)); - m_css_borders.radius.bottom_left_y = el->get_length_property(_border_bottom_left_radius_y_, false, 0, offset(m_css_borders.radius.bottom_left_y)); + m_css_borders.radius.bottom_left_x = el->get_property<css_length>(_border_bottom_left_radius_x_, false, 0, offset(m_css_borders.radius.bottom_left_x)); + m_css_borders.radius.bottom_left_y = el->get_property<css_length>(_border_bottom_left_radius_y_, false, 0, offset(m_css_borders.radius.bottom_left_y)); - m_css_borders.radius.bottom_right_x = el->get_length_property(_border_bottom_right_radius_x_, false, 0, offset(m_css_borders.radius.bottom_right_x)); - m_css_borders.radius.bottom_right_y = el->get_length_property(_border_bottom_right_radius_y_, false, 0, offset(m_css_borders.radius.bottom_right_y)); + m_css_borders.radius.bottom_right_x = el->get_property<css_length>(_border_bottom_right_radius_x_, false, 0, offset(m_css_borders.radius.bottom_right_x)); + m_css_borders.radius.bottom_right_y = el->get_property<css_length>(_border_bottom_right_radius_y_, false, 0, offset(m_css_borders.radius.bottom_right_y)); - doc->cvt_units( m_css_borders.radius.top_left_x, font_size); - doc->cvt_units( m_css_borders.radius.top_left_y, font_size); - doc->cvt_units( m_css_borders.radius.top_right_x, font_size); - doc->cvt_units( m_css_borders.radius.top_right_y, font_size); - doc->cvt_units( m_css_borders.radius.bottom_left_x, font_size); - doc->cvt_units( m_css_borders.radius.bottom_left_y, font_size); - doc->cvt_units( m_css_borders.radius.bottom_right_x, font_size); - doc->cvt_units( m_css_borders.radius.bottom_right_y, font_size); + doc->cvt_units( m_css_borders.radius.top_left_x, m_font_metrics, 0); + doc->cvt_units( m_css_borders.radius.top_left_y, m_font_metrics, 0); + doc->cvt_units( m_css_borders.radius.top_right_x, m_font_metrics, 0); + doc->cvt_units( m_css_borders.radius.top_right_y, m_font_metrics, 0); + doc->cvt_units( m_css_borders.radius.bottom_left_x, m_font_metrics, 0); + doc->cvt_units( m_css_borders.radius.bottom_left_y, m_font_metrics, 0); + doc->cvt_units( m_css_borders.radius.bottom_right_x, m_font_metrics, 0); + doc->cvt_units( m_css_borders.radius.bottom_right_y, m_font_metrics, 0); - m_border_collapse = (border_collapse) el->get_enum_property(_border_collapse_, true, border_collapse_separate, offset(m_border_collapse)); + m_border_collapse = (border_collapse) el->get_property<int>(_border_collapse_, true, border_collapse_separate, offset(m_border_collapse)); - m_css_border_spacing_x = el->get_length_property(__litehtml_border_spacing_x_, true, 0, offset(m_css_border_spacing_x)); - m_css_border_spacing_y = el->get_length_property(__litehtml_border_spacing_y_, true, 0, offset(m_css_border_spacing_y)); + m_css_border_spacing_x = el->get_property<css_length>(__litehtml_border_spacing_x_, true, 0, offset(m_css_border_spacing_x)); + m_css_border_spacing_y = el->get_property<css_length>(__litehtml_border_spacing_y_, true, 0, offset(m_css_border_spacing_y)); - doc->cvt_units(m_css_border_spacing_x, font_size); - doc->cvt_units(m_css_border_spacing_y, font_size); + doc->cvt_units(m_css_border_spacing_x, m_font_metrics, 0); + doc->cvt_units(m_css_border_spacing_y, m_font_metrics, 0); - m_css_offsets.left = el->get_length_property(_left_, false, _auto, offset(m_css_offsets.left)); - m_css_offsets.right = el->get_length_property(_right_, false, _auto, offset(m_css_offsets.right)); - m_css_offsets.top = el->get_length_property(_top_, false, _auto, offset(m_css_offsets.top)); - m_css_offsets.bottom = el->get_length_property(_bottom_,false, _auto, offset(m_css_offsets.bottom)); + m_css_offsets.left = el->get_property<css_length>(_left_, false, _auto, offset(m_css_offsets.left)); + m_css_offsets.right = el->get_property<css_length>(_right_, false, _auto, offset(m_css_offsets.right)); + m_css_offsets.top = el->get_property<css_length>(_top_, false, _auto, offset(m_css_offsets.top)); + m_css_offsets.bottom = el->get_property<css_length>(_bottom_,false, _auto, offset(m_css_offsets.bottom)); - doc->cvt_units(m_css_offsets.left, font_size); - doc->cvt_units(m_css_offsets.right, font_size); - doc->cvt_units(m_css_offsets.top, font_size); - doc->cvt_units(m_css_offsets.bottom, font_size); + doc->cvt_units(m_css_offsets.left, m_font_metrics, 0); + doc->cvt_units(m_css_offsets.right, m_font_metrics, 0); + doc->cvt_units(m_css_offsets.top, m_font_metrics, 0); + doc->cvt_units(m_css_offsets.bottom, m_font_metrics, 0); - m_z_index = el->get_length_property(_z_index_, false, _auto, offset(m_z_index)); - m_content = el->get_string_property(_content_, false, "", offset(m_content)); - m_cursor = el->get_string_property(_cursor_, true, "auto", offset(m_cursor)); + m_z_index = el->get_property<css_length>(_z_index_, false, _auto, offset(m_z_index)); + m_content = el->get_property<string>(_content_, false, "", offset(m_content)); + m_cursor = el->get_property<string>(_cursor_, true, "auto", offset(m_cursor)); - m_css_text_indent = el->get_length_property(_text_indent_, true, 0, offset(m_css_text_indent)); - doc->cvt_units(m_css_text_indent, font_size); + m_css_text_indent = el->get_property<css_length>(_text_indent_, true, 0, offset(m_css_text_indent)); + doc->cvt_units(m_css_text_indent, m_font_metrics, 0); - m_css_line_height = el->get_length_property(_line_height_, true, normal, offset(m_css_line_height)); - if(m_css_line_height.is_predefined()) + m_line_height.css_value = el->get_property<css_length>(_line_height_, true, normal, offset(m_line_height.css_value)); + if(m_line_height.css_value.is_predefined()) { - m_line_height = m_font_metrics.height; - } else if(m_css_line_height.units() == css_units_none) + m_line_height.computed_value = m_font_metrics.height; + } else if(m_line_height.css_value.units() == css_units_none) { - m_line_height = (int) std::nearbyint(m_css_line_height.val() * font_size); + m_line_height.computed_value = (int) std::nearbyint(m_line_height.css_value.val() * font_size); } else { - m_line_height = doc->to_pixels(m_css_line_height, font_size, font_size); - m_css_line_height = (float) m_line_height; + m_line_height.computed_value = doc->to_pixels(m_line_height.css_value, m_font_metrics, m_font_metrics.font_size); + m_line_height.css_value = (float) m_line_height.computed_value; } - m_list_style_type = (list_style_type) el->get_enum_property(_list_style_type_, true, list_style_type_disc, offset(m_list_style_type)); - m_list_style_position = (list_style_position) el->get_enum_property(_list_style_position_, true, list_style_position_outside, offset(m_list_style_position)); + m_list_style_type = (list_style_type) el->get_property<int>(_list_style_type_, true, list_style_type_disc, offset(m_list_style_type)); + m_list_style_position = (list_style_position) el->get_property<int>(_list_style_position_, true, list_style_position_outside, offset(m_list_style_position)); - m_list_style_image = el->get_string_property(_list_style_image_, true, "", offset(m_list_style_image)); + m_list_style_image = el->get_property<string>(_list_style_image_, true, "", offset(m_list_style_image)); if (!m_list_style_image.empty()) { - m_list_style_image_baseurl = el->get_string_property(_list_style_image_baseurl_, true, "", offset(m_list_style_image_baseurl)); + m_list_style_image_baseurl = el->get_property<string>(_list_style_image_baseurl_, true, "", offset(m_list_style_image_baseurl)); doc->container()->load_image(m_list_style_image.c_str(), m_list_style_image_baseurl.c_str(), true); } - m_order = el->get_int_property(_order_, false, 0, offset(m_order)); + m_order = el->get_property<int>(_order_, false, 0, offset(m_order)); compute_background(el, doc); compute_flex(el, doc); } +// used for all color properties except `color` (color:currentcolor is converted to color:inherit during parsing) +litehtml::web_color litehtml::css_properties::get_color_property(const html_tag* el, string_id name, bool inherited, web_color default_value, uint_ptr member_offset) const +{ + web_color color = el->get_property<web_color>(name, inherited, default_value, member_offset); + if (color.is_current_color) color = m_color; + return color; +} + static const int font_size_table[8][7] = { { 9, 9, 9, 9, 11, 14, 18}, @@ -258,10 +275,10 @@ static const int font_size_table[8][7] = { 9, 10, 13, 16, 18, 24, 32} }; -void litehtml::css_properties::compute_font(const element* el, const document::ptr& doc) +void litehtml::css_properties::compute_font(const html_tag* el, const document::ptr& doc) { // initialize font size - css_length sz = el->get_length_property(_font_size_, true, css_length::predef_value(font_size_medium), offset(m_font_size)); + css_length sz = el->get_property<css_length>(_font_size_, true, css_length::predef_value(font_size_medium), offset(m_font_size)); int parent_sz = 0; int doc_font_size = doc->container()->get_default_font_size(); @@ -273,7 +290,7 @@ void litehtml::css_properties::compute_font(const element* el, const document::p { parent_sz = doc_font_size; } - + int font_size = parent_sz; if(sz.is_predefined()) @@ -334,87 +351,176 @@ void litehtml::css_properties::compute_font(const element* el, const document::p font_size = sz.calc_percent(parent_sz); } else { - font_size = doc->to_pixels(sz, parent_sz); + font_metrics fm; + fm.x_height = fm.font_size = parent_sz; + font_size = doc->to_pixels(sz, fm, 0); } } - + m_font_size = (float)font_size; // initialize font - m_font_family = el->get_string_property(_font_family_, true, doc->container()->get_default_font_name(), offset(m_font_family)); - m_font_weight = (font_weight) el->get_enum_property( _font_weight_, true, font_weight_normal, offset(m_font_weight)); - m_font_style = (font_style) el->get_enum_property( _font_style_, true, font_style_normal, offset(m_font_style)); - m_text_decoration = el->get_string_property(_text_decoration_, true, "none", offset(m_text_decoration)); - - m_font = doc->get_font( - m_font_family.c_str(), - font_size, - index_value(m_font_weight, font_weight_strings).c_str(), - index_value(m_font_style, font_style_strings).c_str(), - m_text_decoration.c_str(), - &m_font_metrics); + m_font_family = el->get_property<string>( _font_family_, true, doc->container()->get_default_font_name(), offset(m_font_family)); + m_font_weight = el->get_property<css_length>(_font_weight_, true, css_length::predef_value(font_weight_normal), offset(m_font_weight)); + m_font_style = (font_style) el->get_property<int>( _font_style_, true, font_style_normal, offset(m_font_style)); + bool propagate_decoration = !is_one_of(m_display, display_inline_block, display_inline_table, display_inline_flex) && + m_float == float_none && !is_one_of(m_el_position, element_position_absolute, element_position_fixed); + + m_text_decoration_line = el->get_property<int>(_text_decoration_line_, propagate_decoration, text_decoration_line_none, offset(m_text_decoration_line)); + + // Merge parent text decoration with child text decoration + if (propagate_decoration && el->parent()) + { + m_text_decoration_line |= el->parent()->css().get_text_decoration_line(); + } + + if(m_text_decoration_line) + { + m_text_decoration_thickness = el->get_property<css_length>(_text_decoration_thickness_, propagate_decoration, css_length::predef_value(text_decoration_thickness_auto), offset(m_text_decoration_thickness)); + m_text_decoration_style = (text_decoration_style) el->get_property<int>(_text_decoration_style_, propagate_decoration, text_decoration_style_solid, offset(m_text_decoration_style)); + m_text_decoration_color = get_color_property(el, _text_decoration_color_, propagate_decoration, web_color::current_color, offset(m_text_decoration_color)); + } else + { + m_text_decoration_thickness = css_length::predef_value(text_decoration_thickness_auto); + m_text_decoration_color = web_color::current_color; + } + + // text-emphasis + m_text_emphasis_style = el->get_property<string>(_text_emphasis_style_, true, "", offset(m_text_emphasis_style)); + m_text_emphasis_position = el->get_property<int>(_text_emphasis_position_, true, text_emphasis_position_over, offset(m_text_emphasis_position)); + m_text_emphasis_color = get_color_property(el, _text_emphasis_color_, true, web_color::current_color, offset(m_text_emphasis_color)); + + if(el->parent()) + { + if(m_text_emphasis_style.empty() || m_text_emphasis_style == "initial" || m_text_emphasis_style == "unset") + { + m_text_emphasis_style = el->parent()->css().get_text_emphasis_style(); + } + if(m_text_emphasis_color == web_color::current_color) + { + m_text_emphasis_color = el->parent()->css().get_text_emphasis_color(); + } + m_text_emphasis_position |= el->parent()->css().get_text_emphasis_position(); + } + + if(m_font_weight.is_predefined()) + { + switch(m_font_weight.predef()) + { + case font_weight_bold: + m_font_weight = 700; + break; + case font_weight_bolder: + { + const int inherited = (int) el->parent()->css().m_font_weight.val(); + if(inherited < 400) m_font_weight = 400; + else if(inherited >= 400 && inherited < 600) m_font_weight = 700; + else m_font_weight = 900; + } + break; + case font_weight_lighter: + { + const int inherited = (int) el->parent()->css().m_font_weight.val(); + if(inherited < 600) m_font_weight = 100; + else if(inherited >= 600 && inherited < 800) m_font_weight = 400; + else m_font_weight = 700; + } + break; + default: + m_font_weight = 400; + break; + } + } + + font_description descr; + descr.family = m_font_family; + descr.size = font_size; + descr.style = m_font_style; + descr.weight = (int) m_font_weight.val(); + descr.decoration_line = m_text_decoration_line; + descr.decoration_thickness = m_text_decoration_thickness; + descr.decoration_style = m_text_decoration_style; + descr.decoration_color = m_text_decoration_color; + descr.emphasis_style = m_text_emphasis_style; + descr.emphasis_color = m_text_emphasis_color; + descr.emphasis_position = m_text_emphasis_position; + + m_font = doc->get_font(descr, &m_font_metrics); } -void litehtml::css_properties::compute_background(const element* el, const document::ptr& doc) +void litehtml::css_properties::compute_background(const html_tag* el, const document::ptr& doc) { - int font_size = get_font_size(); - - m_bg.m_color = el->get_color_property(_background_color_, false, web_color::transparent, offset(m_bg.m_color)); + m_bg.m_color = get_color_property(el, _background_color_, false, web_color::transparent, offset(m_bg.m_color)); const css_size auto_auto(css_length::predef_value(background_size_auto), css_length::predef_value(background_size_auto)); - m_bg.m_position_x = el->get_length_vector_property(_background_position_x_, false, { css_length(0, css_units_percentage) }, offset(m_bg.m_position_x)); - m_bg.m_position_y = el->get_length_vector_property(_background_position_y_, false, { css_length(0, css_units_percentage) }, offset(m_bg.m_position_y)); - m_bg.m_size = el->get_size_vector_property (_background_size_, false, { auto_auto }, offset(m_bg.m_size)); + m_bg.m_position_x = el->get_property<length_vector>(_background_position_x_, false, { css_length(0, css_units_percentage) }, offset(m_bg.m_position_x)); + m_bg.m_position_y = el->get_property<length_vector>(_background_position_y_, false, { css_length(0, css_units_percentage) }, offset(m_bg.m_position_y)); + m_bg.m_size = el->get_property<size_vector> (_background_size_, false, { auto_auto }, offset(m_bg.m_size)); - for (auto& x : m_bg.m_position_x) doc->cvt_units(x, font_size); - for (auto& y : m_bg.m_position_y) doc->cvt_units(y, font_size); + for (auto& x : m_bg.m_position_x) doc->cvt_units(x, m_font_metrics, 0); + for (auto& y : m_bg.m_position_y) doc->cvt_units(y, m_font_metrics, 0); for (auto& size : m_bg.m_size) { - doc->cvt_units(size.width, font_size); - doc->cvt_units(size.height, font_size); + doc->cvt_units(size.width, m_font_metrics, 0); + doc->cvt_units(size.height, m_font_metrics, 0); } - m_bg.m_attachment = el->get_int_vector_property(_background_attachment_, false, { background_attachment_scroll }, offset(m_bg.m_attachment)); - m_bg.m_repeat = el->get_int_vector_property(_background_repeat_, false, { background_repeat_repeat }, offset(m_bg.m_repeat)); - m_bg.m_clip = el->get_int_vector_property(_background_clip_, false, { background_box_border }, offset(m_bg.m_clip)); - m_bg.m_origin = el->get_int_vector_property(_background_origin_, false, { background_box_padding }, offset(m_bg.m_origin)); + m_bg.m_attachment = el->get_property<int_vector>(_background_attachment_, false, { background_attachment_scroll }, offset(m_bg.m_attachment)); + m_bg.m_repeat = el->get_property<int_vector>(_background_repeat_, false, { background_repeat_repeat }, offset(m_bg.m_repeat)); + m_bg.m_clip = el->get_property<int_vector>(_background_clip_, false, { background_box_border }, offset(m_bg.m_clip)); + m_bg.m_origin = el->get_property<int_vector>(_background_origin_, false, { background_box_padding }, offset(m_bg.m_origin)); - m_bg.m_image = el->get_string_vector_property(_background_image_, false, {""}, offset(m_bg.m_image)); - m_bg.m_baseurl = el->get_string_property(_background_image_baseurl_, false, "", offset(m_bg.m_baseurl)); + m_bg.m_image = el->get_property<vector<image>>(_background_image_, false, {{}}, offset(m_bg.m_image)); + m_bg.m_baseurl = el->get_property<string>(_background_image_baseurl_, false, "", offset(m_bg.m_baseurl)); - for (const auto& image : m_bg.m_image) + for (auto& image : m_bg.m_image) { - if (!image.empty()) + switch (image.type) { - doc->container()->load_image(image.c_str(), m_bg.m_baseurl.c_str(), true); + + case image::type_none: + break; + case image::type_url: + if (!image.url.empty()) + { + doc->container()->load_image(image.url.c_str(), m_bg.m_baseurl.c_str(), true); + } + break; + case image::type_gradient: + for(auto& item : image.m_gradient.m_colors) + { + if (item.length) + doc->cvt_units(*item.length, m_font_metrics, 0); + } + break; } } } -void litehtml::css_properties::compute_flex(const element* el, const document::ptr& doc) +void litehtml::css_properties::compute_flex(const html_tag* el, const document::ptr& doc) { if (m_display == display_flex || m_display == display_inline_flex) { - m_flex_direction = (flex_direction) el->get_enum_property(_flex_direction_, false, flex_direction_row, offset(m_flex_direction)); - m_flex_wrap = (flex_wrap) el->get_enum_property(_flex_wrap_, false, flex_wrap_nowrap, offset(m_flex_wrap)); + m_flex_direction = (flex_direction) el->get_property<int>(_flex_direction_, false, flex_direction_row, offset(m_flex_direction)); + m_flex_wrap = (flex_wrap) el->get_property<int>(_flex_wrap_, false, flex_wrap_nowrap, offset(m_flex_wrap)); - m_flex_justify_content = (flex_justify_content) el->get_enum_property(_justify_content_, false, flex_justify_content_flex_start, offset(m_flex_justify_content)); - m_flex_align_items = (flex_align_items) el->get_enum_property(_align_items_, false, flex_align_items_flex_normal, offset(m_flex_align_items)); - m_flex_align_content = (flex_align_content) el->get_enum_property(_align_content_, false, flex_align_content_stretch, offset(m_flex_align_content)); + m_flex_justify_content = (flex_justify_content) el->get_property<int>(_justify_content_, false, flex_justify_content_flex_start, offset(m_flex_justify_content)); + m_flex_align_items = (flex_align_items) el->get_property<int>(_align_items_, false, flex_align_items_normal, offset(m_flex_align_items)); + m_flex_align_content = (flex_align_content) el->get_property<int>(_align_content_, false, flex_align_content_stretch, offset(m_flex_align_content)); } - m_flex_align_self = (flex_align_items) el->get_enum_property(_align_self_, false, flex_align_items_auto, offset(m_flex_align_self)); + m_flex_align_self = (flex_align_items) el->get_property<int>(_align_self_, false, flex_align_items_auto, offset(m_flex_align_self)); auto parent = el->parent(); if (parent && (parent->css().m_display == display_flex || parent->css().m_display == display_inline_flex)) { - m_flex_grow = el->get_number_property(_flex_grow_, false, 0, offset(m_flex_grow)); - m_flex_shrink = el->get_number_property(_flex_shrink_, false, 1, offset(m_flex_shrink)); - m_flex_basis = el->get_length_property(_flex_basis_, false, css_length::predef_value(flex_basis_auto), offset(m_flex_basis)); + m_flex_grow = el->get_property<float>(_flex_grow_, false, 0, offset(m_flex_grow)); + m_flex_shrink = el->get_property<float>(_flex_shrink_, false, 1, offset(m_flex_shrink)); + m_flex_basis = el->get_property<css_length>(_flex_basis_, false, css_length::predef_value(flex_basis_auto), offset(m_flex_basis)); if(!m_flex_basis.is_predefined() && m_flex_basis.units() == css_units_none && m_flex_basis.val() != 0) { // flex-basis property must contain units m_flex_basis.predef(flex_basis_auto); } - doc->cvt_units(m_flex_basis, get_font_size()); + doc->cvt_units(m_flex_basis, m_font_metrics, 0); if(m_display == display_inline || m_display == display_inline_block) { m_display = display_block; @@ -432,34 +538,35 @@ std::vector<std::tuple<litehtml::string, litehtml::string>> litehtml::css_proper { std::vector<std::tuple<string, string>> ret; - ret.emplace_back(std::make_tuple("display", index_value(m_display, style_display_strings))); - ret.emplace_back(std::make_tuple("el_position", index_value(m_el_position, element_position_strings))); - ret.emplace_back(std::make_tuple("text_align", index_value(m_text_align, text_align_strings))); - ret.emplace_back(std::make_tuple("font_size", m_font_size.to_string())); - ret.emplace_back(std::make_tuple("overflow", index_value(m_overflow, overflow_strings))); - ret.emplace_back(std::make_tuple("white_space", index_value(m_white_space, white_space_strings))); - ret.emplace_back(std::make_tuple("visibility", index_value(m_visibility, visibility_strings))); - ret.emplace_back(std::make_tuple("box_sizing", index_value(m_box_sizing, box_sizing_strings))); - ret.emplace_back(std::make_tuple("z_index", m_z_index.to_string())); - ret.emplace_back(std::make_tuple("vertical_align", index_value(m_vertical_align, vertical_align_strings))); - ret.emplace_back(std::make_tuple("float", index_value(m_float, element_float_strings))); - ret.emplace_back(std::make_tuple("clear", index_value(m_clear, element_clear_strings))); - ret.emplace_back(std::make_tuple("margins", m_css_margins.to_string())); - ret.emplace_back(std::make_tuple("padding", m_css_padding.to_string())); - ret.emplace_back(std::make_tuple("borders", m_css_borders.to_string())); - ret.emplace_back(std::make_tuple("width", m_css_width.to_string())); - ret.emplace_back(std::make_tuple("height", m_css_height.to_string())); - ret.emplace_back(std::make_tuple("min_width", m_css_min_width.to_string())); - ret.emplace_back(std::make_tuple("min_height", m_css_min_width.to_string())); - ret.emplace_back(std::make_tuple("max_width", m_css_max_width.to_string())); - ret.emplace_back(std::make_tuple("max_height", m_css_max_width.to_string())); - ret.emplace_back(std::make_tuple("offsets", m_css_offsets.to_string())); - ret.emplace_back(std::make_tuple("text_indent", m_css_text_indent.to_string())); - ret.emplace_back(std::make_tuple("line_height", std::to_string(m_line_height))); - ret.emplace_back(std::make_tuple("list_style_type", index_value(m_list_style_type, list_style_type_strings))); - ret.emplace_back(std::make_tuple("list_style_position", index_value(m_list_style_position, list_style_position_strings))); - ret.emplace_back(std::make_tuple("border_spacing_x", m_css_border_spacing_x.to_string())); - ret.emplace_back(std::make_tuple("border_spacing_y", m_css_border_spacing_y.to_string())); + ret.emplace_back("display", index_value(m_display, style_display_strings)); + ret.emplace_back("el_position", index_value(m_el_position, element_position_strings)); + ret.emplace_back("text_align", index_value(m_text_align, text_align_strings)); + ret.emplace_back("font_size", m_font_size.to_string()); + ret.emplace_back("overflow", index_value(m_overflow, overflow_strings)); + ret.emplace_back("white_space", index_value(m_white_space, white_space_strings)); + ret.emplace_back("visibility", index_value(m_visibility, visibility_strings)); + ret.emplace_back("appearance", index_value(m_appearance, appearance_strings)); + ret.emplace_back("box_sizing", index_value(m_box_sizing, box_sizing_strings)); + ret.emplace_back("z_index", m_z_index.to_string()); + ret.emplace_back("vertical_align", index_value(m_vertical_align, vertical_align_strings)); + ret.emplace_back("float", index_value(m_float, element_float_strings)); + ret.emplace_back("clear", index_value(m_clear, element_clear_strings)); + ret.emplace_back("margins", m_css_margins.to_string()); + ret.emplace_back("padding", m_css_padding.to_string()); + ret.emplace_back("borders", m_css_borders.to_string()); + ret.emplace_back("width", m_css_width.to_string()); + ret.emplace_back("height", m_css_height.to_string()); + ret.emplace_back("min_width", m_css_min_width.to_string()); + ret.emplace_back("min_height", m_css_min_width.to_string()); + ret.emplace_back("max_width", m_css_max_width.to_string()); + ret.emplace_back("max_height", m_css_max_width.to_string()); + ret.emplace_back("offsets", m_css_offsets.to_string()); + ret.emplace_back("text_indent", m_css_text_indent.to_string()); + ret.emplace_back("line_height", std::to_string(m_line_height.computed_value)); + ret.emplace_back("list_style_type", index_value(m_list_style_type, list_style_type_strings)); + ret.emplace_back("list_style_position", index_value(m_list_style_position, list_style_position_strings)); + ret.emplace_back("border_spacing_x", m_css_border_spacing_x.to_string()); + ret.emplace_back("border_spacing_y", m_css_border_spacing_y.to_string()); return ret; } diff --git a/src/css_selector.cpp b/src/css_selector.cpp index 5843cb1a6..56a11e337 100644 --- a/src/css_selector.cpp +++ b/src/css_selector.cpp @@ -1,321 +1,716 @@ #include "html.h" #include "css_selector.h" +#include "css_parser.h" +#include "internal.h" #include "document.h" +#include <set> -void litehtml::css_element_selector::parse_nth_child_params(const string& param, int& num, int& off) +namespace litehtml { - if (param == "odd") + +void css_selector::calc_specificity() +{ + if(m_right.m_tag != star_id) { - num = 2; - off = 1; + m_specificity.d = 1; } - else if (param == "even") + for(const auto& attr : m_right.m_attrs) { - num = 2; - off = 0; + if(attr.type == select_id) + { + m_specificity.b++; + } else + { + m_specificity.c++; + } } - else + if(m_left) { - string_vector tokens; - split_string(param, tokens, " n", "n"); + m_left->calc_specificity(); + m_specificity += m_left->m_specificity; + } +} - string s_num; - string s_off; +void css_selector::add_media_to_doc( document* doc ) const +{ + if(m_media_query && doc) + { + doc->add_media_list(m_media_query); + } +} - string s_int; - for (const auto& token : tokens) - { - if (token == "n") - { - s_num = s_int; - s_int.clear(); - } - else - { - s_int += token; - } - } - s_off = s_int; +// https://www.w3.org/TR/selectors-4/#type-nmsp +// <ns-prefix> = [ <ident-token> | '*' ]? '|' https://www.w3.org/TR/selectors-4/#typedef-ns-prefix +string parse_ns_prefix(const css_token_vector& tokens, int& index) +{ + const auto& a = at(tokens, index); + const auto& b = at(tokens, index + 1); - num = atoi(s_num.c_str()); - off = atoi(s_off.c_str()); + if (a.ch == '|') + { + index++; + return ""; } + + if ((a.type == IDENT || a.ch == '*') && b.ch == '|') + { + index += 2; + return a.type == IDENT ? a.name : "*"; + } + + return ""; } -void litehtml::css_element_selector::parse( const string& txt ) +struct wq_name { - string::size_type el_end = txt.find_first_of(".#[:"); - string tag = txt.substr(0, el_end); - litehtml::lcase(tag); - if (tag == "") tag = "*"; - m_tag = _id(tag); + string prefix; + string name; +}; - m_attrs.clear(); - while(el_end != string::npos) +// <wq-name> = <ns-prefix>? <ident-token> +// Whitespace is forbidden between any of the components of a <wq-name>. +wq_name parse_wq_name(const css_token_vector& tokens, int& index) +{ + int start = index; + string prefix = parse_ns_prefix(tokens, index); + + auto tok = at(tokens, index); + if (tok.type == IDENT) { - if(txt[el_end] == '.') - { - css_attribute_selector attribute; - - attribute.type = select_class; - string::size_type pos = txt.find_first_of(".#[:", el_end + 1); - string name = txt.substr(el_end + 1, pos - el_end - 1); - litehtml::lcase(name); - attribute.name = _id(name); - m_attrs.push_back(attribute); - el_end = pos; - } else if(txt[el_end] == '#') - { - css_attribute_selector attribute; - - attribute.type = select_id; - string::size_type pos = txt.find_first_of(".#[:", el_end + 1); - string name = txt.substr(el_end + 1, pos - el_end - 1); - litehtml::lcase(name); - attribute.name = _id(name); - m_attrs.push_back(attribute); - el_end = pos; - } else if(txt[el_end] == ':') - { - css_attribute_selector attribute; - - if(txt[el_end + 1] == ':') - { - attribute.type = select_pseudo_element; - string::size_type pos = txt.find_first_of(".#[:", el_end + 2); - string name = txt.substr(el_end + 2, pos - el_end - 2); - litehtml::lcase(name); - attribute.name = _id(name); - m_attrs.push_back(attribute); - el_end = pos; - } else - { - string::size_type pos = txt.find_first_of(".#[:(", el_end + 1); - string name = txt.substr(el_end + 1, pos - el_end - 1); - lcase(name); - attribute.name = _id(name); - if(attribute.name == _after_ || attribute.name == _before_) - { - attribute.type = select_pseudo_element; - } else - { - attribute.type = select_pseudo_class; - } - - string val; - if(pos != string::npos && txt.at(pos) == '(') - { - auto end = find_close_bracket(txt, pos); - val = txt.substr(pos + 1, end - pos - 1); - if (end != string::npos) pos = end + 1; - } - - switch (attribute.name) - { - case _nth_child_: - case _nth_of_type_: - case _nth_last_child_: - case _nth_last_of_type_: - lcase(val); - parse_nth_child_params(val, attribute.a, attribute.b); - break; - case _not_: - attribute.sel = std::make_shared<css_element_selector>(); - attribute.sel->parse(val); - break; - case _lang_: - trim(val); - lcase(val); - attribute.val = val; - break; - } - - m_attrs.push_back(attribute); - el_end = pos; - } - } else if(txt[el_end] == '[') - { - css_attribute_selector attribute; - - string::size_type pos = txt.find_first_of("]~=|$*^", el_end + 1); - string attr = txt.substr(el_end + 1, pos - el_end - 1); - trim(attr); - litehtml::lcase(attr); - if(pos != string::npos) - { - if(txt[pos] == ']') - { - attribute.type = select_exists; - } else if(txt[pos] == '=') - { - attribute.type = select_equal; - pos++; - } else if(txt.substr(pos, 2) == "~=") - { - attribute.type = select_contain_str; - pos += 2; - } else if(txt.substr(pos, 2) == "|=") - { - attribute.type = select_start_str; - pos += 2; - } else if(txt.substr(pos, 2) == "^=") - { - attribute.type = select_start_str; - pos += 2; - } else if(txt.substr(pos, 2) == "$=") - { - attribute.type = select_end_str; - pos += 2; - } else if(txt.substr(pos, 2) == "*=") - { - attribute.type = select_contain_str; - pos += 2; - } else - { - attribute.type = select_exists; - pos += 1; - } - pos = txt.find_first_not_of(" \t", pos); - if(pos != string::npos) - { - if(txt[pos] == '"') - { - string::size_type pos2 = txt.find_first_of('\"', pos + 1); - attribute.val = txt.substr(pos + 1, pos2 == string::npos ? pos2 : (pos2 - pos - 1)); - pos = pos2 == string::npos ? pos2 : (pos2 + 1); - } else if(txt[pos] == '\'') - { - string::size_type pos2 = txt.find_first_of('\'', pos + 1); - attribute.val = txt.substr(pos + 1, pos2 == string::npos ? pos2 : (pos2 - pos - 1)); - pos = pos2 == string::npos ? pos2 : (pos2 + 1); - } else if(txt[pos] == ']') - { - pos ++; - } else - { - string::size_type pos2 = txt.find_first_of(']', pos + 1); - attribute.val = txt.substr(pos, pos2 == string::npos ? pos2 : (pos2 - pos)); - trim(attribute.val); - pos = pos2 == string::npos ? pos2 : (pos2 + 1); - } - } - } else - { - attribute.type = select_exists; - } - attribute.name = _id(attr); - m_attrs.push_back(attribute); - el_end = pos; - } else - { - el_end++; - } - el_end = txt.find_first_of(".#[:", el_end); + index++; + return { prefix, tok.name }; } + // restore index to before <ns-prefix> if failed to parse <ident-token> + index = start; + + // handle situation when a name is erroneously parsed as prefix, eg. [x|=a] + tok = at(tokens, index); + if (tok.type == IDENT) + { + index++; + return { "", tok.name }; + } + + return {}; } +// https://www.w3.org/TR/selectors-4/#typedef-type-selector +// <type-selector> = <wq-name> | <ns-prefix>? '*' +// <wq-name> = <ns-prefix>? <ident-token> +// So: +// <type-selector> = <ns-prefix>? [ <ident-token> | '*' ] +wq_name parse_type_selector(const css_token_vector& tokens, int& index) +{ + int start = index; + string prefix = parse_ns_prefix(tokens, index); -bool litehtml::css_selector::parse( const string& text ) + const auto& tok = at(tokens, index); + if (tok.type == IDENT || tok.ch == '*') + { + index++; + string name = tok.type == IDENT ? tok.name : "*"; + // type selector is always ASCII-case-insensitive for HTML documents, regardless of document mode (quirks/no quirks) + return { lowcase(prefix), lowcase(name) }; + } + // restore index to before <ns-prefix> if failed to parse <ident-token> or '*' + index = start; + return {}; +} + +// <attr-matcher> = [ '~' | '|' | '^' | '$' | '*' ]? '=' +bool parse_attr_matcher(const css_token_vector& tokens, int& index, attr_matcher& matcher) { - if(text.empty()) + const auto& a = at(tokens, index); + const auto& b = at(tokens, index + 1); + + if (a.ch == '=') { + index++; + matcher = attribute_equals; + return true; + } + + if (!(is_one_of(a.ch, '~', '|', '^', '$', '*') && b.ch == '=')) return false; + + index += 2; + matcher = (attr_matcher)a.ch; + return true; +} + +// https://www.w3.org/TR/selectors-4/#typedef-attribute-selector +// <attribute-selector> = '[' <wq-name> ']' | +// '[' <wq-name> <attr-matcher> [ <string-token> | <ident-token> ] <attr-modifier>? ']' +// <attr-matcher> = [ '~' | '|' | '^' | '$' | '*' ]? '=' +// <attr-modifier> = i | s +css_attribute_selector parse_attribute_selector(const css_token& block) +{ + css_attribute_selector selector; + + const css_token_vector& tokens = block.value; + int index = 0; + + // <wq-name> + skip_whitespace(tokens, index); + wq_name wq_name = parse_wq_name(tokens, index); + if (wq_name.name == "") return {}; + + // attribute name in attribute selector is ASCII case-insensitive for HTML documents, regardless of document mode (quirks/no quirks) + auto prefix = lowcase(wq_name.prefix); + auto name = lowcase(wq_name.name); + + skip_whitespace(tokens, index); + if (index == (int)tokens.size()) // [name] + { + selector.type = select_attr; + selector.prefix = _id(prefix); + selector.name = _id(name); + selector.matcher = attribute_exists; + return selector; } - string_vector tokens; - split_string(text, tokens, "", " \t>+~", "(["); - if(tokens.empty()) + // <attr-matcher> + skip_whitespace(tokens, index); + attr_matcher matcher; + if (!parse_attr_matcher(tokens, index, matcher)) + return {}; + + // <string-token> | <ident-token> + skip_whitespace(tokens, index); + const css_token& value = at(tokens, index); + if (value.type != STRING && value.type != IDENT) + return {}; + index++; + + // <attr-modifier>? + skip_whitespace(tokens, index); + char modifier = 0; + const css_token& tok = at(tokens, index); + if (tok.type == IDENT) { + if (tok.ident() == "s") modifier = 's'; + else if (tok.ident() == "i") modifier = 'i'; + else return {}; // junk at the end of attribute selector + index++; + } + + skip_whitespace(tokens, index); + if (index != (int)tokens.size()) + return {}; // junk at the end of attribute selector + + // https://html.spec.whatwg.org/multipage/semantics-other.html#case-sensitivity-of-selectors + // Attribute selectors on an HTML element in an HTML document must treat the values + // of attributes with the following names as ASCII case-insensitive (unless s modifier is specified): + static string_vector special_attributes = { + "accept", + "accept-charset", + "align", + "alink", + "axis", + "bgcolor", + "charset", + "checked", + "clear", + "codetype", + "color", + "compact", + "declare", + "defer", + "dir", + "direction", + "disabled", + "enctype", + "face", + "frame", + "hreflang", + "http-equiv", + "lang", + "language", + "link", + "media", + "method", + "multiple", + "nohref", + "noresize", + "noshade", + "nowrap", + "readonly", + "rel", + "rev", + "rules", + "scope", + "scrolling", + "selected", + "shape", + "target", + "text", + "type", + "valign", + "valuetype", + "vlink", + }; + + selector.type = select_attr; + selector.prefix = _id(prefix); + selector.name = _id(name); + selector.matcher = matcher; + selector.caseless_match = modifier == 'i' || (!modifier && name in special_attributes); + selector.value = selector.caseless_match ? lowcase(value.str) : value.str; + return selector; +} + +struct an_b +{ + int a, b; + bool valid; + an_b() : a(), b(), valid(false) {} + an_b(int a, int b) : a(a), b(b), valid(true) {} +}; + +// NOTE: "+ 5" is not valid, and strtol correctly fails to parse it +bool to_int(string s, int& number) +{ + if (s == "") return false; + + const char* ptr = s.c_str(); + char* end; + int n = strtol(ptr, &end, 10); + + if (end != ptr + s.size()) return false; + + number = n; + return true; +} + +// https://www.w3.org/TR/css-syntax-3/#anb-syntax +// I don't use the formal grammar because it creates a lot of unnecessary complexity. +// Deviations from the standard: +// * escapes are not allowed +// * comments are allowed inside numbers and identifiers: ev/**/en +an_b parse_an_b(string s) +{ + lcase(trim(s)); + if (s == "even") return {2, 0}; + if (s == "odd") return {2, 1}; + + int a, b; + + int i = (int)s.find('n'); + if (i == -1) + { + if (!to_int(s, b)) return {}; + return {0, b}; } - string left; - string right = tokens.back(); - char combinator = 0; + auto str_a = s.substr(0, i); + auto str_b = s.substr(i + 1); - tokens.pop_back(); - while(!tokens.empty() && (tokens.back() == " " || tokens.back() == "\t" || tokens.back() == "+" || tokens.back() == "~" || tokens.back() == ">")) + if (is_one_of(str_a, "", "+", "-")) + a = str_a == "-" ? -1 : 1; + else { - if(combinator == ' ' || combinator == 0) - { - combinator = tokens.back()[0]; - } - tokens.pop_back(); + if (!to_int(str_a, a)) return {}; } - for(const auto & token : tokens) + trim(str_b); // spaces after n are allowed: 2n + 3 + if (str_b != "") { - left += token; + if (str_b[0] == '+' || str_b[0] == '-') + while (is_whitespace(str_b[1])) str_b.erase(1, 1); // spaces after sign are allowed + + if (!to_int(str_b, b)) return {}; } + else + b = 0; - trim(left); - trim(right); + return {a, b}; +} - if(right.empty()) +int find_of_keyword(const css_token_vector& tokens) +{ + for (int i = 0; i < (int)tokens.size(); i++) { - return false; + if (tokens[i].ident() == "of") + return i; } + return -1; +} + +// :nth-child(An+B [of S]?) https://www.w3.org/TR/selectors-4/#the-nth-child-pseudo +// :nth-last-child(An+B [of S]?) +// where S is a forgiving <complex-selector-list> +// +// :nth-of-type(An+B) https://www.w3.org/TR/selectors-4/#the-nth-of-type-pseudo +// :nth-last-of-type(An+B) +// +css_attribute_selector parse_nth_child(const css_token& token, bool of_keyword, document_mode mode) +{ + css_attribute_selector selector(select_pseudo_class, lowcase(token.name)); - m_right.parse(right); + const auto& tokens = token.value; - switch(combinator) + // find "of" keyword + int i = of_keyword ? find_of_keyword(tokens) : -1; + if (i >= 0) { - case '>': - m_combinator = combinator_child; - break; - case '+': - m_combinator = combinator_adjacent_sibling; - break; - case '~': - m_combinator = combinator_general_sibling; - break; - default: - m_combinator = combinator_descendant; - break; + const auto& selector_tokens = slice(tokens, i + 1); + + // The standard doesn't specify if pseudo-elements are allowed in this selector list. + // But specifying them will make selector match nothing anyway because + // "The structural pseudo-classes only apply to elements in the document tree; + // they must never match pseudo-elements." https://www.w3.org/TR/selectors-4/#structural-pseudos + // So I parse as if they were not allowed. + selector.selector_list = parse_selector_list(selector_tokens, forgiving_mode + forbid_pseudo_elements, mode); + // NOTE: selector_list may be empty, this does not invalidate the selector. + + // Chrome/Firefox behavior differs from the standard: they treat S as unforgiving and allow pseudo-elements. + // NOTE: :is(), which also accepts <forgiving-selector-list>, is handled correctly by Chrome/Firefox. + // Use this code instead of above to match Chrome/Firefox behavior: + //selector.selector_list = parse_selector_list(selector_tokens, strict_mode); + //if (selector.selector_list.empty()) return {}; + } + + // get string representation of everything between "nth-child(" and "of" or ")", except for comments + string str = get_repr(tokens, 0, i); // Note: i == -1 works as expected + + an_b x = parse_an_b(str); + if (!x.valid) return {}; + + selector.a = x.a; + selector.b = x.b; + + return selector; +} + +css_attribute_selector parse_function_pseudo_class(const css_token& token, document_mode mode) +{ + string name = lowcase(token.name); + if (name == "nth-child" || name == "nth-last-child") + { + return parse_nth_child(token, true, mode); + } + else if (name == "nth-of-type" || name == "nth-last-of-type") + { + return parse_nth_child(token, false, mode); + } + else if (name == "is") // https://www.w3.org/TR/selectors-4/#matches + { + css_attribute_selector selector(select_pseudo_class, name); + // "taking a <forgiving-selector-list> as its sole argument" + // "Pseudo-elements... are not valid within :is()." + selector.selector_list = parse_selector_list(token.value, forgiving_mode + forbid_pseudo_elements, mode); + return selector; + } + else if (name == "not") // https://www.w3.org/TR/selectors-4/#negation + { + css_attribute_selector selector(select_pseudo_class, name); + // "taking a selector list as an argument" + // "Pseudo-elements... are not valid within :not()." + selector.selector_list = parse_selector_list(token.value, strict_mode + forbid_pseudo_elements, mode); + if (selector.selector_list.empty()) return {}; + return selector; + } + else if (name == "lang") // https://www.w3.org/TR/selectors-4/#the-lang-pseudo + { + css_attribute_selector selector(select_pseudo_class, name); + selector.value = get_repr(token.value); + return selector; + } + return {}; +} + +// simple = non-functional (without parentheses) +bool is_supported_simple_pseudo_class(const string& name) +{ + static std::set<string> supported_simple_pseudo_classes = + { + // Location Pseudo-classes https://www.w3.org/TR/selectors-4/#location + "any-link", "link", "visited", "local-link", "target", "target-within", "scope", + // User Action Pseudo-classes https://www.w3.org/TR/selectors-4/#useraction-pseudos + "hover", "active", "focus", "focus-visible", "focus-within", + // Tree-Structural pseudo-classes https://www.w3.org/TR/selectors-4/#structural-pseudos + "root", "empty", "first-child", "last-child", "only-child", "first-of-type", "last-of-type", "only-of-type", + }; + return supported_simple_pseudo_classes.count(lowcase(name)) == 1; +} + +// https://www.w3.org/TR/selectors-4/#typedef-pseudo-class-selector +// <pseudo-class-selector> = ':' <ident-token> | +// ':' <function-token> <any-value> ')' +// where <ident-token> is not before, after, first-line or first-letter +css_attribute_selector parse_pseudo_class(const css_token_vector& tokens, int& index, document_mode mode) +{ + const auto& a = at(tokens, index); + const auto& b = at(tokens, index + 1); + + if (a.ch != ':') + return {}; + + if (b.type == IDENT) + { + // unsupported pseudo-classes must be treated as invalid: https://www.w3.org/TR/selectors-4/#w3c-partial + if (!is_supported_simple_pseudo_class(b.ident())) + return {}; + + index += 2; + return { select_pseudo_class, b.ident() }; + } + if (b.type == CV_FUNCTION) + { + css_attribute_selector sel = parse_function_pseudo_class(b, mode); + if (sel) index += 2; + return sel; } + return {}; +} + +// https://www.w3.org/TR/selectors-4/#typedef-subclass-selector +// <subclass-selector> = <id-selector> | <class-selector> | <attribute-selector> | <pseudo-class-selector> +// <id-selector> = <hash-token> with hash_type == ID +// <class-selector> = '.' <ident-token> +css_attribute_selector parse_subclass_selector(const css_token_vector& tokens, int& index, document_mode mode) +{ + css_attribute_selector selector; - m_left = nullptr; + const auto& tok0 = at(tokens, index); + const auto& tok1 = at(tokens, index + 1); - if(!left.empty()) + switch (tok0.type) { - m_left = std::make_shared<css_selector>(); - if(!m_left->parse(left)) + case HASH: + if (tok0.hash_type == css_hash_id) { - return false; + index++; + selector.type = select_id; + string name = tok0.name; + // ids are matched ASCII case-insensitively in quirks mode + if (mode == quirks_mode) lcase(name); + selector.name = _id(name); + return selector; } + return {}; + + case '.': + if (tok1.type == IDENT) + { + index += 2; + selector.type = select_class; + string name = tok1.name; + // class names are matched ASCII case-insensitively in quirks mode + if (mode == quirks_mode) lcase(name); + selector.name = _id(name); + return selector; + } + return {}; + + case SQUARE_BLOCK: + selector = parse_attribute_selector(tok0); + if (selector) index++; + return selector; + + default: + return parse_pseudo_class(tokens, index, mode); } +} - return true; +// simple = non-functional (without parentheses) +bool is_supported_simple_pseudo_element(const string& name) +{ + return is_one_of(lowcase(name), + // Typographic Pseudo-elements https://www.w3.org/TR/css-pseudo-4/#typographic-pseudos + //"first-line", "first-letter", + // Highlight Pseudo-elements https://www.w3.org/TR/css-pseudo-4/#highlight-pseudos + //"selection", + // Tree-Abiding Pseudo-elements https://www.w3.org/TR/css-pseudo-4/#treelike + "before", "after" //"marker", "placeholder", + ); } -void litehtml::css_selector::calc_specificity() +css_attribute_selector parse_pseudo_element(const css_token_vector& tokens, int& index) { - if(m_right.m_tag != star_id) + const auto& a = at(tokens, index); + const auto& b = at(tokens, index + 1); + const auto& c = at(tokens, index + 2); + + if (a.ch != ':') + return {}; + if (b.ch != ':' && b.type != IDENT) + return {}; + + if (b.type == IDENT) // legacy syntax with one ':' https://www.w3.org/TR/selectors-4/#single-colon-pseudos { - m_specificity.d = 1; + if (!is_one_of(b.ident(), "before", "after")) // first-line/letter are not supported + return {}; + + index += 2; + return {select_pseudo_element, b.ident()}; } - for(const auto& attr : m_right.m_attrs) + + if (c.type == IDENT) // normal syntax with '::' { - if(attr.type == select_id) - { - m_specificity.b++; - } else - { - m_specificity.c++; - } + if (!is_supported_simple_pseudo_element(c.ident())) + return {}; + + index += 3; + return {select_pseudo_element, c.ident()}; } - if(m_left) + + return {}; +} + +// https://www.w3.org/TR/selectors-4/#typedef-compound-selector +// <compound-selector> = [ <type-selector>? <subclass-selector>* +// [ <pseudo-element-selector> <pseudo-class-selector>* ]* ]! +// NOTE: This grammar allows pseudo-classes to go before #id and .class and [attr]. +// Whitespace is forbidden: +// * Between any of the top-level components of a <compound-selector> +css_element_selector::ptr parse_compound_selector(const css_token_vector& tokens, int& index, document_mode mode) +{ + auto selector = make_shared<css_element_selector>(); + + // <type-selector>? + wq_name wq_name = parse_type_selector(tokens, index); + selector->m_prefix = _id(wq_name.prefix); + selector->m_tag = _id(wq_name.name); + + // <subclass-selector>* + while (css_attribute_selector sel = parse_subclass_selector(tokens, index, mode)) + selector->m_attrs.push_back(sel); + + // [ <pseudo-element-selector> <pseudo-class-selector>* ]* + while (true) { - m_left->calc_specificity(); - m_specificity += m_left->m_specificity; + auto sel = parse_pseudo_element(tokens, index); + if (!sel) break; + selector->m_attrs.push_back(sel); + + while ((sel = parse_pseudo_class(tokens, index, mode))) + selector->m_attrs.push_back(sel); } + + // [..]! "must produce at least one value" https://www.w3.org/TR/css-values-4/#mult-req + if (selector->m_tag == empty_id && selector->m_attrs.empty()) + return nullptr; + + if (selector->m_tag == empty_id) + selector->m_tag = star_id; + + return selector; } -void litehtml::css_selector::add_media_to_doc( document* doc ) const +// <combinator> = '>' | '+' | '~' | [ '|' '|' ] +// <whitespace> combinator is also handled here +// parse_combinator consumes all leading and trailing whitespace +// column combinator || is at-risk https://www.w3.org/TR/selectors-4/ and not implemented in Chrome/Firefox https://caniuse.com/mdn-css_selectors_column +int parse_combinator(const css_token_vector& tokens, int& index) { - if(m_media_query && doc) + bool ws = skip_whitespace(tokens, index); + + const css_token& tok = at(tokens, index); + if (is_one_of(tok.ch, '>', '+', '~')) +// if (tok.ch in ${'>', '+', '~'}) { - doc->add_media_list(m_media_query); + index++; + skip_whitespace(tokens, index); + return tok.ch; } + + return ws ? ' ' : 0; +} + +css_selector::ptr parse_complex_selector(const css_token_vector& tokens, document_mode mode) +{ + int index = 0; + skip_whitespace(tokens, index); + auto sel = parse_compound_selector(tokens, index, mode); + if (!sel) return nullptr; + + auto selector = make_shared<css_selector>(); + selector->m_right = *sel; + + // NOTE: all the whitespace is handled by parse_combinator, that's why skip_whitespace is never called in the loop + // NOTE: parse_complex_selector is different from most other parse_xxx functions in that it's required + // to parse all input tokens, it doesn't just parse as much as possible. + while (true) + { + int combinator = parse_combinator(tokens, index); + if (index == (int)tokens.size()) + // combinator == 0 means index already was at the end before the call to parse_combinator + return !combinator || combinator == ' ' ? selector : nullptr; + if (!combinator) // not the end and combinator failed to parse + return nullptr; + + // otherwise: index is not at the end, combinator is good and tokens[index] is not whitespace + // it means if parse_compound_selector fails it's an error + sel = parse_compound_selector(tokens, index, mode); + if (!sel) + return nullptr; + + auto new_selector = make_shared<css_selector>(); + new_selector->m_left = selector; + new_selector->m_right = *sel; + new_selector->m_combinator = (css_combinator)combinator; + selector = new_selector; + } +} + +// Return true if `selector` has (in any of its css_element_selector's) a css_attribute_selector +// of type `type` and name `name`. name == "" matches any name. +bool has_selector(const css_selector& selector, attr_select_type type, const string& name = "") +{ + for (const auto& sel : selector.m_right.m_attrs) + { + if (sel.type == type && (name == "" || equal_i(_s(sel.name), name))) + return true; + } + + if (selector.m_left) + return has_selector(*selector.m_left, type, name); + + return false; +} + +// https://www.w3.org/TR/css-syntax-3/#parse-comma-list +// https://www.w3.org/TR/selectors-4/#selector-list +// https://www.w3.org/TR/selectors-4/#forgiving-selector +// Parse comma-separated list of complex selectors. +css_selector::vector parse_selector_list(const css_token_vector& tokens, int options, document_mode mode) +{ + // NOTE: this is unnecessary: "If input contains only <whitespace-token>s, return an empty list." + + vector<css_token_vector> list_of_lists = parse_comma_separated_list(tokens); + css_selector::vector result; + + for (const auto& list: list_of_lists) + { + css_selector::ptr selector = parse_complex_selector(list, mode); + + // if selector is failed to parse or not allowed by the options + if (!selector || + ((options & forbid_pseudo_elements) && has_selector(*selector, select_pseudo_element))) + { + // in forgiving mode, ignore the bad selector + if (options & forgiving_mode) + continue; + + // in strict mode, entire selector-list fails to parse because of one bad selector + return {}; + } + + result.push_back(selector); + } + + return result; +} + +bool css_selector::parse(const string& text, document_mode mode) +{ + auto tokens = normalize(text, f_componentize); + auto ptr = parse_complex_selector(tokens, mode); + if (!ptr) return false; + *this = *ptr; + return true; } +} // namespace litehtml diff --git a/src/css_tokenizer.cpp b/src/css_tokenizer.cpp new file mode 100644 index 000000000..3daf31f7d --- /dev/null +++ b/src/css_tokenizer.cpp @@ -0,0 +1,725 @@ +#include "html.h" +#include "utf8_strings.h" +#include "css_tokenizer.h" + +namespace litehtml +{ + +void css_parse_error(string /*msg*/) +{ + //printf("%s\n", msg.c_str()); +} + +string css_token::ident() const +{ + if (type != IDENT) return ""; + return name.substr(0, 2) == "--" ? name : lowcase(name); +} + + +char mirror(char c) +{ + if (c == '{') return '}'; + if (c == '[') return ']'; + if (c == '(') return ')'; + return c; +} + +string css_token::get_repr(bool insert_spaces) const +{ + if (!is_component_value()) return repr; + + using litehtml::get_repr; + if (type == CV_FUNCTION) return name + '(' + get_repr(value, 0, -1, insert_spaces) + ')'; + + char opening_bracket = char(-type - 100); + char closing_bracket = mirror(opening_bracket); + return opening_bracket + get_repr(value, 0, -1, insert_spaces) + closing_bracket; +} + +// concatenate string representations of tokens +string get_repr(const css_token_vector& tokens, int index, int count, bool insert_spaces) +{ + if (count == -1) count = (int)tokens.size() - index; + string str; + string space = insert_spaces ? " " : ""; + for (int i = index; i < index + count; i++) + { + str += tokens[i].get_repr(insert_spaces) + space; + } + if (insert_spaces) remove(str, -1); + return str; +} + +// https://www.w3.org/TR/css-syntax-3/#whitespace +bool css_tokenizer::is_whitespace(int ch) { + // NOTE: \r and \f are converted to \n in filter_code_points + return ch == '\n' || ch == '\t' || ch == ' '; +} + +// https://www.w3.org/TR/css-syntax-3/#non-printable-code-point +bool css_tokenizer::is_non_printable_code_point(int ch) { + return (ch >= 0 && ch <= 8) || ch == 0xB || (ch >= 0xE && ch <= 0x1F) || ch == 0x7F; +} + +// https://www.w3.org/TR/css-syntax-3/#ident-start-code-point +bool css_tokenizer::is_ident_start_code_point(int ch) { + return is_letter(ch) || ch >= 0x80 || ch == '_'; +} + +// https://www.w3.org/TR/css-syntax-3/#ident-code-point +bool css_tokenizer::is_ident_code_point(int ch) { + return is_ident_start_code_point(ch) || is_digit(ch) || ch == '-'; +} + + +// Consume the next input code point. Return the current input code point. +// When we know that next input char is ASCII and not NUL, we can just write str[index++] instead. +int css_tokenizer::consume_char() +{ + // NOTE: if str[index] == 0 index is not incremented + return current_char = read_utf8_char(str, index); +} + +// https://www.w3.org/TR/css-syntax-3/#reconsume-the-current-input-code-point +// "reconsume" is not a good name - it should be called unconsume (the char will actually be reconsumed later when consume_char is called). +// When we know that current input char is ASCII and index != 0, we can just write index-- instead. +void css_tokenizer::unconsume_char() +{ + // see comment for current_char + if (current_char == 0) + return; + + // NOTE: if index == 0 index is not decremented + prev_utf8_char(str, index); +} + +int css_tokenizer::peek_char() +{ + int i = index; + return read_utf8_char(str, i); +} + +css_tokenizer::three_chars css_tokenizer::peek_chars() +{ + three_chars chars; + int i = index; + chars._1 = read_utf8_char(str, i); + chars._2 = read_utf8_char(str, i); + chars._3 = read_utf8_char(str, i); + return chars; +} + + +// https://www.w3.org/TR/css-syntax-3/#consume-comments +void css_tokenizer::consume_comments() +{ + while (true) + { + if (str[index] == '/' && str[index + 1] == '*') + { + int i = (int)str.find("*/", index + 2); + + if (i != -1) + index = i + 2; + else + { + index = (int)str.size(); + css_parse_error("eof in comment"); + break; + } + } + else + break; + } +} + +// https://www.w3.org/TR/css-syntax-3/#consume-escaped-code-point +// It assumes that the U+005C (\) has already been consumed and that the next input code point +// is not a newline (see https://www.w3.org/TR/css-syntax-3/#check-if-two-code-points-are-a-valid-escape). +int css_tokenizer::consume_escaped_code_point() +{ + // Consume the next input code point. + int ch = consume_char(); + + if (is_hex_digit(ch)) + { + int number = digit_value(ch); + // Consume as many hex digits as possible, but no more than 5. + int max = 5; + while (max-- > 0 && is_hex_digit(str[index])) + { + ch = consume_char(); + number = number * 16 + digit_value(ch); + } + // If the next input code point is whitespace, consume it as well. + if (is_whitespace(str[index])) + consume_char(); + // If this number is zero, or is for a surrogate, or is greater than the maximum allowed code point + if (number == 0 || is_surrogate(number) || number > 0x10FFFF) + return 0xFFFD; + // Otherwise, return the code point with that value. + return number; + } + else if (ch == 0) // EOF + { + // This is a parse error. Return U+FFFD. + css_parse_error("eof in escaped codepoint"); + return 0xFFFD; + } + else // anything else + // Return the current input code point. + return ch; +} + +// https://www.w3.org/TR/css-syntax-3/#consume-string-token +css_token css_tokenizer::consume_string_token(int ending_code_point) +{ + // Initially create a <string-token> with its value set to the empty string. + css_token token(STRING); + + while (true) + { + // Repeatedly consume the next input code point from the stream: + int ch = consume_char(); + switch (ch) + { + case 0: // EOF + // This is a parse error. Return the <string-token>. + css_parse_error("eof in string"); + return token; + case '\n': + // This is a parse error. Reconsume the current input code point, create a <bad-string-token>, and return it. + css_parse_error("newline in string"); + unconsume_char(); + return {BAD_STRING}; + case '\\': + // If the next input code point is EOF, do nothing. + if (str[index] == 0) + break; + // Otherwise, if the next input code point is a newline, consume it. + else if (str[index] == '\n') + index++; + // Otherwise, (the stream starts with a valid escape) consume an escaped code point and + // append the returned code point to the <string-token>’s value. + else + append_char(token.str, consume_escaped_code_point()); + break; + default: + if (ch == ending_code_point) + return token; + else // anything else + // Append the current input code point to the <string-token>’s value. + append_char(token.str, ch); + break; + } + } +} + +// https://www.w3.org/TR/css-syntax-3/#would-start-an-identifier +bool css_tokenizer::would_start_ident_sequence(three_chars chars) +{ + int c1 = chars._1; + int c2 = chars._2; + int c3 = chars._3; + + if (c1 == '-') + { + // If the second code point is an ident-start code point or a U+002D HYPHEN-MINUS, or + // the second and third code points are a valid escape, return true. Otherwise, return false. + return is_ident_start_code_point(c2) || c2 == '-' || (c2 == '\\' && c3 != '\n'); + } + else if (is_ident_start_code_point(c1)) + return true; + else if (c1 == '\\') + // If the first and second code points are a valid escape, return true. Otherwise, return false. + return c2 != '\n'; + else + return false; +} + +// https://www.w3.org/TR/css-syntax-3/#consume-name +string css_tokenizer::consume_ident_sequence() +{ + string result; + + while (true) + { + // Repeatedly consume the next input code point from the stream: + int ch = consume_char(); + + if (is_ident_code_point(ch)) + append_char(result, ch); // Append the code point to result. + + // else if the stream starts with a valid escape + // NOTE: the wording is confusing because ch is not in the input stream anymore (it has been consumed) + else if (ch == '\\' && str[index] != '\n') + // Consume an escaped code point. Append the returned code point to result. + append_char(result, consume_escaped_code_point()); + + else + { + // Reconsume the current input code point. Return result. + unconsume_char(); + return result; + } + } +} + +// https://www.w3.org/TR/css-syntax-3/#starts-with-a-number +bool css_tokenizer::would_start_a_number(int x, int y, int z) +{ + if (x == '+' || x == '-') + { + // If the second code point is a digit, return true. + if (is_digit(y)) return true; + // Otherwise, if the second code point is a U+002E (.) and the third code point is a digit, return true. + else if (y == '.' && is_digit(z)) return true; + // Otherwise, return false. + else return false; + } + else if (x == '.') + // If the second code point is a digit, return true. Otherwise, return false. + return is_digit(y); + else + return is_digit(x); +} + +// https://www.w3.org/TR/css-syntax-3/#convert-string-to-number +double css_tokenizer::convert_string_to_number(const string& str) +{ + const char* p = str.c_str(); + + // Divide the string into seven components, in order from left to right: + + // 1. A sign: a single U+002B (+) or U+002D (-), or the empty string. + // Let s be the number -1 if the sign is U+002D (-); otherwise, let s be the number 1. + double s = 1; + if (*p == '-') s = -1, p++; + else if (*p == '+') p++; + + // 2. An integer part: zero or more digits. If there is at least one digit, let i be the number formed by + // interpreting the digits as a base-10 integer; otherwise, let i be the number 0. + double i = 0; + while (is_digit(*p)) i = i * 10 + digit_value(*p++); + + // 3. A decimal point: a single U+002E (.), or the empty string. + if (*p == '.') p++; + + // 4. A fractional part: zero or more digits. If there is at least one digit, let f be the number formed by + // interpreting the digits as a base-10 integer and d be the number of digits; + // otherwise, let f and d be the number 0. + double f = 0, d = 0; + while (is_digit(*p)) f = f * 10 + digit_value(*p++), d++; + + // 5. An exponent indicator: a single U+0045 (E) or U+0065 (e), or the empty string. + if (*p == 'e' || *p == 'E') p++; + + // 6. An exponent sign: a single U+002B (+) or U+002D (-), or the empty string. + // Let t be the number -1 if the sign is U+002D (-); otherwise, let t be the number 1. + double t = 1; + if (*p == '-') t = -1, p++; + else if (*p == '+') p++; + + // 7. An exponent: zero or more digits. If there is at least one digit, let e be the number formed by + // interpreting the digits as a base-10 integer; otherwise, let e be the number 0. + double e = 0; + while (is_digit(*p)) e = e * 10 + digit_value(*p++); + + // Return the number s·(i + f·10ᐨᵈ)·10ᵗᵉ. + return s * (i + f * pow(10, -d)) * pow(10, t * e); +} + +// https://www.w3.org/TR/css-syntax-3/#consume-number +double css_tokenizer::consume_number(css_number_type& type) +{ + // 1. Initially set type to "integer". Let repr be the empty string. + type = css_number_integer; + string repr; + + // 2. If the next input code point is U+002B (+) or U+002D (-), consume it and append it to repr. + if (is_one_of(str[index], '+', '-')) + append_char(repr, str[index++]); + + // 3. While the next input code point is a digit, consume it and append it to repr. + while (is_digit(str[index])) + append_char(repr, str[index++]); + + // 4. If the next 2 input code points are U+002E (.) followed by a digit, then: + if (str[index] == '.' && is_digit(str[index+1])) + { + // 1. Consume them. + // 2. Append them to repr. + append_char(repr, str[index++]); + append_char(repr, str[index++]); + // 3. Set type to "number". + type = css_number_number; + // 4. While the next input code point is a digit, consume it and append it to repr. + while (is_digit(str[index])) + append_char(repr, str[index++]); + } + + // 5. If the next 2 or 3 input code points are U+0045 (E) or U+0065 (e), + // optionally followed by U+002D (-) or U+002B (+), followed by a digit, then: + bool a = lowcase(str[index]) == 'e' && is_one_of(str[index+1], '+', '-') && is_digit(str[index+2]); + bool b = lowcase(str[index]) == 'e' && is_digit(str[index+1]); + + if (a || b) + { + // 1. Consume them. + // 2. Append them to repr. + append_char(repr, str[index++]); + append_char(repr, str[index++]); + if (a) append_char(repr, str[index++]); + // 3. Set type to "number". + type = css_number_number; + // 4. While the next input code point is a digit, consume it and append it to repr. + while (is_digit(str[index])) + append_char(repr, str[index++]); + } + + // 6. Convert repr to a number, and set the value to the returned value. + double value = convert_string_to_number(repr); + + // 7. Return value and type. + return value; +} + +// https://www.w3.org/TR/css-syntax-3/#consume-numeric-token +css_token css_tokenizer::consume_numeric_token() +{ + // Consume a number and let number be the result. + css_number_type type; + float number = (float)consume_number(type); + + // If the next 3 input code points would start an ident sequence, then: + if (would_start_ident_sequence(peek_chars())) + { + // 1. Create a <dimension-token> with the same value and type flag as number, and + // a unit set initially to the empty string. + css_token token(DIMENSION, number, type); + + // 2. Consume an ident sequence. Set the <dimension-token>’s unit to the returned value. + token.unit = consume_ident_sequence(); + + // 3. Return the <dimension-token>. + return token; + } + + // Otherwise, if the next input code point is U+0025 (%), consume it. + // Create a <percentage-token> with the same value as number, and return it. + if (str[index] == '%') + { + index++; + return {PERCENTAGE, number}; // NOTE: number_type is unused in <percentage-token> + } + + // Otherwise, create a <number-token> with the same value and type flag as number, and return it. + return {NUMBER, number, type}; +} + +// https://www.w3.org/TR/css-syntax-3/#consume-remnants-of-bad-url +void css_tokenizer::consume_remnants_of_bad_url() +{ + while (true) + { + // Repeatedly consume the next input code point from the stream: + int ch = consume_char(); + if (ch == ')' || ch == 0) // ')' or EOF + return; + // else if the input stream starts with a valid escape + // NOTE: the wording is confusing because ch is not in the input stream anymore (it has been consumed) + else if (ch == '\\' && str[index] != '\n') + { + consume_escaped_code_point(); + } + // anything else: Do nothing. + } +} + +// https://www.w3.org/TR/css-syntax-3/#consume-url-token +css_token css_tokenizer::consume_url_token() +{ + // Initially create a <url-token> with its value set to the empty string. + css_token token(URL); + + // Consume as much whitespace as possible. + while (is_whitespace(str[index])) + index++; + + while (true) + { + // Repeatedly consume the next input code point from the stream: + int ch = consume_char(); + switch (ch) + { + case ')': + // Return the <url-token>. + return token; + + case 0: // EOF + // This is a parse error. Return the <url-token>. + css_parse_error("eof in unquoted url"); + return token; + + case '\n': + case '\t': + case ' ': + // Consume as much whitespace as possible. + while (is_whitespace(str[index])) + index++; + // If the next input code point is U+0029 ()) or EOF, consume it and return the <url-token> + // (if EOF was encountered, this is a parse error); + if (str[index] == ')' || str[index] == 0) + { + if (str[index] == 0) + css_parse_error("eof in unquoted url"); + else + index++; // consume ')' + return token; + } + // otherwise, consume the remnants of a bad url, create a <bad-url-token>, and return it. + consume_remnants_of_bad_url(); + return {BAD_URL}; + + case '"': + case '\'': + case '(': + bad_url: + // This is a parse error. Consume the remnants of a bad url, create a <bad-url-token>, and return it. + css_parse_error("invalid char in unquoted url"); + consume_remnants_of_bad_url(); + return {BAD_URL}; + + case '\\': + // If the stream starts with a valid escape, consume an escaped code point and + // append the returned code point to the <url-token>’s value. + if (str[index] != '\n') + append_char(token.str, consume_escaped_code_point()); + // Otherwise, this is a parse error. Consume the remnants of a bad url, create a <bad-url-token>, and return it. + else + { + css_parse_error("escaped newline in unquoted url"); + consume_remnants_of_bad_url(); + return {BAD_URL}; + } + break; + + default: + if (is_non_printable_code_point(ch)) + goto bad_url; + else // anything else + // Append the current input code point to the <url-token>’s value. + append_char(token.str, ch); + break; + } + } +} + + +// https://www.w3.org/TR/css-syntax-3/#consume-ident-like-token +css_token css_tokenizer::consume_ident_like_token() +{ + // Consume an ident sequence, and let string be the result. + auto string = consume_ident_sequence(); + + // If string’s value is an ASCII case-insensitive match for "url", and the next input code point is + // U+0028 ((), consume it. + if (lowcase(string) == "url" && str[index] == '(') + { + index++; // consume '(' + + while (is_whitespace(str[index])) // not looking for 2 spaces, see next comment + index++; + + if (is_one_of(str[index], '"', '\'')) + { + // This is not exactly what standard says, but equivalent. The purpose is to preserve a whitespace token. + if (is_whitespace(str[index-1])) index--; + return {FUNCTION, string}; + } + else // Otherwise, consume a url token, and return it. + { + return consume_url_token(); + } + } + + // Otherwise, if the next input code point is U+0028 ((), consume it. + // Create a <function-token> with its value set to string and return it. + else if (str[index] == '(') + { + index++; + return {FUNCTION, string}; + } + + // Otherwise, create an <ident-token> with its value set to string and return it. + return {IDENT, string}; +} + +// https://www.w3.org/TR/css-syntax-3/#consume-token +css_token css_tokenizer::consume_token() +{ + consume_comments(); + + css_token token; + int start = index; + + // Consume the next input code point. + int ch = consume_char(); + three_chars next; + + switch (ch) + { + // whitespace + case '\n': + case '\t': + case ' ': + // Consume as much whitespace as possible. Return a <whitespace-token>. + while (is_whitespace(str[index])) + index++; + token.type = WHITESPACE; + break; + + case '"': + case '\'': + token = consume_string_token(ch); + break; + + case '#': + // If the next input code point is an ident code point or the next two input code points are a valid escape, then: + if (is_ident_code_point(peek_char()) || (str[index] == '\\' && str[index+1] != '\n')) + { + // 1. Create a <hash-token>. + token.type = HASH; + // 2. If the next 3 input code points would start an ident sequence, set the <hash-token>’s type flag to "id". + token.hash_type = would_start_ident_sequence(peek_chars()) ? css_hash_id : css_hash_unrestricted; + // 3. Consume an ident sequence, and set the <hash-token>’s value to the returned string. + token.name = consume_ident_sequence(); + // 4. Return the <hash-token>. + } + else + // Otherwise, return a <delim-token> with its value set to the current input code point. + token.ch = ch; + break; + + case '+': + case '.': + // If the input stream starts with a number, reconsume the current input code point, consume a numeric token, and return it. + next = peek_chars(); + if (would_start_a_number(ch, next._1, next._2)) + { + unconsume_char(); + token = consume_numeric_token(); + } + else + // Otherwise, return a <delim-token> with its value set to the current input code point. + token.ch = ch; + break; + + case '-': + // If the input stream starts with a number, reconsume the current input code point, consume a numeric token, and return it. + next = peek_chars(); + if (would_start_a_number(ch, next._1, next._2)) + { + unconsume_char(); + token = consume_numeric_token(); + } + // Otherwise, if the next 2 input code points are U+002D U+003E (->), consume them and return a <CDC-token>. + else if (next._1 == '-' && next._2 == '>') + { + index += 2; + token.type = CDC; + } + // Otherwise, if the input stream starts with an ident sequence, reconsume the current input code point, + // consume an ident-like token, and return it. + else if (would_start_ident_sequence({ ch, next._1, next._2 })) + { + unconsume_char(); + token = consume_ident_like_token(); + } + else + // Otherwise, return a <delim-token> with its value set to the current input code point. + token.ch = ch; + break; + + case '<': + // If the next 3 input code points are !--, consume them and return a <CDO-token>. + if (match(str, index, "!--")) + { + index += 3; + token.type = CDO; + } + else + // Otherwise, return a <delim-token> with its value set to the current input code point. + token.ch = ch; + break; + + case '@': + // If the next 3 input code points would start an ident sequence, consume an ident sequence, + // create an <at-keyword-token> with its value set to the returned value, and return it. + if (would_start_ident_sequence(peek_chars())) + { + token.type = AT_KEYWORD; + token.name = consume_ident_sequence(); + } + else + // Otherwise, return a <delim-token> with its value set to the current input code point. + token.ch = ch; + break; + + case '\\': + // If the input stream starts with a valid escape, reconsume the current input code point, + // consume an ident-like token, and return it. + if (str[index] != '\n') + { + unconsume_char(); + token = consume_ident_like_token(); + } + else + { + // Otherwise, this is a parse error. Return a <delim-token> with its value set to the current input code point. + css_parse_error("escaped newline outside a string"); + token.ch = ch; + } + break; + + case 0: // EOF + token.type = _EOF; + break; + + default: + if (is_digit(ch)) + { + // Reconsume the current input code point, consume a numeric token, and return it. + unconsume_char(); + token = consume_numeric_token(); + } + else if (is_ident_start_code_point(ch)) + { + // Reconsume the current input code point, consume an ident-like token, and return it. + unconsume_char(); + token = consume_ident_like_token(); + } + else // anything else + // Return a <delim-token> with its value set to the current input code point. + token.ch = ch; // NOTE: :;,()[]{} tokens are also handled here + } + + token.repr = str.substr(start, index - start); + return token; +} + +css_token_vector css_tokenizer::tokenize() +{ + css_token_vector tokens; + while (true) + { + css_token token = consume_token(); + if (token.type == EOF) break; + tokens.push_back(token); + } + return tokens; +} + + +} // namespace litehtml diff --git a/src/document.cpp b/src/document.cpp index ec336613d..d49891408 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -21,23 +21,23 @@ #include "el_div.h" #include "el_font.h" #include "el_tr.h" -#include <cmath> -#include <cstdio> -#include <algorithm> #include "gumbo.h" -#include "utf8_strings.h" #include "render_item.h" #include "render_table.h" #include "render_block.h" +#include "document_container.h" -litehtml::document::document(document_container* objContainer) +namespace litehtml { - m_container = objContainer; + +document::document(document_container* container) +{ + m_container = container; } -litehtml::document::~document() +document::~document() { - m_over_element = nullptr; + m_over_element = m_active_element = nullptr; if(m_container) { for(auto& font : m_fonts) @@ -47,13 +47,25 @@ litehtml::document::~document() } } -litehtml::document::ptr litehtml::document::createFromString( const char* str, document_container* objPainter, const char* master_styles, const char* user_styles ) +document::ptr document::createFromString( + const estring& str, + document_container* container, + const string& master_styles, + const string& user_styles ) { - // parse document into GumboOutput - GumboOutput* output = gumbo_parse(str); - // Create litehtml::document - document::ptr doc = std::make_shared<document>(objPainter); + document::ptr doc = make_shared<document>(container); + + // Parse document into GumboOutput + GumboOutput* output = doc->parse_html(str); + + // mode must be set before doc->create_node because it is used in html_tag::set_attr + switch (output->document->v.document.doc_type_quirks_mode) + { + case GUMBO_DOCTYPE_NO_QUIRKS: doc->m_mode = no_quirks_mode; break; + case GUMBO_DOCTYPE_QUIRKS: doc->m_mode = quirks_mode; break; + case GUMBO_DOCTYPE_LIMITED_QUIRKS: doc->m_mode = limited_quirks_mode; break; + } // Create litehtml::elements. elements_list root_elements; @@ -62,17 +74,18 @@ litehtml::document::ptr litehtml::document::createFromString( const char* str, d { doc->m_root = root_elements.back(); } + // Destroy GumboOutput gumbo_destroy_output(&kGumboDefaultOptions, output); - if (master_styles && *master_styles) + if (master_styles != "") { - doc->m_master_css.parse_stylesheet(master_styles, nullptr, doc, nullptr); + doc->m_master_css.parse_css_stylesheet(master_styles, "", doc); doc->m_master_css.sort_selectors(); } - if (user_styles && *user_styles) + if (user_styles != "") { - doc->m_user_css.parse_stylesheet(user_styles, nullptr, doc, nullptr); + doc->m_user_css.parse_css_stylesheet(user_styles, "", doc); doc->m_user_css.sort_selectors(); } @@ -90,27 +103,22 @@ litehtml::document::ptr litehtml::document::createFromString( const char* str, d doc->m_root->parse_attributes(); // parse style sheets linked in document - media_query_list::ptr media; for (const auto& css : doc->m_css) { - if (!css.media.empty()) - { - media = media_query_list::create_from_string(css.media, doc); - } - else + media_query_list_list::ptr media; + if (css.media != "") { - media = nullptr; + auto mq_list = parse_media_query_list(css.media, doc); + media = make_shared<media_query_list_list>(); + media->add(mq_list); } - doc->m_styles.parse_stylesheet(css.text.c_str(), css.baseurl.c_str(), doc, media); + doc->m_styles.parse_css_stylesheet(css.text, css.baseurl, doc, media); } // Sort css selectors using CSS rules. doc->m_styles.sort_selectors(); - // get current media features - if (!doc->m_media_lists.empty()) - { - doc->update_media_lists(doc->m_media); - } + // Apply media features. + doc->update_media_lists(doc->m_media); // Apply parsed styles. doc->m_root->apply_stylesheet(doc->m_styles); @@ -118,128 +126,333 @@ litehtml::document::ptr litehtml::document::createFromString( const char* str, d // Apply user styles if any doc->m_root->apply_stylesheet(doc->m_user_css); - // Initialize m_css + // Initialize element::m_css doc->m_root->compute_styles(); // Create rendering tree doc->m_root_render = doc->m_root->create_render_item(nullptr); // Now the m_tabular_elements is filled with tabular elements. - // We have to check the tabular elements for missing table elements + // We have to check the tabular elements for missing table elements // and create the anonymous boxes in visual table layout doc->fix_tables_layout(); // Finally initialize elements - // init() return pointer to the render_init element because it can change its type - doc->m_root_render = doc->m_root_render->init(); + // init() returns pointer to the render_init element because it can change its type + if(doc->m_root_render) + { + doc->m_root_render = doc->m_root_render->init(); + } } return doc; } -litehtml::uint_ptr litehtml::document::add_font( const char* name, int size, const char* weight, const char* style, const char* decoration, font_metrics* fm ) +// https://html.spec.whatwg.org/multipage/parsing.html#change-the-encoding +encoding adjust_meta_encoding(encoding meta_encoding, encoding current_encoding) { - uint_ptr ret = 0; + // 1. + if (current_encoding == encoding::utf_16le || current_encoding == encoding::utf_16be) + return current_encoding; + + // 2. + if (meta_encoding == encoding::utf_16le || meta_encoding == encoding::utf_16be) + return encoding::utf_8; - if(!name) + // 3. + if (meta_encoding == encoding::x_user_defined) + return encoding::windows_1252; + + // 4,5,6. + return meta_encoding; +} + +// https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-inhead:change-the-encoding +encoding get_meta_encoding(GumboNode* root) +{ + // find <head> + GumboNode* head = nullptr; + for (size_t i = 0; i < root->v.element.children.length; i++) { - name = m_container->get_default_font_name(); + GumboNode* node = (GumboNode*)root->v.element.children.data[i]; + if (node->type == GUMBO_NODE_ELEMENT && node->v.element.tag == GUMBO_TAG_HEAD) + { + head = node; + break; + } + } + if (!head) return encoding::null; + + // go through <meta> tags in <head> + for (size_t i = 0; i < head->v.element.children.length; i++) + { + GumboNode* node = (GumboNode*)head->v.element.children.data[i]; + if (node->type != GUMBO_NODE_ELEMENT || node->v.element.tag != GUMBO_TAG_META) + continue; + + auto charset = gumbo_get_attribute(&node->v.element.attributes, "charset"); + auto http_equiv = gumbo_get_attribute(&node->v.element.attributes, "http-equiv"); + auto content = gumbo_get_attribute(&node->v.element.attributes, "content"); + // 1. If the element has a charset attribute... + if (charset) + { + auto encoding = get_encoding(charset->value); + if (encoding != encoding::null) + return encoding; + } + // 2. Otherwise, if the element has an http-equiv attribute... + else if (http_equiv && t_strcasecmp(http_equiv->value, "content-type") == 0 && content) + { + auto encoding = extract_encoding_from_meta_element(content->value); + if (encoding != encoding::null) + return encoding; + } } - char strSize[20]; - t_itoa(size, strSize, 20, 10); + return encoding::null; +} - string key = name; - key += ":"; - key += strSize; - key += ":"; - key += weight; - key += ":"; - key += style; - key += ":"; - key += decoration; +// substitute for gumbo_parse that handles encodings +GumboOutput* document::parse_html(estring str) +{ + // https://html.spec.whatwg.org/multipage/parsing.html#the-input-byte-stream + encoding_sniffing_algorithm(str); + // cannot store output in local variable because gumbo keeps pointers into it, + // which will be accessed later in gumbo_tag_from_original_text + if (str.encoding == encoding::utf_8) + m_text = str; + else + decode(str, str.encoding, m_text); - if(m_fonts.find(key) == m_fonts.end()) + // Gumbo does not support callbacks on node creation, so we cannot change encoding while parsing. + // Instead, we parse entire file and then handle <meta> tags. + + // Using gumbo_parse_with_options to pass string length (m_text may contain NUL chars). + GumboOutput* output = gumbo_parse_with_options(&kGumboDefaultOptions, m_text.data(), m_text.size()); + + if (str.confidence == confidence::certain) + return output; + + // Otherwise: confidence is tentative. + + // If valid HTML encoding is specified in <meta> tag... + encoding meta_encoding = get_meta_encoding(output->root); + if (meta_encoding != encoding::null) + { + // ...and it is different from currently used encoding... + encoding new_encoding = adjust_meta_encoding(meta_encoding, str.encoding); + if (new_encoding != str.encoding) + { + // ...reparse with the new encoding. + gumbo_destroy_output(&kGumboDefaultOptions, output); + m_text.clear(); + + if (new_encoding == encoding::utf_8) + m_text = str; + else + decode(str, new_encoding, m_text); + output = gumbo_parse_with_options(&kGumboDefaultOptions, m_text.data(), m_text.size()); + } + } + + return output; +} + +void document::create_node(void* gnode, elements_list& elements, bool parseTextNode) +{ + auto* node = (GumboNode*)gnode; + switch (node->type) { - font_style fs = (font_style) value_index(style, font_style_strings, font_style_normal); - int fw = value_index(weight, font_weight_strings, -1); - if(fw >= 0) + case GUMBO_NODE_ELEMENT: + { + string_map attrs; + GumboAttribute* attr; + for (unsigned int i = 0; i < node->v.element.attributes.length; i++) { - switch(fw) + attr = (GumboAttribute*)node->v.element.attributes.data[i]; + attrs[attr->name] = attr->value; + } + + + element::ptr ret; + const char* tag = gumbo_normalized_tagname(node->v.element.tag); + if (tag[0]) + { + ret = create_element(tag, attrs); + } + else + { + if (node->v.element.original_tag.data && node->v.element.original_tag.length) { - case litehtml::font_weight_bold: - fw = 700; - break; - case litehtml::font_weight_bolder: - fw = 600; - break; - case litehtml::font_weight_lighter: - fw = 300; - break; - case litehtml::font_weight_normal: - fw = 400; - break; - case litehtml::font_weight_100: - fw = 100; - break; - case litehtml::font_weight_200: - fw = 200; - break; - case litehtml::font_weight_300: - fw = 300; - break; - case litehtml::font_weight_400: - fw = 400; - break; - case litehtml::font_weight_500: - fw = 500; - break; - case litehtml::font_weight_600: - fw = 600; - break; - case litehtml::font_weight_700: - fw = 700; - break; - case litehtml::font_weight_800: - fw = 800; - break; - case litehtml::font_weight_900: - fw = 900; - break; + string str; + gumbo_tag_from_original_text(&node->v.element.original_tag); + str.append(node->v.element.original_tag.data, node->v.element.original_tag.length); + ret = create_element(str.c_str(), attrs); } - } else + } + if (!strcmp(tag, "script")) { - fw = atoi(weight); - if(fw < 100) + parseTextNode = false; + } + if (ret) + { + elements_list child; + for (unsigned int i = 0; i < node->v.element.children.length; i++) { - fw = 400; + child.clear(); + create_node(static_cast<GumboNode*> (node->v.element.children.data[i]), child, parseTextNode); + std::for_each(child.begin(), child.end(), + [&ret](element::ptr& el) + { + ret->appendChild(el); + } + ); } + elements.push_back(ret); + } + } + break; + case GUMBO_NODE_TEXT: + { + if (!parseTextNode) + { + elements.push_back(std::make_shared<el_text>(node->v.text.text, shared_from_this())); + } + else + { + m_container->split_text(node->v.text.text, + [this, &elements](const char* text) { elements.push_back(std::make_shared<el_text>(text, shared_from_this())); }, + [this, &elements](const char* text) { elements.push_back(std::make_shared<el_space>(text, shared_from_this())); }); + } + } + break; + case GUMBO_NODE_CDATA: + { + element::ptr ret = std::make_shared<el_cdata>(shared_from_this()); + ret->set_data(node->v.text.text); + elements.push_back(ret); + } + break; + case GUMBO_NODE_COMMENT: + { + element::ptr ret = std::make_shared<el_comment>(shared_from_this()); + ret->set_data(node->v.text.text); + elements.push_back(ret); + } + break; + case GUMBO_NODE_WHITESPACE: + { + string str = node->v.text.text; + for (size_t i = 0; i < str.length(); i++) + { + elements.push_back(std::make_shared<el_space>(str.substr(i, 1).c_str(), shared_from_this())); } + } + break; + default: + break; + } +} - unsigned int decor = 0; +element::ptr document::create_element(const char* tag_name, const string_map& attributes) +{ + element::ptr newTag; + document::ptr this_doc = shared_from_this(); + if (m_container) + { + newTag = m_container->create_element(tag_name, attributes, this_doc); + } + if (!newTag) + { + if (!strcmp(tag_name, "br")) + { + newTag = std::make_shared<el_break>(this_doc); + } + else if (!strcmp(tag_name, "p")) + { + newTag = std::make_shared<el_para>(this_doc); + } + else if (!strcmp(tag_name, "img")) + { + newTag = std::make_shared<el_image>(this_doc); + } + else if (!strcmp(tag_name, "table")) + { + newTag = std::make_shared<el_table>(this_doc); + } + else if (!strcmp(tag_name, "td") || !strcmp(tag_name, "th")) + { + newTag = std::make_shared<el_td>(this_doc); + } + else if (!strcmp(tag_name, "link")) + { + newTag = std::make_shared<el_link>(this_doc); + } + else if (!strcmp(tag_name, "title")) + { + newTag = std::make_shared<el_title>(this_doc); + } + else if (!strcmp(tag_name, "a")) + { + newTag = std::make_shared<el_anchor>(this_doc); + } + else if (!strcmp(tag_name, "tr")) + { + newTag = std::make_shared<el_tr>(this_doc); + } + else if (!strcmp(tag_name, "style")) + { + newTag = std::make_shared<el_style>(this_doc); + } + else if (!strcmp(tag_name, "base")) + { + newTag = std::make_shared<el_base>(this_doc); + } + else if (!strcmp(tag_name, "body")) + { + newTag = std::make_shared<el_body>(this_doc); + } + else if (!strcmp(tag_name, "div")) + { + newTag = std::make_shared<el_div>(this_doc); + } + else if (!strcmp(tag_name, "script")) + { + newTag = std::make_shared<el_script>(this_doc); + } + else if (!strcmp(tag_name, "font")) + { + newTag = std::make_shared<el_font>(this_doc); + } + else + { + newTag = std::make_shared<html_tag>(this_doc); + } + } - if(decoration) + if (newTag) + { + newTag->set_tagName(tag_name); + for (const auto& attribute : attributes) { - std::vector<string> tokens; - split_string(decoration, tokens, " "); - for(auto & token : tokens) - { - if(!t_strcasecmp(token.c_str(), "underline")) - { - decor |= font_decoration_underline; - } else if(!t_strcasecmp(token.c_str(), "line-through")) - { - decor |= font_decoration_linethrough; - } else if(!t_strcasecmp(token.c_str(), "overline")) - { - decor |= font_decoration_overline; - } - } + newTag->set_attr(attribute.first.c_str(), attribute.second.c_str()); } + } - font_item fi= {0}; + return newTag; +} + +uint_ptr document::add_font( const font_description& descr, font_metrics* fm ) +{ + uint_ptr ret = 0; + + std::string key = descr.hash(); + + if(m_fonts.find(key) == m_fonts.end()) + { + font_item fi = {0, {}}; - fi.font = m_container->create_font(name, size, fw, fs, decor, &fi.metrics); + fi.font = m_container->create_font(descr, this, &fi.metrics); m_fonts[key] = fi; ret = fi.font; if(fm) @@ -250,29 +463,14 @@ litehtml::uint_ptr litehtml::document::add_font( const char* name, int size, con return ret; } -litehtml::uint_ptr litehtml::document::get_font( const char* name, int size, const char* weight, const char* style, const char* decoration, font_metrics* fm ) +uint_ptr document::get_font( const font_description& descr, font_metrics* fm ) { - if(!size) + if(!descr.size) { return 0; } - if(!name) - { - name = m_container->get_default_font_name(); - } - - char strSize[20]; - t_itoa(size, strSize, 20, 10); - string key = name; - key += ":"; - key += strSize; - key += ":"; - key += weight; - key += ":"; - key += style; - key += ":"; - key += decoration; + auto key = descr.hash(); auto el = m_fonts.find(key); @@ -284,20 +482,20 @@ litehtml::uint_ptr litehtml::document::get_font( const char* name, int size, con } return el->second.font; } - return add_font(name, size, weight, style, decoration, fm); + return add_font(descr, fm); } -int litehtml::document::render( int max_width, render_type rt ) +int document::render( int max_width, render_type rt ) { int ret = 0; - if(m_root) + if(m_root && m_root_render) { - position client_rc; - m_container->get_client_rect(client_rc); + position viewport; + m_container->get_viewport(viewport); containing_block_context cb_context; cb_context.width = max_width; cb_context.width.type = containing_block_context::cbc_value_type_absolute; - cb_context.height = client_rc.height; + cb_context.height = viewport.height; cb_context.height.type = containing_block_context::cbc_value_type_absolute; if(rt == render_fixed_only) @@ -322,7 +520,7 @@ int litehtml::document::render( int max_width, render_type rt ) return ret; } -void litehtml::document::draw( uint_ptr hdc, int x, int y, const position* clip ) +void document::draw( uint_ptr hdc, int x, int y, const position* clip ) { if(m_root && m_root_render) { @@ -331,20 +529,7 @@ void litehtml::document::draw( uint_ptr hdc, int x, int y, const position* clip } } -int litehtml::document::to_pixels( const char* str, int fontSize, bool* is_percent/*= 0*/ ) const -{ - if(!str) return 0; - - css_length val; - val.fromString(str); - if(is_percent && val.units() == css_units_percentage && !val.is_predefined()) - { - *is_percent = true; - } - return to_pixels(val, fontSize); -} - -int litehtml::document::to_pixels( const css_length& val, int fontSize, int size ) const +int document::to_pixels( const css_length& val, const font_metrics& metrics, int size ) const { if(val.is_predefined()) { @@ -357,20 +542,26 @@ int litehtml::document::to_pixels( const css_length& val, int fontSize, int size ret = val.calc_percent(size); break; case css_units_em: - ret = round_f(val.val() * (float) fontSize); + ret = round_f(val.val() * (float) metrics.font_size); break; + + // https://drafts.csswg.org/css-values-4/#absolute-lengths case css_units_pt: - ret = m_container->pt_to_px((int) val.val()); + ret = m_container->pt_to_px(round_f(val.val())); break; case css_units_in: - ret = m_container->pt_to_px((int) (val.val() * 72)); + ret = m_container->pt_to_px(round_f(val.val() * 72)); // 1in = 72pt + break; + case css_units_pc: + ret = m_container->pt_to_px(round_f(val.val() * 12)); // 1pc = (1/6)in = 12pt break; case css_units_cm: - ret = m_container->pt_to_px((int) (val.val() * 0.3937 * 72)); + ret = m_container->pt_to_px(round_f(val.val() * 0.3937f * 72)); // 1cm = (1/2.54)in = (72/2.54)pt break; case css_units_mm: - ret = m_container->pt_to_px((int) (val.val() * 0.3937 * 72) / 10); + ret = m_container->pt_to_px(round_f(val.val() * 0.3937f * 72 / 10)); break; + case css_units_vw: ret = (int)((double)m_media.width * (double)val.val() / 100.0); break; @@ -386,6 +577,12 @@ int litehtml::document::to_pixels( const css_length& val, int fontSize, int size case css_units_rem: ret = (int) ((double) m_root->css().get_font_size() * (double) val.val()); break; + case css_units_ex: + ret = (int) ((double) metrics.x_height * val.val()); + break; + case css_units_ch: + ret = (int) ((double) metrics.ch_width * val.val()); + break; default: ret = (int) val.val(); break; @@ -393,68 +590,48 @@ int litehtml::document::to_pixels( const css_length& val, int fontSize, int size return ret; } -void litehtml::document::cvt_units( css_length& val, int fontSize, int size ) const +void document::cvt_units( css_length& val, const font_metrics& metrics, int size ) const { if(val.is_predefined()) { return; } - int ret; - switch(val.units()) + if(val.units() != css_units_percentage) { - case css_units_em: - ret = round_f(val.val() * (float) fontSize); - val.set_value((float) ret, css_units_px); - break; - case css_units_pt: - ret = m_container->pt_to_px((int) val.val()); - val.set_value((float) ret, css_units_px); - break; - case css_units_in: - ret = m_container->pt_to_px((int) (val.val() * 72)); - val.set_value((float) ret, css_units_px); - break; - case css_units_cm: - ret = m_container->pt_to_px((int) (val.val() * 0.3937 * 72)); - val.set_value((float) ret, css_units_px); - break; - case css_units_mm: - ret = m_container->pt_to_px((int) (val.val() * 0.3937 * 72) / 10); - val.set_value((float) ret, css_units_px); - break; + val.set_value((float)to_pixels(val, metrics, size), css_units_px); } } -int litehtml::document::width() const +int document::width() const { return m_size.width; } -int litehtml::document::height() const +int document::height() const { return m_size.height; } -int litehtml::document::content_width() const +int document::content_width() const { return m_content_size.width; } -int litehtml::document::content_height() const +int document::content_height() const { return m_content_size.height; } -void litehtml::document::add_stylesheet( const char* str, const char* baseurl, const char* media ) +void document::add_stylesheet( const char* str, const char* baseurl, const char* media ) { if(str && str[0]) { - m_css.push_back(css_text(str, baseurl, media)); + m_css.emplace_back(str, baseurl, media); } } -bool litehtml::document::on_mouse_over( int x, int y, int client_x, int client_y, position::vector& redraw_boxes ) +bool document::on_mouse_over( int x, int y, int client_x, int client_y, position::vector& redraw_boxes ) { if(!m_root || !m_root_render) { @@ -471,6 +648,7 @@ bool litehtml::document::on_mouse_over( int x, int y, int client_x, int client_y { if(m_over_element->on_mouse_leave()) { + m_container->on_mouse_event(m_over_element, mouse_event_leave); state_was_changed = true; } } @@ -487,17 +665,18 @@ bool litehtml::document::on_mouse_over( int x, int y, int client_x, int client_y } cursor = m_over_element->css().get_cursor(); } - + m_container->set_cursor(cursor.c_str()); - + if(state_was_changed) { + m_container->on_mouse_event(m_over_element, mouse_event_enter); return m_root->find_styles_changes(redraw_boxes); } return false; } -bool litehtml::document::on_mouse_leave( position::vector& redraw_boxes ) +bool document::on_mouse_leave( position::vector& redraw_boxes ) { if(!m_root || !m_root_render) { @@ -507,13 +686,14 @@ bool litehtml::document::on_mouse_leave( position::vector& redraw_boxes ) { if(m_over_element->on_mouse_leave()) { + m_container->on_mouse_event(m_over_element, mouse_event_leave); return m_root->find_styles_changes(redraw_boxes); } } return false; } -bool litehtml::document::on_lbutton_down( int x, int y, int client_x, int client_y, position::vector& redraw_boxes ) +bool document::on_lbutton_down( int x, int y, int client_x, int client_y, position::vector& redraw_boxes ) { if(!m_root || !m_root_render) { @@ -521,6 +701,7 @@ bool litehtml::document::on_lbutton_down( int x, int y, int client_x, int client } element::ptr over_el = m_root_render->get_element_by_point(x, y, client_x, client_y); + m_active_element = over_el; bool state_was_changed = false; @@ -530,6 +711,7 @@ bool litehtml::document::on_lbutton_down( int x, int y, int client_x, int client { if(m_over_element->on_mouse_leave()) { + m_container->on_mouse_event(m_over_element, mouse_event_leave); state_was_changed = true; } } @@ -558,13 +740,14 @@ bool litehtml::document::on_lbutton_down( int x, int y, int client_x, int client if(state_was_changed) { + m_container->on_mouse_event(m_over_element, mouse_event_enter); return m_root->find_styles_changes(redraw_boxes); } return false; } -bool litehtml::document::on_lbutton_up( int x, int y, int client_x, int client_y, position::vector& redraw_boxes ) +bool document::on_lbutton_up( int /*x*/, int /*y*/, int /*client_x*/, int /*client_y*/, position::vector& redraw_boxes ) { if(!m_root || !m_root_render) { @@ -572,7 +755,7 @@ bool litehtml::document::on_lbutton_up( int x, int y, int client_x, int client_y } if(m_over_element) { - if(m_over_element->on_lbutton_up()) + if(m_over_element->on_lbutton_up(m_active_element == m_over_element)) { return m_root->find_styles_changes(redraw_boxes); } @@ -580,90 +763,37 @@ bool litehtml::document::on_lbutton_up( int x, int y, int client_x, int client_y return false; } -litehtml::element::ptr litehtml::document::create_element(const char* tag_name, const string_map& attributes) -{ - element::ptr newTag; - document::ptr this_doc = shared_from_this(); - if(m_container) - { - newTag = m_container->create_element(tag_name, attributes, this_doc); - } - if(!newTag) - { - if(!strcmp(tag_name, "br")) - { - newTag = std::make_shared<litehtml::el_break>(this_doc); - } else if(!strcmp(tag_name, "p")) - { - newTag = std::make_shared<litehtml::el_para>(this_doc); - } else if(!strcmp(tag_name, "img")) - { - newTag = std::make_shared<litehtml::el_image>(this_doc); - } else if(!strcmp(tag_name, "table")) - { - newTag = std::make_shared<litehtml::el_table>(this_doc); - } else if(!strcmp(tag_name, "td") || !strcmp(tag_name, "th")) - { - newTag = std::make_shared<litehtml::el_td>(this_doc); - } else if(!strcmp(tag_name, "link")) - { - newTag = std::make_shared<litehtml::el_link>(this_doc); - } else if(!strcmp(tag_name, "title")) - { - newTag = std::make_shared<litehtml::el_title>(this_doc); - } else if(!strcmp(tag_name, "a")) - { - newTag = std::make_shared<litehtml::el_anchor>(this_doc); - } else if(!strcmp(tag_name, "tr")) - { - newTag = std::make_shared<litehtml::el_tr>(this_doc); - } else if(!strcmp(tag_name, "style")) - { - newTag = std::make_shared<litehtml::el_style>(this_doc); - } else if(!strcmp(tag_name, "base")) - { - newTag = std::make_shared<litehtml::el_base>(this_doc); - } else if(!strcmp(tag_name, "body")) - { - newTag = std::make_shared<litehtml::el_body>(this_doc); - } else if(!strcmp(tag_name, "div")) - { - newTag = std::make_shared<litehtml::el_div>(this_doc); - } else if(!strcmp(tag_name, "script")) - { - newTag = std::make_shared<litehtml::el_script>(this_doc); - } else if(!strcmp(tag_name, "font")) - { - newTag = std::make_shared<litehtml::el_font>(this_doc); - } else - { - newTag = std::make_shared<litehtml::html_tag>(this_doc); - } - } - - if(newTag) - { - newTag->set_tagName(tag_name); - for (const auto & attribute : attributes) - { - newTag->set_attr(attribute.first.c_str(), attribute.second.c_str()); - } - } - - return newTag; +bool document::on_button_cancel(position::vector& redraw_boxes) { + if(!m_root || !m_root_render) + { + return false; + } + m_active_element = nullptr; + if(m_over_element) + { + if(m_over_element->on_mouse_leave()) + { + m_container->on_mouse_event(m_over_element, mouse_event_leave); + } + if(m_over_element->on_lbutton_up(false)) + { + return m_root->find_styles_changes(redraw_boxes); + } + } + return false; } -void litehtml::document::get_fixed_boxes( position::vector& fixed_boxes ) +void document::get_fixed_boxes( position::vector& fixed_boxes ) { fixed_boxes = m_fixed_boxes; } -void litehtml::document::add_fixed_box( const position& pos ) +void document::add_fixed_box( const position& pos ) { m_fixed_boxes.push_back(pos); } -bool litehtml::document::media_changed() +bool document::media_changed() { container()->get_media_features(m_media); if (update_media_lists(m_media)) @@ -675,13 +805,13 @@ bool litehtml::document::media_changed() return false; } -bool litehtml::document::lang_changed() +bool document::lang_changed() { - if(!m_media_lists.empty()) + if (!m_media_lists.empty()) { string culture; container()->get_language(m_lang, culture); - if(!culture.empty()) + if (!culture.empty()) { m_culture = m_lang + '-' + culture; } @@ -696,12 +826,13 @@ bool litehtml::document::lang_changed() return false; } -bool litehtml::document::update_media_lists(const media_features& features) +// Apply media features (determine which selectors are active). +bool document::update_media_lists(const media_features& features) { bool update_styles = false; - for(auto & m_media_list : m_media_lists) + for (auto& media_list : m_media_lists) { - if(m_media_list->apply_media_features(features)) + if (media_list->apply_media_features(features)) { update_styles = true; } @@ -709,114 +840,13 @@ bool litehtml::document::update_media_lists(const media_features& features) return update_styles; } -void litehtml::document::add_media_list( const media_query_list::ptr& list ) +void document::add_media_list(media_query_list_list::ptr list) { - if(list) - { - if(std::find(m_media_lists.begin(), m_media_lists.end(), list) == m_media_lists.end()) - { - m_media_lists.push_back(list); - } - } -} - -void litehtml::document::create_node(void* gnode, elements_list& elements, bool parseTextNode) -{ - auto* node = (GumboNode*)gnode; - switch (node->type) - { - case GUMBO_NODE_ELEMENT: - { - string_map attrs; - GumboAttribute* attr; - for (unsigned int i = 0; i < node->v.element.attributes.length; i++) - { - attr = (GumboAttribute*)node->v.element.attributes.data[i]; - attrs[attr->name] = attr->value; - } - - - element::ptr ret; - const char* tag = gumbo_normalized_tagname(node->v.element.tag); - if (tag[0]) - { - ret = create_element(tag, attrs); - } - else - { - if (node->v.element.original_tag.data && node->v.element.original_tag.length) - { - std::string strA; - gumbo_tag_from_original_text(&node->v.element.original_tag); - strA.append(node->v.element.original_tag.data, node->v.element.original_tag.length); - ret = create_element(strA.c_str(), attrs); - } - } - if (!strcmp(tag, "script")) - { - parseTextNode = false; - } - if (ret) - { - elements_list child; - for (unsigned int i = 0; i < node->v.element.children.length; i++) - { - child.clear(); - create_node(static_cast<GumboNode*> (node->v.element.children.data[i]), child, parseTextNode); - std::for_each(child.begin(), child.end(), - [&ret](element::ptr& el) - { - ret->appendChild(el); - } - ); - } - elements.push_back(ret); - } - } - break; - case GUMBO_NODE_TEXT: - { - if (!parseTextNode) - { - elements.push_back(std::make_shared<el_text>(node->v.text.text, shared_from_this())); - } - else - { - m_container->split_text(node->v.text.text, - [this, &elements](const char* text) { elements.push_back(std::make_shared<el_text>(text, shared_from_this())); }, - [this, &elements](const char* text) { elements.push_back(std::make_shared<el_space>(text, shared_from_this())); }); - } - } - break; - case GUMBO_NODE_CDATA: - { - element::ptr ret = std::make_shared<el_cdata>(shared_from_this()); - ret->set_data(node->v.text.text); - elements.push_back(ret); - } - break; - case GUMBO_NODE_COMMENT: - { - element::ptr ret = std::make_shared<el_comment>(shared_from_this()); - ret->set_data(node->v.text.text); - elements.push_back(ret); - } - break; - case GUMBO_NODE_WHITESPACE: - { - string str = node->v.text.text; - for (size_t i = 0; i < str.length(); i++) - { - elements.push_back(std::make_shared<el_space>(str.substr(i, 1).c_str(), shared_from_this())); - } - } - break; - default: - break; - } + if (list && !contains(m_media_lists, list)) + m_media_lists.push_back(list); } -void litehtml::document::fix_tables_layout() +void document::fix_tables_layout() { for (const auto& el_ptr : m_tabular_elements) { @@ -856,7 +886,7 @@ void litehtml::document::fix_tables_layout() } } -void litehtml::document::fix_table_children(const std::shared_ptr<render_item>& el_ptr, style_display disp, const char* disp_str) +void document::fix_table_children(const std::shared_ptr<render_item>& el_ptr, style_display disp, const char* disp_str) { std::list<std::shared_ptr<render_item>> tmp; auto first_iter = el_ptr->children().begin(); @@ -926,7 +956,7 @@ void litehtml::document::fix_table_children(const std::shared_ptr<render_item>& } } -void litehtml::document::fix_table_parent(const std::shared_ptr<render_item>& el_ptr, style_display disp, const char* disp_str) +void document::fix_table_parent(const std::shared_ptr<render_item>& el_ptr, style_display disp, const char* disp_str) { auto parent = el_ptr->parent(); @@ -1008,7 +1038,7 @@ void litehtml::document::fix_table_parent(const std::shared_ptr<render_item>& el } } -void litehtml::document::append_children_from_string(element& parent, const char* str) +void document::append_children_from_string(element& parent, const char* str) { // parent must belong to this document if (parent.get_document().get() != this) @@ -1048,7 +1078,7 @@ void litehtml::document::append_children_from_string(element& parent, const char child->compute_styles(); // Now the m_tabular_elements is filled with tabular elements. - // We have to check the tabular elements for missing table elements + // We have to check the tabular elements for missing table elements // and create the anonymous boxes in visual table layout fix_tables_layout(); @@ -1057,10 +1087,12 @@ void litehtml::document::append_children_from_string(element& parent, const char } } -void litehtml::document::dump(dumper& cout) +void document::dump(dumper& cout) { if(m_root_render) { m_root_render->dump(cout); } } + +} // namespace litehtml diff --git a/src/document_container.cpp b/src/document_container.cpp index d3802473e..95f45c62c 100644 --- a/src/document_container.cpp +++ b/src/document_container.cpp @@ -1,23 +1,21 @@ -#include "html.h" +#include "utf8_strings.h" #include "document_container.h" void litehtml::document_container::split_text(const char* text, const std::function<void(const char*)>& on_word, const std::function<void(const char*)>& on_space) { - std::wstring str; - std::wstring str_in = (const wchar_t*)utf8_to_wchar(text); - ucode_t c; - for (size_t i = 0; i < str_in.length(); i++) + std::u32string str; + std::u32string str_in = (const char32_t*)utf8_to_utf32(text); + for (auto c : str_in) { - c = (ucode_t)str_in[i]; if (c <= ' ' && (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f')) { if (!str.empty()) { - on_word(wchar_to_utf8(str.c_str())); + on_word(utf32_to_utf8(str)); str.clear(); } str += c; - on_space(wchar_to_utf8(str.c_str())); + on_space(utf32_to_utf8(str)); str.clear(); } // CJK character range @@ -25,11 +23,11 @@ void litehtml::document_container::split_text(const char* text, const std::funct { if (!str.empty()) { - on_word(wchar_to_utf8(str.c_str())); + on_word(utf32_to_utf8(str)); str.clear(); } str += c; - on_word(wchar_to_utf8(str.c_str())); + on_word(utf32_to_utf8(str)); str.clear(); } else @@ -39,6 +37,6 @@ void litehtml::document_container::split_text(const char* text, const std::funct } if (!str.empty()) { - on_word(wchar_to_utf8(str.c_str())); + on_word(utf32_to_utf8(str)); } } diff --git a/src/el_anchor.cpp b/src/el_anchor.cpp index 372e4afb5..2491c6003 100644 --- a/src/el_anchor.cpp +++ b/src/el_anchor.cpp @@ -1,6 +1,6 @@ -#include "html.h" #include "el_anchor.h" #include "document.h" +#include "document_container.h" litehtml::el_anchor::el_anchor(const std::shared_ptr<litehtml::document>& doc) : html_tag(doc) { diff --git a/src/el_base.cpp b/src/el_base.cpp index baf025653..b2db13e09 100644 --- a/src/el_base.cpp +++ b/src/el_base.cpp @@ -1,10 +1,10 @@ -#include "html.h" #include "el_base.h" #include "document.h" +#include "document_container.h" litehtml::el_base::el_base(const std::shared_ptr<document>& doc) : html_tag(doc) { - + } void litehtml::el_base::parse_attributes() diff --git a/src/el_before_after.cpp b/src/el_before_after.cpp index c7c2d680b..a3f754c2a 100644 --- a/src/el_before_after.cpp +++ b/src/el_before_after.cpp @@ -18,53 +18,54 @@ void litehtml::el_before_after_base::add_style(const style& style) m_children.clear(); const auto& content_property = style.get_property(_content_); - if(content_property.m_type == prop_type_string && !content_property.m_string.empty()) + if(content_property.is<string>() && !content_property.get<string>().empty()) { - int idx = value_index(content_property.m_string, content_property_string); + const string& str = content_property.get<string>(); + int idx = value_index(str, content_property_string); if(idx < 0) { string fnc; string::size_type i = 0; - while(i < content_property.m_string.length() && i != string::npos) + while(i < str.length() && i != string::npos) { - if(content_property.m_string.at(i) == '"' || content_property.m_string.at(i) == '\'') + if(str.at(i) == '"' || str.at(i) == '\'') { - auto chr = content_property.m_string.at(i); + auto chr = str.at(i); fnc.clear(); i++; - string::size_type pos = content_property.m_string.find(chr, i); + string::size_type pos = str.find(chr, i); string txt; if(pos == string::npos) { - txt = content_property.m_string.substr(i); + txt = str.substr(i); i = string::npos; } else { - txt = content_property.m_string.substr(i, pos - i); + txt = str.substr(i, pos - i); i = pos + 1; } add_text(txt); - } else if(content_property.m_string.at(i) == '(') + } else if(str.at(i) == '(') { i++; litehtml::trim(fnc); litehtml::lcase(fnc); - string::size_type pos = content_property.m_string.find(')', i); + string::size_type pos = str.find(')', i); string params; if(pos == string::npos) { - params = content_property.m_string.substr(i); + params = str.substr(i); i = string::npos; } else { - params = content_property.m_string.substr(i, pos - i); + params = str.substr(i, pos - i); i = pos + 1; } add_function(fnc, params); fnc.clear(); } else { - fnc += content_property.m_string.at(i); + fnc += str.at(i); i++; } } @@ -85,7 +86,13 @@ void litehtml::el_before_after_base::add_text( const string& txt ) for(auto chr : txt) { if(chr == '\\' || - !esc.empty() && esc.length() < 5 && (chr >= '0' && chr <= '9' || chr >= 'A' && chr <= 'Z' || chr >= 'a' && chr <= 'z')) + (!esc.empty() && esc.length() < 5 && + ( + (chr >= '0' && chr <= '9') || + (chr >= 'A' && chr <= 'Z') || + (chr >= 'a' && chr <= 'z') + ) + )) { if(!esc.empty() && chr == '\\') { @@ -100,7 +107,7 @@ void litehtml::el_before_after_base::add_text( const string& txt ) word += convert_escape(esc.c_str() + 1); esc.clear(); } - if(isspace(chr)) + if(isspace((unsigned char) chr)) { if(!word.empty()) { @@ -162,6 +169,7 @@ void litehtml::el_before_after_base::add_function( const string& fnc, const stri { string_vector tokens; split_string(params, tokens, ","); + for (auto& str : tokens) trim(str); add_text(get_counters_value(tokens)); } break; @@ -200,9 +208,9 @@ void litehtml::el_before_after_base::add_function( const string& fnc, const stri litehtml::string litehtml::el_before_after_base::convert_escape( const char* txt ) { - char* str_end; - wchar_t u_str[2]; - u_str[0] = (wchar_t) strtol(txt, &str_end, 16); - u_str[1] = 0; - return litehtml::string(litehtml_from_wchar(u_str)); + char* str_end; + char32_t u_str[2]; + u_str[0] = (char32_t) strtol(txt, &str_end, 16); + u_str[1] = 0; + return string(litehtml_from_utf32(u_str)); } diff --git a/src/el_body.cpp b/src/el_body.cpp index 3d35f8f75..899f27f04 100644 --- a/src/el_body.cpp +++ b/src/el_body.cpp @@ -1,4 +1,3 @@ -#include "html.h" #include "el_body.h" #include "document.h" diff --git a/src/el_break.cpp b/src/el_break.cpp index f13b2e3cf..f190d06c1 100644 --- a/src/el_break.cpp +++ b/src/el_break.cpp @@ -1,4 +1,3 @@ -#include "html.h" #include "el_break.h" litehtml::el_break::el_break(const std::shared_ptr<litehtml::document>& doc) : html_tag(doc) diff --git a/src/el_cdata.cpp b/src/el_cdata.cpp index e5948bff1..c3b431b3f 100644 --- a/src/el_cdata.cpp +++ b/src/el_cdata.cpp @@ -1,4 +1,3 @@ -#include "html.h" #include "el_cdata.h" litehtml::el_cdata::el_cdata(const std::shared_ptr<document>& doc) : element(doc) diff --git a/src/el_comment.cpp b/src/el_comment.cpp index 1ec934d04..1a29a5166 100644 --- a/src/el_comment.cpp +++ b/src/el_comment.cpp @@ -1,4 +1,3 @@ -#include "html.h" #include "el_comment.h" litehtml::el_comment::el_comment(const std::shared_ptr<document>& doc) : element(doc) diff --git a/src/el_div.cpp b/src/el_div.cpp index 983d45ab2..a23681cd1 100644 --- a/src/el_div.cpp +++ b/src/el_div.cpp @@ -1,5 +1,5 @@ -#include "html.h" #include "el_div.h" +#include "document.h" litehtml::el_div::el_div(const document::ptr& doc) : html_tag(doc) diff --git a/src/el_font.cpp b/src/el_font.cpp index 3d5f6d9f7..e8e1f7e23 100644 --- a/src/el_font.cpp +++ b/src/el_font.cpp @@ -1,5 +1,5 @@ -#include "html.h" #include "el_font.h" +#include "document.h" litehtml::el_font::el_font(const std::shared_ptr<document>& doc) : html_tag(doc) diff --git a/src/el_image.cpp b/src/el_image.cpp index 9cf8146b4..7ac81193b 100644 --- a/src/el_image.cpp +++ b/src/el_image.cpp @@ -1,15 +1,15 @@ -#include "html.h" #include "el_image.h" #include "render_image.h" +#include "document_container.h" litehtml::el_image::el_image(const document::ptr& doc) : html_tag(doc) { m_css.set_display(display_inline_block); } -void litehtml::el_image::get_content_size( size& sz, int max_width ) +void litehtml::el_image::get_content_size( size& sz, int /*max_width*/ ) { - get_document()->container()->get_image_size(m_src.c_str(), 0, sz); + get_document()->container()->get_image_size(m_src.c_str(), nullptr, sz); } bool litehtml::el_image::is_replaced() const @@ -21,74 +21,39 @@ void litehtml::el_image::parse_attributes() { m_src = get_attr("src", ""); - const char* attr_height = get_attr("height"); - if(attr_height) - { - m_style.add_property(_height_, attr_height); - } - const char* attr_width = get_attr("width"); - if(attr_width) - { - m_style.add_property(_width_, attr_width); - } + // https://html.spec.whatwg.org/multipage/rendering.html#attributes-for-embedded-content-and-images:the-img-element-5 + const char* str = get_attr("width"); + if (str) + map_to_dimension_property(_width_, str); + + str = get_attr("height"); + if (str) + map_to_dimension_property(_height_, str); } void litehtml::el_image::draw(uint_ptr hdc, int x, int y, const position *clip, const std::shared_ptr<render_item> &ri) { + html_tag::draw(hdc, x, y, clip, ri); position pos = ri->pos(); pos.x += x; pos.y += y; - position el_pos = pos; - el_pos += ri->get_paddings(); - el_pos += ri->get_borders(); - - // draw standard background here - if (el_pos.does_intersect(clip)) - { - const background* bg = get_background(); - if (bg) - { - std::vector<background_paint> bg_paint; - init_background_paint(pos, bg_paint, bg, ri); - - get_document()->container()->draw_background(hdc, bg_paint); - } - } - // draw image as background if(pos.does_intersect(clip)) { - if (pos.width > 0 && pos.height > 0) { - background_paint bg; - bg.image = m_src; - bg.clip_box = pos; - bg.origin_box = pos; - bg.border_box = pos; - bg.border_box += ri->get_paddings(); - bg.border_box += ri->get_borders(); - bg.repeat = background_repeat_no_repeat; - bg.image_size.width = pos.width; - bg.image_size.height = pos.height; - bg.border_radius = css().get_borders().radius.calc_percents(bg.border_box.width, bg.border_box.height); - bg.position_x = pos.x; - bg.position_y = pos.y; - get_document()->container()->draw_background(hdc, {bg}); + if (pos.width > 0 && pos.height > 0) + { + background_layer layer; + layer.clip_box = pos; + layer.origin_box = pos; + layer.border_box = pos; + layer.border_box += ri->get_paddings(); + layer.border_box += ri->get_borders(); + layer.repeat = background_repeat_no_repeat; + layer.border_radius = css().get_borders().radius.calc_percents(layer.border_box.width, layer.border_box.height); + get_document()->container()->draw_image(hdc, layer, m_src, {}); } } - - // draw borders - if (el_pos.does_intersect(clip)) - { - position border_box = pos; - border_box += ri->get_paddings(); - border_box += ri->get_borders(); - - borders bdr = css().get_borders(); - bdr.radius = css().get_borders().radius.calc_percents(border_box.width, border_box.height); - - get_document()->container()->draw_borders(hdc, bdr, border_box, is_root()); - } } void litehtml::el_image::compute_styles(bool recursive) @@ -109,12 +74,12 @@ void litehtml::el_image::compute_styles(bool recursive) litehtml::string litehtml::el_image::dump_get_name() { - return "img src=\"" + m_src + "\""; + return "img src=\"" + m_src + "\""; } std::shared_ptr<litehtml::render_item> litehtml::el_image::create_render_item(const std::shared_ptr<render_item>& parent_ri) { - auto ret = std::make_shared<render_item_image>(shared_from_this()); - ret->parent(parent_ri); - return ret; -} + auto ret = std::make_shared<render_item_image>(shared_from_this()); + ret->parent(parent_ri); + return ret; +} \ No newline at end of file diff --git a/src/el_link.cpp b/src/el_link.cpp index aa3bc7659..2783feafc 100644 --- a/src/el_link.cpp +++ b/src/el_link.cpp @@ -1,7 +1,7 @@ -#include "html.h" #include "el_link.h" #include "document.h" - +#include "document_container.h" +#include <cstring> litehtml::el_link::el_link(const std::shared_ptr<document>& doc) : litehtml::html_tag(doc) { diff --git a/src/el_para.cpp b/src/el_para.cpp index 5d7915135..0362be9df 100644 --- a/src/el_para.cpp +++ b/src/el_para.cpp @@ -1,4 +1,3 @@ -#include "html.h" #include "el_para.h" #include "document.h" diff --git a/src/el_script.cpp b/src/el_script.cpp index 4b598a137..857e47fc5 100644 --- a/src/el_script.cpp +++ b/src/el_script.cpp @@ -1,4 +1,3 @@ -#include "html.h" #include "el_script.h" #include "document.h" diff --git a/src/el_space.cpp b/src/el_space.cpp index f5e3818ca..879e04bf9 100644 --- a/src/el_space.cpp +++ b/src/el_space.cpp @@ -35,10 +35,10 @@ bool litehtml::el_space::is_break() const bool litehtml::el_space::is_space() const { - return true; + return true; } litehtml::string litehtml::el_space::dump_get_name() { - return "space: \"" + get_escaped_string(m_text) + "\""; + return "space: \"" + get_escaped_string(m_text) + "\""; } diff --git a/src/el_style.cpp b/src/el_style.cpp index cc2f0bab6..f0fbc66fa 100644 --- a/src/el_style.cpp +++ b/src/el_style.cpp @@ -1,4 +1,3 @@ -#include "html.h" #include "el_style.h" #include "document.h" @@ -21,8 +20,17 @@ void litehtml::el_style::parse_attributes() bool litehtml::el_style::appendChild(const ptr &el) { - m_children.push_back(el); - return true; + if(el && el->is_text()) + { + m_children.push_back(el); + return true; + } + return false; +} + +void litehtml::el_style::compute_styles(bool /* recursive */) +{ + css_w().set_display(display_none); } litehtml::string_id litehtml::el_style::tag() const diff --git a/src/el_table.cpp b/src/el_table.cpp index 0bb864843..c3ce2a9ff 100644 --- a/src/el_table.cpp +++ b/src/el_table.cpp @@ -1,55 +1,47 @@ -#include "html.h" #include "el_table.h" #include "document.h" -#include "iterators.h" - -litehtml::el_table::el_table(const std::shared_ptr<document>& doc) : html_tag(doc) +namespace litehtml { -} - -bool litehtml::el_table::appendChild(const element::ptr& el) -{ - if(!el) return false; - if( el->tag() == _tbody_ || - el->tag() == _thead_ || - el->tag() == _tfoot_ || - el->tag() == _caption_) + el_table::el_table(const shared_ptr<document>& doc) : + html_tag(doc) { - return html_tag::appendChild(el); } - return false; -} -void litehtml::el_table::parse_attributes() -{ - const char* str = get_attr("width"); - if(str) + bool el_table::appendChild(const element::ptr& el) { - m_style.add_property(_width_, str); + if(!el) return false; + if(el->tag() == _tbody_ || el->tag() == _thead_ || el->tag() == _tfoot_ || el->tag() == _caption_) + { + return html_tag::appendChild(el); + } + return false; } - str = get_attr("cellspacing"); - if(str) - { - string val = str; - val += " "; - val += str; - m_style.add_property(_border_spacing_, val); - } - - str = get_attr("border"); - if(str) + void el_table::parse_attributes() { - m_style.add_property(_border_width_, str); - } + // https://html.spec.whatwg.org/multipage/rendering.html#tables-2:attr-table-width + const char* str = get_attr("width"); + if(str) map_to_dimension_property_ignoring_zero(_width_, str); - str = get_attr("bgcolor"); - if (str) - { - m_style.add_property(_background_color_, str, "", false, get_document()->container()); + // https://html.spec.whatwg.org/multipage/rendering.html#tables-2:attr-table-height + str = get_attr("height"); + if(str) map_to_dimension_property(_height_, str); + + // https://html.spec.whatwg.org/multipage/rendering.html#tables-2:attr-table-cellspacing + str = get_attr("cellspacing"); + if(str) map_to_pixel_length_property(_border_spacing_, str); + + // https://html.spec.whatwg.org/multipage/rendering.html#tables-2:attr-table-border + str = get_attr("border"); + if(str) map_to_pixel_length_property_with_default_value(_border_width_, str, 1); + + // https://html.spec.whatwg.org/multipage/rendering.html#tables-2:attr-background + str = get_attr("bgcolor"); + if(str) { m_style.add_property(_background_color_, str, "", false, get_document()->container()); } + + html_tag::parse_attributes(); } - html_tag::parse_attributes(); -} +} // namespace litehtml diff --git a/src/el_td.cpp b/src/el_td.cpp index 679d9212a..f665c7c09 100644 --- a/src/el_td.cpp +++ b/src/el_td.cpp @@ -1,19 +1,26 @@ -#include "html.h" #include "el_td.h" +#include "document.h" - -litehtml::el_td::el_td(const std::shared_ptr<document>& doc) : html_tag(doc) +namespace litehtml { +el_td::el_td(const shared_ptr<document>& doc) : html_tag(doc) +{ } -void litehtml::el_td::parse_attributes() +void el_td::parse_attributes() { + // https://html.spec.whatwg.org/multipage/rendering.html#tables-2:attr-tdth-width const char* str = get_attr("width"); - if(str) - { - m_style.add_property(_width_, str); - } + if (str) + map_to_dimension_property_ignoring_zero(_width_, str); + + // https://html.spec.whatwg.org/multipage/rendering.html#tables-2:attr-tdth-height + str = get_attr("height"); + if (str) + map_to_dimension_property_ignoring_zero(_height_, str); + + // https://html.spec.whatwg.org/multipage/rendering.html#tables-2:attr-background str = get_attr("background"); if(str) { @@ -22,11 +29,6 @@ void litehtml::el_td::parse_attributes() url += "')"; m_style.add_property(_background_image_, url); } - str = get_attr("align"); - if(str) - { - m_style.add_property(_text_align_, str); - } str = get_attr("bgcolor"); if (str) @@ -34,10 +36,19 @@ void litehtml::el_td::parse_attributes() m_style.add_property(_background_color_, str, "", false, get_document()->container()); } + str = get_attr("align"); + if(str) + { + m_style.add_property(_text_align_, str); + } + str = get_attr("valign"); if(str) { m_style.add_property(_vertical_align_, str); } + html_tag::parse_attributes(); } + +} // namespace litehtml \ No newline at end of file diff --git a/src/el_text.cpp b/src/el_text.cpp index a22224a35..042da3265 100644 --- a/src/el_text.cpp +++ b/src/el_text.cpp @@ -1,6 +1,7 @@ #include "html.h" #include "el_text.h" #include "render_item.h" +#include "document_container.h" litehtml::el_text::el_text(const char* text, const document::ptr& doc) : element(doc) { @@ -10,10 +11,10 @@ litehtml::el_text::el_text(const char* text, const document::ptr& doc) : element } m_use_transformed = false; m_draw_spaces = true; - css_w().set_display(display_inline_text); + css_w().set_display(display_inline_text); } -void litehtml::el_text::get_content_size( size& sz, int max_width ) +void litehtml::el_text::get_content_size( size& sz, int /*max_width*/ ) { sz = m_size; } @@ -23,19 +24,19 @@ void litehtml::el_text::get_text( string& text ) text += m_text; } -void litehtml::el_text::compute_styles(bool recursive) +void litehtml::el_text::compute_styles(bool /*recursive*/) { - element::ptr el_parent = parent(); - if (el_parent) - { - css_w().set_line_height(el_parent->css().get_line_height()); - css_w().set_font(el_parent->css().get_font()); - css_w().set_font_metrics(el_parent->css().get_font_metrics()); - css_w().set_white_space(el_parent->css().get_white_space()); + element::ptr el_parent = parent(); + if (el_parent) + { + css_w().line_height_w() = el_parent->css().line_height(); + css_w().set_font(el_parent->css().get_font()); + css_w().set_font_metrics(el_parent->css().get_font_metrics()); + css_w().set_white_space(el_parent->css().get_white_space()); css_w().set_text_transform(el_parent->css().get_text_transform()); - } - css_w().set_display(display_inline_text); - css_w().set_float(float_none); + } + css_w().set_display(display_inline_text); + css_w().set_float(float_none); if(m_css.get_text_transform() != text_transform_none) { @@ -47,21 +48,21 @@ void litehtml::el_text::compute_styles(bool recursive) m_use_transformed = false; } - element::ptr p = parent(); - while(p && p->css().get_display() == display_inline) - { - if(p->css().get_position() == element_position_relative) - { - css_w().set_offsets(p->css().get_offsets()); - css_w().set_position(element_position_relative); - break; - } - p = p->parent(); - } - if(p) - { - css_w().set_position(element_position_static); - } + element::ptr p = parent(); + while(p && p->css().get_display() == display_inline) + { + if(p->css().get_position() == element_position_relative) + { + css_w().set_offsets(p->css().get_offsets()); + css_w().set_position(element_position_relative); + break; + } + p = p->parent(); + } + if(p) + { + css_w().set_position(element_position_static); + } if(is_white_space()) { @@ -86,7 +87,7 @@ void litehtml::el_text::compute_styles(bool recursive) if (el_parent) { font = el_parent->css().get_font(); - fm = el_parent->css().get_font_metrics(); + fm = el_parent->css().get_font_metrics(); } if(is_break() || !font) { @@ -131,10 +132,10 @@ void litehtml::el_text::draw(uint_ptr hdc, int x, int y, const position *clip, c litehtml::string litehtml::el_text::dump_get_name() { - return "text: \"" + get_escaped_string(m_text) + "\""; + return "text: \"" + get_escaped_string(m_text) + "\""; } std::vector<std::tuple<litehtml::string, litehtml::string>> litehtml::el_text::dump_get_attrs() { - return std::vector<std::tuple<string, string>>(); + return {}; } diff --git a/src/el_title.cpp b/src/el_title.cpp index f0e42311b..7752d49a8 100644 --- a/src/el_title.cpp +++ b/src/el_title.cpp @@ -1,6 +1,6 @@ -#include "html.h" #include "el_title.h" #include "document.h" +#include "document_container.h" litehtml::el_title::el_title(const std::shared_ptr<document>& doc) : html_tag(doc) { diff --git a/src/el_tr.cpp b/src/el_tr.cpp index f10997654..78c53f4e8 100644 --- a/src/el_tr.cpp +++ b/src/el_tr.cpp @@ -1,15 +1,19 @@ -#include "html.h" #include "el_tr.h" +#include "document.h" litehtml::el_tr::el_tr(const std::shared_ptr<document>& doc) : html_tag(doc) { - } void litehtml::el_tr::parse_attributes() { - const char* str = get_attr("align"); + // https://html.spec.whatwg.org/multipage/rendering.html#tables-2:attr-tr-height + const char* str = get_attr("height"); + if (str) + map_to_dimension_property(_height_, str); + + str = get_attr("align"); if(str) { m_style.add_property(_text_align_, str); diff --git a/src/element.cpp b/src/element.cpp index 16b4b5878..8672b9de7 100644 --- a/src/element.cpp +++ b/src/element.cpp @@ -256,7 +256,7 @@ bool element::find_styles_changes( position::vector& redraw_boxes) return ret; } -element::ptr element::_add_before_after(int type, const style& style) +element::ptr element::_add_before_after(int type, const style& /*style*/) { element::ptr el; if(type == 0) @@ -310,14 +310,14 @@ litehtml::string litehtml::element::get_counter_value(const string& counter_name string litehtml::element::get_counters_value(const string_vector& parameters) { - string result = ""; + string result; if (parameters.size() >= 2) { const string_id counter_name_id = _id(parameters[0]); string delims = parameters[1]; litehtml::trim(delims, "\"'"); string_vector values; - + element::ptr current = shared_from_this(); while (current != nullptr) { @@ -328,7 +328,7 @@ string litehtml::element::get_counters_value(const string_vector& parameters) current = current->parent(); } if (values.empty()) { - // if no counter is found, instanciate one with value '0' + // if no counter is found, instance one with value '0' shared_from_this()->m_counter_values[counter_name_id] = 0; result = "0"; } @@ -362,7 +362,7 @@ bool litehtml::element::find_counter(const string_id& counter_name_id, std::map< } current = current->parent(); } - + return false; } @@ -383,10 +383,10 @@ std::vector<element::ptr> litehtml::element::get_siblings_before() const void litehtml::element::parse_counter_tokens(const string_vector& tokens, const int default_value, std::function<void(const string_id&, const int)> handler) const { int pos = 0; - while (pos < tokens.size()) { - string name = tokens[pos]; + while (pos < (int) tokens.size()) { + const string& name = tokens[pos]; int value = default_value; - if (pos < tokens.size() - 1 && litehtml::is_number(tokens[pos + 1], 0)) { + if (pos < (int) tokens.size() - 1 && litehtml::is_number(tokens[pos + 1], false)) { value = atoi(tokens[pos + 1].c_str()); pos += 2; } @@ -414,33 +414,33 @@ void litehtml::element::reset_counter(const string_id& counter_name_id, const in m_counter_values[counter_name_id] = value; } -const background* element::get_background(bool own_only) LITEHTML_RETURN_FUNC(nullptr) -void element::add_style( const style& style) LITEHTML_EMPTY_FUNC -void element::select_all(const css_selector& selector, elements_list& res) LITEHTML_EMPTY_FUNC -elements_list element::select_all(const css_selector& selector) LITEHTML_RETURN_FUNC(elements_list()) -elements_list element::select_all(const string& selector) LITEHTML_RETURN_FUNC(elements_list()) -element::ptr element::select_one( const css_selector& selector ) LITEHTML_RETURN_FUNC(nullptr) -element::ptr element::select_one( const string& selector ) LITEHTML_RETURN_FUNC(nullptr) -element::ptr element::find_adjacent_sibling(const element::ptr& el, const css_selector& selector, bool apply_pseudo /*= true*/, bool* is_pseudo /*= 0*/) LITEHTML_RETURN_FUNC(nullptr) -element::ptr element::find_sibling(const element::ptr& el, const css_selector& selector, bool apply_pseudo /*= true*/, bool* is_pseudo /*= 0*/) LITEHTML_RETURN_FUNC(nullptr) -bool element::is_nth_last_child(const element::ptr& el, int num, int off, bool of_type) const LITEHTML_RETURN_FUNC(false) -bool element::is_nth_child(const element::ptr&, int num, int off, bool of_type) const LITEHTML_RETURN_FUNC(false) -bool element::is_only_child(const element::ptr& el, bool of_type) const LITEHTML_RETURN_FUNC(false) -void element::get_content_size( size& sz, int max_width ) LITEHTML_EMPTY_FUNC -bool element::appendChild(const ptr &el) LITEHTML_RETURN_FUNC(false) -bool element::removeChild(const ptr &el) LITEHTML_RETURN_FUNC(false) +const background* element::get_background(bool /*own_only*/) LITEHTML_RETURN_FUNC(nullptr) +void element::add_style( const style& /*style*/) LITEHTML_EMPTY_FUNC +void element::select_all(const css_selector& /*selector*/, elements_list& /*res*/) LITEHTML_EMPTY_FUNC +elements_list element::select_all(const css_selector& /*selector*/) LITEHTML_RETURN_FUNC(elements_list()) +elements_list element::select_all(const string& /*selector*/) LITEHTML_RETURN_FUNC(elements_list()) +element::ptr element::select_one( const css_selector& /*selector*/ ) LITEHTML_RETURN_FUNC(nullptr) +element::ptr element::select_one( const string& /*selector*/ ) LITEHTML_RETURN_FUNC(nullptr) +element::ptr element::find_adjacent_sibling(const element::ptr& /*el*/, const css_selector& /*selector*/, bool /*apply_pseudo*/ /*= true*/, bool* /*is_pseudo*/ /*= 0*/) LITEHTML_RETURN_FUNC(nullptr) +element::ptr element::find_sibling(const element::ptr& /*el*/, const css_selector& /*selector*/, bool /*apply_pseudo*/ /*= true*/, bool* /*is_pseudo*/ /*= 0*/) LITEHTML_RETURN_FUNC(nullptr) +bool element::is_nth_last_child(const element::ptr& /*el*/, int /*num*/, int /*off*/, bool /*of_type*/, const css_selector::vector& /*selector_list*/) const LITEHTML_RETURN_FUNC(false) +bool element::is_nth_child(const element::ptr&, int /*num*/, int /*off*/, bool /*of_type*/, const css_selector::vector& /*selector_list*/) const LITEHTML_RETURN_FUNC(false) +bool element::is_only_child(const element::ptr& /*el*/, bool /*of_type*/) const LITEHTML_RETURN_FUNC(false) +void element::get_content_size( size& /*sz*/, int /*max_width*/ ) LITEHTML_EMPTY_FUNC +bool element::appendChild(const ptr &/*el*/) LITEHTML_RETURN_FUNC(false) +bool element::removeChild(const ptr &/*el*/) LITEHTML_RETURN_FUNC(false) void element::clearRecursive() LITEHTML_EMPTY_FUNC string_id element::id() const LITEHTML_RETURN_FUNC(empty_id) string_id element::tag() const LITEHTML_RETURN_FUNC(empty_id) const char* element::get_tagName() const LITEHTML_RETURN_FUNC("") -void element::set_tagName( const char* tag ) LITEHTML_EMPTY_FUNC -void element::set_data( const char* data ) LITEHTML_EMPTY_FUNC -void element::set_attr( const char* name, const char* val ) LITEHTML_EMPTY_FUNC -void element::apply_stylesheet( const litehtml::css& stylesheet ) LITEHTML_EMPTY_FUNC +void element::set_tagName( const char* /*tag*/ ) LITEHTML_EMPTY_FUNC +void element::set_data( const char* /*data*/ ) LITEHTML_EMPTY_FUNC +void element::set_attr( const char* /*name*/, const char* /*val*/ ) LITEHTML_EMPTY_FUNC +void element::apply_stylesheet( const litehtml::css& /*stylesheet*/ ) LITEHTML_EMPTY_FUNC void element::refresh_styles() LITEHTML_EMPTY_FUNC void element::on_click() LITEHTML_EMPTY_FUNC -void element::compute_styles( bool recursive ) LITEHTML_EMPTY_FUNC -const char* element::get_attr( const char* name, const char* def /*= 0*/ ) const LITEHTML_RETURN_FUNC(def) +void element::compute_styles( bool /*recursive*/ ) LITEHTML_EMPTY_FUNC +const char* element::get_attr( const char* /*name*/, const char* def /*= 0*/ ) const LITEHTML_RETURN_FUNC(def) bool element::is_white_space() const LITEHTML_RETURN_FUNC(false) bool element::is_space() const LITEHTML_RETURN_FUNC(false) bool element::is_comment() const LITEHTML_RETURN_FUNC(false) @@ -451,28 +451,18 @@ bool element::is_text() const LITEHTML_RETURN_FUNC(false) bool element::on_mouse_over() LITEHTML_RETURN_FUNC(false) bool element::on_mouse_leave() LITEHTML_RETURN_FUNC(false) bool element::on_lbutton_down() LITEHTML_RETURN_FUNC(false) -bool element::on_lbutton_up() LITEHTML_RETURN_FUNC(false) -bool element::set_pseudo_class( string_id cls, bool add ) LITEHTML_RETURN_FUNC(false) -bool element::set_class( const char* pclass, bool add ) LITEHTML_RETURN_FUNC(false) +bool element::on_lbutton_up(const bool /*is_click*/) LITEHTML_RETURN_FUNC(false) +bool element::set_pseudo_class( string_id /*cls*/, bool /*add*/ ) LITEHTML_RETURN_FUNC(false) +bool element::set_class( const char* /*pclass*/, bool /*add*/ ) LITEHTML_RETURN_FUNC(false) bool element::is_replaced() const LITEHTML_RETURN_FUNC(false) -void element::draw(uint_ptr hdc, int x, int y, const position *clip, const std::shared_ptr<render_item> &ri) LITEHTML_EMPTY_FUNC -void element::draw_background(uint_ptr hdc, int x, int y, const position *clip, const std::shared_ptr<render_item> &ri) LITEHTML_EMPTY_FUNC -int element::get_enum_property (string_id name, bool inherited, int defval, uint_ptr css_properties_member_offset) const LITEHTML_RETURN_FUNC(0) -int element::get_int_property (string_id name, bool inherited, int defval, uint_ptr css_properties_member_offset) const LITEHTML_RETURN_FUNC(0) -css_length element::get_length_property (string_id name, bool inherited, css_length defval, uint_ptr css_properties_member_offset) const LITEHTML_RETURN_FUNC(0) -web_color element::get_color_property (string_id name, bool inherited, web_color defval, uint_ptr css_properties_member_offset) const LITEHTML_RETURN_FUNC(web_color()) -string element::get_string_property (string_id name, bool inherited, const string& defval, uint_ptr css_properties_member_offset) const LITEHTML_RETURN_FUNC("") -float element::get_number_property (string_id name, bool inherited, float defval, uint_ptr css_properties_member_offset) const LITEHTML_RETURN_FUNC(0) -string_vector element::get_string_vector_property (string_id name, bool inherited, const string_vector& default_value, uint_ptr css_properties_member_offset) const LITEHTML_RETURN_FUNC({}) -int_vector element::get_int_vector_property (string_id name, bool inherited, const int_vector& default_value, uint_ptr css_properties_member_offset) const LITEHTML_RETURN_FUNC({}) -length_vector element::get_length_vector_property (string_id name, bool inherited, const length_vector& default_value, uint_ptr css_properties_member_offset) const LITEHTML_RETURN_FUNC({}) -size_vector element::get_size_vector_property (string_id name, bool inherited, const size_vector& default_value, uint_ptr css_properties_member_offset) const LITEHTML_RETURN_FUNC({}) -string element::get_custom_property (string_id name, const string& defval) const LITEHTML_RETURN_FUNC("") -void element::get_text( string& text ) LITEHTML_EMPTY_FUNC +void element::draw(uint_ptr /*hdc*/, int /*x*/, int /*y*/, const position */*clip*/, const std::shared_ptr<render_item> &/*ri*/) LITEHTML_EMPTY_FUNC +void element::draw_background(uint_ptr /*hdc*/, int /*x*/, int /*y*/, const position */*clip*/, const std::shared_ptr<render_item> &/*ri*/) LITEHTML_EMPTY_FUNC +void element::get_text( string& /*text*/ ) LITEHTML_EMPTY_FUNC void element::parse_attributes() LITEHTML_EMPTY_FUNC -int element::select(const string& selector) LITEHTML_RETURN_FUNC(select_no_match) -int element::select(const css_selector& selector, bool apply_pseudo) LITEHTML_RETURN_FUNC(select_no_match) -int element::select( const css_element_selector& selector, bool apply_pseudo /*= true*/ ) LITEHTML_RETURN_FUNC(select_no_match) -element::ptr element::find_ancestor(const css_selector& selector, bool apply_pseudo, bool* is_pseudo) LITEHTML_RETURN_FUNC(nullptr) +int element::select(const css_selector::vector& /*selector_list*/, bool /*apply_pseudo*/) LITEHTML_RETURN_FUNC(select_no_match) +int element::select(const string& /*selector*/) LITEHTML_RETURN_FUNC(select_no_match) +int element::select(const css_selector& /*selector*/, bool /*apply_pseudo*/) LITEHTML_RETURN_FUNC(select_no_match) +int element::select(const css_element_selector& /*selector*/, bool /*apply_pseudo*/) LITEHTML_RETURN_FUNC(select_no_match) +element::ptr element::find_ancestor(const css_selector& /*selector*/, bool /*apply_pseudo*/, bool* /*is_pseudo*/) LITEHTML_RETURN_FUNC(nullptr) -} // namespace litehtml \ No newline at end of file +} // namespace litehtml diff --git a/src/encodings.cpp b/src/encodings.cpp new file mode 100644 index 000000000..f56c0f10f --- /dev/null +++ b/src/encodings.cpp @@ -0,0 +1,2039 @@ +#include "html.h" +#include "os_types.h" +#include "utf8_strings.h" +#include "encodings.h" +#include <cassert> + +#define out +#define inout +#define countof(a) int(sizeof(a)/sizeof((a)[0])) + +namespace litehtml +{ + +// https://encoding.spec.whatwg.org/#error-mode +enum class error_mode +{ + replacement, // replace bad input bytes with 0xFFFD - this one is used in decode + fatal, // abort on first bad input byte + html // replace bad input bytes with "&#%d;" % byte, see process_an_item +}; + +struct decoder +{ + using ptr = shared_ptr<decoder>; + + // invalid value of code_point or pointer (in std terminology, pointer is an offset into index table). + // it is called null to match the standard. + enum { null = -2 }; + static_assert(null != 0 && null != EOF); + + // https://encoding.spec.whatwg.org/#index-code-point + template<int N> + static int index_code_point(int pointer, int (&index)[N]) + { + if (pointer >= 0 && pointer < N) + return index[pointer]; + + return null; + } + + // https://encoding.spec.whatwg.org/#encoders-and-decoders + enum result + { + result_finished, // no more bytes in input + + result_error, // decoding error occurred, 0xFFFD will be emitted (see process_an_item) + + // decoder handler: need more bytes to compose a character + // process_an_item: keep going (don't exit the loop in process_a_queue) + result_continue, + + // handler returns one or two UTF-32 codepoints through ch parameter (only Big5 decoder can return two codepoints) + result_codepoint + }; + + result process_a_queue(string& input, string& output, error_mode mode); + result process_an_item(string& input, int& input_index, string& output, error_mode mode); + + // NOTE: input can be modified by GB18030, ISO-2022-JP and UTF-16 decoders (search for "input.insert") + virtual result handler(inout string& input, inout int& index, out int ch[2]) = 0; +}; + +// https://encoding.spec.whatwg.org/#concept-encoding-run +decoder::result decoder::process_a_queue(string& input, string& output, error_mode mode) +{ + int index = 0; + while (true) + { + // NOTE: we read byte from input in decoder handlers, not here (standard prescribes to do it here). + auto result = process_an_item(input, index, output, mode); + if (result != result_continue) return result; + } +} + +// https://encoding.spec.whatwg.org/#concept-encoding-process +// ioQueue is represented by the pair {input, index}, index points to the head of the queue. +// Some decoders may modify input (so they're not just incrementing the index). +// "item" is the current input byte, input[index]. +decoder::result decoder::process_an_item(string& input, int& index, string& output, error_mode mode) +{ + // 1. Assert: if encoderDecoder is an encoder instance, mode is not "replacement". + + // 2. Assert: if encoderDecoder is a decoder instance, mode is not "html". + assert(mode != error_mode::html); + + // 3. Assert: if encoderDecoder is an encoder instance, item is not a surrogate. + + // 4. Let result be the result of running encoderDecoder’s handler on input and item. + int ch[2] = {0}; // NOTE: only Big5 decoder can return two codepoints + auto result = handler(input, index, ch); + + // 5. If result is finished: + if (result == result_finished) + { + return result; + } + // 6. Otherwise, if result is one or more items: + else if (result == result_codepoint) + { + // 1. Assert: if encoderDecoder is a decoder instance, result does not contain any surrogates. + assert(!is_surrogate(*ch) && !is_surrogate(ch[1])); + + // 2. Push result to output. + append_char(output, *ch); + if (ch[1]) append_char(output, ch[1]); + } + // 7. Otherwise, if result is an error, switch on mode and run the associated steps: + else if (result == result_error) + { + switch (mode) + { + case error_mode::replacement: + append_char(output, 0xFFFD); + break; + case error_mode::html: + { + char buf[16] = {0}; + t_itoa(*ch, buf, 16, 10); + output += "&#"; + output += buf; + output += ';'; + break; + } + case error_mode::fatal: + return result; + } + } + // 8. + return result_continue; +} + +// https://encoding.spec.whatwg.org/#bom-sniff +encoding bom_sniff(const string& str) +{ + if (str.substr(0, 3) == "\xEF\xBB\xBF") return encoding::utf_8; + if (str.substr(0, 2) == "\xFE\xFF") return encoding::utf_16be; + if (str.substr(0, 2) == "\xFF\xFE") return encoding::utf_16le; + return encoding::null; +} + +decoder::ptr get_decoder(encoding _encoding); + +// https://encoding.spec.whatwg.org/#decode +// input is copied because it can be modified by GB18030, ISO-2022-JP and UTF-16 decoders +void decode(string input, encoding _encoding, string& output) +{ + // 1. + encoding bom_encoding = bom_sniff(input); + + // 2. + if (bom_encoding != encoding::null) + { + _encoding = bom_encoding; + int len = (_encoding == encoding::utf_8 ? 3 : 2); + input.erase(0, len); // remove BOM + } + + // 3. + auto decoder = get_decoder(_encoding); + decoder->process_a_queue(input, output, error_mode::replacement); +} + +string decode(string input, encoding encoding) +{ + string output; + decode(input, encoding, output); + return output; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +struct utf_8_decoder final : decoder +{ + int m_code_point = 0; + int m_bytes_seen = 0; + int m_bytes_needed = 0; + int m_lower_boundary = 0x80; + int m_upper_boundary = 0xBF; + + result handler(string& input, int& index, int ch[2]) override; +}; + +// https://encoding.spec.whatwg.org/#utf-8-decoder +decoder::result utf_8_decoder::handler(inout string& input, inout int& index, out int ch[2]) +{ + int b = index == (int)input.size() ? EOF : (byte)input[index++]; // read byte from input + + // 1. If byte is end-of-queue and UTF-8 bytes needed is not 0, set UTF-8 bytes needed to 0 and return error. + if (b == EOF && m_bytes_needed != 0) + { + m_bytes_needed = 0; + return result_error; + } + + // 2. If byte is end-of-queue, return finished. + if (b == EOF) + return result_finished; + + // 3. + if (m_bytes_needed == 0) + { + if (b >= 0 && b <= 0x7F) + { + // Return a code point whose value is byte. + *ch = b; + return result_codepoint; + } + else if (b >= 0xC2 && b <= 0xDF) + { + m_bytes_needed = 1; + m_code_point = b & 0x1F; + } + else if (b >= 0xE0 && b <= 0xEF) + { + if (b == 0xE0) m_lower_boundary = 0xA0; + if (b == 0xED) m_upper_boundary = 0x9F; + m_bytes_needed = 2; + m_code_point = b & 0xF; + } + else if (b >= 0xF0 && b <= 0xF4) + { + if (b == 0xF0) m_lower_boundary = 0x90; + if (b == 0xF4) m_upper_boundary = 0x8F; + m_bytes_needed = 3; + m_code_point = b & 0x7; + } + else + return result_error; + + return result_continue; + } + + // 4. + if (!(b >= m_lower_boundary && b <= m_upper_boundary)) + { + // 1. + m_code_point = 0; + m_bytes_needed = 0; + m_bytes_seen = 0; + m_lower_boundary = 0x80; + m_upper_boundary = 0xBF; + + // 2. Restore byte to ioQueue. + index--; + + // 3. + return result_error; + } + + // 5. + m_lower_boundary = 0x80; + m_upper_boundary = 0xBF; + + // 6. + m_code_point = (m_code_point << 6) | (b & 0x3F); + + // 7. + m_bytes_seen++; + + // 8. + if (m_bytes_seen != m_bytes_needed) + return result_continue; + + // 9. + int codepoint = m_code_point; + + // 10. + m_code_point = 0; + m_bytes_needed = 0; + m_bytes_seen = 0; + + // 11. Return a code point whose value is code point. + *ch = codepoint; + return result_codepoint; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +struct single_byte_decoder final : decoder +{ + int* m_index; // https://encoding.spec.whatwg.org/#index-single-byte + + single_byte_decoder(encoding _encoding) + { + m_index = m_indexes[(int)_encoding - (int)encoding::ibm866]; + } + + result handler(string& input, int& index, int ch[2]) override; + + static int* m_indexes[(int)encoding::x_mac_cyrillic - (int)encoding::ibm866 + 1]; + + static int ibm866_index[128]; + static int iso_8859_2_index[128]; + static int iso_8859_3_index[128]; + static int iso_8859_4_index[128]; + static int iso_8859_5_index[128]; + static int iso_8859_6_index[128]; + static int iso_8859_7_index[128]; + static int iso_8859_8_index[128]; + static int iso_8859_10_index[128]; + static int iso_8859_13_index[128]; + static int iso_8859_14_index[128]; + static int iso_8859_15_index[128]; + static int iso_8859_16_index[128]; + static int koi8_r_index[128]; + static int koi8_u_index[128]; + static int macintosh_index[128]; + static int windows_874_index[128]; + static int windows_1250_index[128]; + static int windows_1251_index[128]; + static int windows_1252_index[128]; + static int windows_1253_index[128]; + static int windows_1254_index[128]; + static int windows_1255_index[128]; + static int windows_1256_index[128]; + static int windows_1257_index[128]; + static int windows_1258_index[128]; + static int x_mac_cyrillic_index[128]; +}; + +// https://encoding.spec.whatwg.org/#legacy-single-byte-encodings +// Data is from https://encoding.spec.whatwg.org/indexes.json +// NOTE: iso_2022_jp_katakana_index is used only in ISO-2022-JP encoder, decoder doesn't need it. +int single_byte_decoder::ibm866_index[128] = {1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,9617,9618,9619,9474,9508,9569,9570,9558,9557,9571,9553,9559,9565,9564,9563,9488,9492,9524,9516,9500,9472,9532,9566,9567,9562,9556,9577,9574,9568,9552,9580,9575,9576,9572,9573,9561,9560,9554,9555,9579,9578,9496,9484,9608,9604,9612,9616,9600,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,1025,1105,1028,1108,1031,1111,1038,1118,176,8729,183,8730,8470,164,9632,160}; +int single_byte_decoder::iso_8859_2_index[128] = {128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,260,728,321,164,317,346,167,168,352,350,356,377,173,381,379,176,261,731,322,180,318,347,711,184,353,351,357,378,733,382,380,340,193,194,258,196,313,262,199,268,201,280,203,282,205,206,270,272,323,327,211,212,336,214,215,344,366,218,368,220,221,354,223,341,225,226,259,228,314,263,231,269,233,281,235,283,237,238,271,273,324,328,243,244,337,246,247,345,367,250,369,252,253,355,729}; +int single_byte_decoder::iso_8859_3_index[128] = {128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,294,728,163,164,null,292,167,168,304,350,286,308,173,null,379,176,295,178,179,180,181,293,183,184,305,351,287,309,189,null,380,192,193,194,null,196,266,264,199,200,201,202,203,204,205,206,207,null,209,210,211,212,288,214,215,284,217,218,219,220,364,348,223,224,225,226,null,228,267,265,231,232,233,234,235,236,237,238,239,null,241,242,243,244,289,246,247,285,249,250,251,252,365,349,729}; +int single_byte_decoder::iso_8859_4_index[128] = {128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,260,312,342,164,296,315,167,168,352,274,290,358,173,381,175,176,261,731,343,180,297,316,711,184,353,275,291,359,330,382,331,256,193,194,195,196,197,198,302,268,201,280,203,278,205,206,298,272,325,332,310,212,213,214,215,216,370,218,219,220,360,362,223,257,225,226,227,228,229,230,303,269,233,281,235,279,237,238,299,273,326,333,311,244,245,246,247,248,371,250,251,252,361,363,729}; +int single_byte_decoder::iso_8859_5_index[128] = {128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,1025,1026,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,173,1038,1039,1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,8470,1105,1106,1107,1108,1109,1110,1111,1112,1113,1114,1115,1116,167,1118,1119}; +int single_byte_decoder::iso_8859_6_index[128] = {128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,null,null,null,164,null,null,null,null,null,null,null,1548,173,null,null,null,null,null,null,null,null,null,null,null,null,null,1563,null,null,null,1567,null,1569,1570,1571,1572,1573,1574,1575,1576,1577,1578,1579,1580,1581,1582,1583,1584,1585,1586,1587,1588,1589,1590,1591,1592,1593,1594,null,null,null,null,null,1600,1601,1602,1603,1604,1605,1606,1607,1608,1609,1610,1611,1612,1613,1614,1615,1616,1617,1618,null,null,null,null,null,null,null,null,null,null,null,null,null}; +int single_byte_decoder::iso_8859_7_index[128] = {128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,8216,8217,163,8364,8367,166,167,168,169,890,171,172,173,null,8213,176,177,178,179,900,901,902,183,904,905,906,187,908,189,910,911,912,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,null,931,932,933,934,935,936,937,938,939,940,941,942,943,944,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,962,963,964,965,966,967,968,969,970,971,972,973,974,null}; +int single_byte_decoder::iso_8859_8_index[128] = {128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,null,162,163,164,165,166,167,168,169,215,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,247,187,188,189,190,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,8215,1488,1489,1490,1491,1492,1493,1494,1495,1496,1497,1498,1499,1500,1501,1502,1503,1504,1505,1506,1507,1508,1509,1510,1511,1512,1513,1514,null,null,8206,8207,null}; +int single_byte_decoder::iso_8859_10_index[128] = {128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,260,274,290,298,296,310,167,315,272,352,358,381,173,362,330,176,261,275,291,299,297,311,183,316,273,353,359,382,8213,363,331,256,193,194,195,196,197,198,302,268,201,280,203,278,205,206,207,208,325,332,211,212,213,214,360,216,370,218,219,220,221,222,223,257,225,226,227,228,229,230,303,269,233,281,235,279,237,238,239,240,326,333,243,244,245,246,361,248,371,250,251,252,253,254,312}; +int single_byte_decoder::iso_8859_13_index[128] = {128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,8221,162,163,164,8222,166,167,216,169,342,171,172,173,174,198,176,177,178,179,8220,181,182,183,248,185,343,187,188,189,190,230,260,302,256,262,196,197,280,274,268,201,377,278,290,310,298,315,352,323,325,211,332,213,214,215,370,321,346,362,220,379,381,223,261,303,257,263,228,229,281,275,269,233,378,279,291,311,299,316,353,324,326,243,333,245,246,247,371,322,347,363,252,380,382,8217}; +int single_byte_decoder::iso_8859_14_index[128] = {128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,7682,7683,163,266,267,7690,167,7808,169,7810,7691,7922,173,174,376,7710,7711,288,289,7744,7745,182,7766,7809,7767,7811,7776,7923,7812,7813,7777,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,372,209,210,211,212,213,214,7786,216,217,218,219,220,221,374,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,373,241,242,243,244,245,246,7787,248,249,250,251,252,253,375,255}; +int single_byte_decoder::iso_8859_15_index[128] = {128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,8364,165,352,167,353,169,170,171,172,173,174,175,176,177,178,179,381,181,182,183,382,185,186,187,338,339,376,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255}; +int single_byte_decoder::iso_8859_16_index[128] = {128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,260,261,321,8364,8222,352,167,353,169,536,171,377,173,378,379,176,177,268,322,381,8221,182,183,382,269,537,187,338,339,376,380,192,193,194,258,196,262,198,199,200,201,202,203,204,205,206,207,272,323,210,211,212,336,214,346,368,217,218,219,220,280,538,223,224,225,226,259,228,263,230,231,232,233,234,235,236,237,238,239,273,324,242,243,244,337,246,347,369,249,250,251,252,281,539,255}; +int single_byte_decoder::koi8_r_index[128] = {9472,9474,9484,9488,9492,9496,9500,9508,9516,9524,9532,9600,9604,9608,9612,9616,9617,9618,9619,8992,9632,8729,8730,8776,8804,8805,160,8993,176,178,183,247,9552,9553,9554,1105,9555,9556,9557,9558,9559,9560,9561,9562,9563,9564,9565,9566,9567,9568,9569,1025,9570,9571,9572,9573,9574,9575,9576,9577,9578,9579,9580,169,1102,1072,1073,1094,1076,1077,1092,1075,1093,1080,1081,1082,1083,1084,1085,1086,1087,1103,1088,1089,1090,1091,1078,1074,1100,1099,1079,1096,1101,1097,1095,1098,1070,1040,1041,1062,1044,1045,1060,1043,1061,1048,1049,1050,1051,1052,1053,1054,1055,1071,1056,1057,1058,1059,1046,1042,1068,1067,1047,1064,1069,1065,1063,1066}; +int single_byte_decoder::koi8_u_index[128] = {9472,9474,9484,9488,9492,9496,9500,9508,9516,9524,9532,9600,9604,9608,9612,9616,9617,9618,9619,8992,9632,8729,8730,8776,8804,8805,160,8993,176,178,183,247,9552,9553,9554,1105,1108,9556,1110,1111,9559,9560,9561,9562,9563,1169,1118,9566,9567,9568,9569,1025,1028,9571,1030,1031,9574,9575,9576,9577,9578,1168,1038,169,1102,1072,1073,1094,1076,1077,1092,1075,1093,1080,1081,1082,1083,1084,1085,1086,1087,1103,1088,1089,1090,1091,1078,1074,1100,1099,1079,1096,1101,1097,1095,1098,1070,1040,1041,1062,1044,1045,1060,1043,1061,1048,1049,1050,1051,1052,1053,1054,1055,1071,1056,1057,1058,1059,1046,1042,1068,1067,1047,1064,1069,1065,1063,1066}; +int single_byte_decoder::macintosh_index[128] = {196,197,199,201,209,214,220,225,224,226,228,227,229,231,233,232,234,235,237,236,238,239,241,243,242,244,246,245,250,249,251,252,8224,176,162,163,167,8226,182,223,174,169,8482,180,168,8800,198,216,8734,177,8804,8805,165,181,8706,8721,8719,960,8747,170,186,937,230,248,191,161,172,8730,402,8776,8710,171,187,8230,160,192,195,213,338,339,8211,8212,8220,8221,8216,8217,247,9674,255,376,8260,8364,8249,8250,64257,64258,8225,183,8218,8222,8240,194,202,193,203,200,205,206,207,204,211,212,63743,210,218,219,217,305,710,732,175,728,729,730,184,733,731,711 }; +int single_byte_decoder::windows_874_index[128] = {8364,129,130,131,132,8230,134,135,136,137,138,139,140,141,142,143,144,8216,8217,8220,8221,8226,8211,8212,152,153,154,155,156,157,158,159,160,3585,3586,3587,3588,3589,3590,3591,3592,3593,3594,3595,3596,3597,3598,3599,3600,3601,3602,3603,3604,3605,3606,3607,3608,3609,3610,3611,3612,3613,3614,3615,3616,3617,3618,3619,3620,3621,3622,3623,3624,3625,3626,3627,3628,3629,3630,3631,3632,3633,3634,3635,3636,3637,3638,3639,3640,3641,3642,null,null,null,null,3647,3648,3649,3650,3651,3652,3653,3654,3655,3656,3657,3658,3659,3660,3661,3662,3663,3664,3665,3666,3667,3668,3669,3670,3671,3672,3673,3674,3675,null,null,null,null}; +int single_byte_decoder::windows_1250_index[128] = {8364,129,8218,131,8222,8230,8224,8225,136,8240,352,8249,346,356,381,377,144,8216,8217,8220,8221,8226,8211,8212,152,8482,353,8250,347,357,382,378,160,711,728,321,164,260,166,167,168,169,350,171,172,173,174,379,176,177,731,322,180,181,182,183,184,261,351,187,317,733,318,380,340,193,194,258,196,313,262,199,268,201,280,203,282,205,206,270,272,323,327,211,212,336,214,215,344,366,218,368,220,221,354,223,341,225,226,259,228,314,263,231,269,233,281,235,283,237,238,271,273,324,328,243,244,337,246,247,345,367,250,369,252,253,355,729}; +int single_byte_decoder::windows_1251_index[128] = {1026,1027,8218,1107,8222,8230,8224,8225,8364,8240,1033,8249,1034,1036,1035,1039,1106,8216,8217,8220,8221,8226,8211,8212,152,8482,1113,8250,1114,1116,1115,1119,160,1038,1118,1032,164,1168,166,167,1025,169,1028,171,172,173,174,1031,176,177,1030,1110,1169,181,182,183,1105,8470,1108,187,1112,1029,1109,1111,1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103}; +int single_byte_decoder::windows_1252_index[128] = {8364,129,8218,402,8222,8230,8224,8225,710,8240,352,8249,338,141,381,143,144,8216,8217,8220,8221,8226,8211,8212,732,8482,353,8250,339,157,382,376,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255}; +int single_byte_decoder::windows_1253_index[128] = {8364,129,8218,402,8222,8230,8224,8225,136,8240,138,8249,140,141,142,143,144,8216,8217,8220,8221,8226,8211,8212,152,8482,154,8250,156,157,158,159,160,901,902,163,164,165,166,167,168,169,null,171,172,173,174,8213,176,177,178,179,900,181,182,183,904,905,906,187,908,189,910,911,912,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,null,931,932,933,934,935,936,937,938,939,940,941,942,943,944,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,962,963,964,965,966,967,968,969,970,971,972,973,974,null}; +int single_byte_decoder::windows_1254_index[128] = {8364,129,8218,402,8222,8230,8224,8225,710,8240,352,8249,338,141,142,143,144,8216,8217,8220,8221,8226,8211,8212,732,8482,353,8250,339,157,158,376,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,286,209,210,211,212,213,214,215,216,217,218,219,220,304,350,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,287,241,242,243,244,245,246,247,248,249,250,251,252,305,351,255}; +int single_byte_decoder::windows_1255_index[128] = {8364,129,8218,402,8222,8230,8224,8225,710,8240,138,8249,140,141,142,143,144,8216,8217,8220,8221,8226,8211,8212,732,8482,154,8250,156,157,158,159,160,161,162,163,8362,165,166,167,168,169,215,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,247,187,188,189,190,191,1456,1457,1458,1459,1460,1461,1462,1463,1464,1465,1466,1467,1468,1469,1470,1471,1472,1473,1474,1475,1520,1521,1522,1523,1524,null,null,null,null,null,null,null,1488,1489,1490,1491,1492,1493,1494,1495,1496,1497,1498,1499,1500,1501,1502,1503,1504,1505,1506,1507,1508,1509,1510,1511,1512,1513,1514,null,null,8206,8207,null}; +int single_byte_decoder::windows_1256_index[128] = {8364,1662,8218,402,8222,8230,8224,8225,710,8240,1657,8249,338,1670,1688,1672,1711,8216,8217,8220,8221,8226,8211,8212,1705,8482,1681,8250,339,8204,8205,1722,160,1548,162,163,164,165,166,167,168,169,1726,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,1563,187,188,189,190,1567,1729,1569,1570,1571,1572,1573,1574,1575,1576,1577,1578,1579,1580,1581,1582,1583,1584,1585,1586,1587,1588,1589,1590,215,1591,1592,1593,1594,1600,1601,1602,1603,224,1604,226,1605,1606,1607,1608,231,232,233,234,235,1609,1610,238,239,1611,1612,1613,1614,244,1615,1616,247,1617,249,1618,251,252,8206,8207,1746}; +int single_byte_decoder::windows_1257_index[128] = {8364,129,8218,131,8222,8230,8224,8225,136,8240,138,8249,140,168,711,184,144,8216,8217,8220,8221,8226,8211,8212,152,8482,154,8250,156,175,731,159,160,null,162,163,164,null,166,167,216,169,342,171,172,173,174,198,176,177,178,179,180,181,182,183,248,185,343,187,188,189,190,230,260,302,256,262,196,197,280,274,268,201,377,278,290,310,298,315,352,323,325,211,332,213,214,215,370,321,346,362,220,379,381,223,261,303,257,263,228,229,281,275,269,233,378,279,291,311,299,316,353,324,326,243,333,245,246,247,371,322,347,363,252,380,382,729}; +int single_byte_decoder::windows_1258_index[128] = {8364,129,8218,402,8222,8230,8224,8225,710,8240,138,8249,338,141,142,143,144,8216,8217,8220,8221,8226,8211,8212,732,8482,154,8250,339,157,158,376,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,258,196,197,198,199,200,201,202,203,768,205,206,207,272,209,777,211,212,416,214,215,216,217,218,219,220,431,771,223,224,225,226,259,228,229,230,231,232,233,234,235,769,237,238,239,273,241,803,243,244,417,246,247,248,249,250,251,252,432,8363,255}; +int single_byte_decoder::x_mac_cyrillic_index[128] = {1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,8224,176,1168,163,167,8226,182,1030,174,169,8482,1026,1106,8800,1027,1107,8734,177,8804,8805,1110,181,1169,1032,1028,1108,1031,1111,1033,1113,1034,1114,1112,1029,172,8730,402,8776,8710,171,187,8230,160,1035,1115,1036,1116,1109,8211,8212,8220,8221,8216,8217,247,8222,1038,1118,1039,1119,8470,1025,1105,1103,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,8364}; + +// must be in sync with enum encoding +int* single_byte_decoder::m_indexes[] = +{ + ibm866_index, + iso_8859_2_index, + iso_8859_3_index, + iso_8859_4_index, + iso_8859_5_index, + iso_8859_6_index, + iso_8859_7_index, + iso_8859_8_index, + iso_8859_8_index, // iso_8859_8_i + iso_8859_10_index, + iso_8859_13_index, + iso_8859_14_index, + iso_8859_15_index, + iso_8859_16_index, + koi8_r_index, + koi8_u_index, + macintosh_index, + windows_874_index, + windows_1250_index, + windows_1251_index, + windows_1252_index, + windows_1253_index, + windows_1254_index, + windows_1255_index, + windows_1256_index, + windows_1257_index, + windows_1258_index, + x_mac_cyrillic_index, +}; + +// https://encoding.spec.whatwg.org/#single-byte-decoder +decoder::result single_byte_decoder::handler(string& input, int& index, int ch[2]) +{ + int b = index == (int)input.size() ? EOF : (byte)input[index++]; // read input byte + + // 1. If byte is end-of-queue, return finished. + if (b == EOF) return result_finished; + + // 2. If byte is an ASCII byte, return a code point whose value is byte. + if (b >= 0 && b <= 0x7F) + { + *ch = b; + return result_codepoint; + } + + // 3. Let code point be the index code point for byte − 0x80 in index single-byte. + int code_point = m_index[b - 0x80]; + + // 4. If code point is null, return error. + if (code_point == null) return result_error; + + // 5. Return a code point whose value is code point. + *ch = code_point; + return result_codepoint; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +struct gb18030_decoder final : decoder +{ + int m_first = 0; + int m_second = 0; + int m_third = 0; + + result handler(string& input, int& index, int ch[2]) override; + + static int ranges_code_point(int pointer); + + struct range + { + int pointer; + int code_point; + }; + + static int m_index[23940]; + static range m_ranges[207]; +}; + +// https://encoding.spec.whatwg.org/indexes.json +int gb18030_decoder::m_index[] = {19970,19972,19973,19974,19983,19986,19991,19999,20000,20001,20003,20006,20009,20014,20015,20017,20019,20021,20023,20028,20032,20033,20034,20036,20038,20042,20049,20053,20055,20058,20059,20066,20067,20068,20069,20071,20072,20074,20075,20076,20077,20078,20079,20082,20084,20085,20086,20087,20088,20089,20090,20091,20092,20093,20095,20096,20097,20098,20099,20100,20101,20103,20106,20112,20118,20119,20121,20124,20125,20126,20131,20138,20143,20144,20145,20148,20150,20151,20152,20153,20156,20157,20158,20168,20172,20175,20176,20178,20186,20187,20188,20192,20194,20198,20199,20201,20205,20206,20207,20209,20212,20216,20217,20218,20220,20222,20224,20226,20227,20228,20229,20230,20231,20232,20235,20236,20242,20243,20244,20245,20246,20252,20253,20257,20259,20264,20265,20268,20269,20270,20273,20275,20277,20279,20281,20283,20286,20287,20288,20289,20290,20292,20293,20295,20296,20297,20298,20299,20300,20306,20308,20310,20321,20322,20326,20328,20330,20331,20333,20334,20337,20338,20341,20343,20344,20345,20346,20349,20352,20353,20354,20357,20358,20359,20362,20364,20366,20368,20370,20371,20373,20374,20376,20377,20378,20380,20382,20383,20385,20386,20388,20395,20397,20400,20401,20402,20403,20404,20406,20407,20408,20409,20410,20411,20412,20413,20414,20416,20417,20418,20422,20423,20424,20425,20427,20428,20429,20434,20435,20436,20437,20438,20441,20443,20448,20450,20452,20453,20455,20459,20460,20464,20466,20468,20469,20470,20471,20473,20475,20476,20477,20479,20480,20481,20482,20483,20484,20485,20486,20487,20488,20489,20490,20491,20494,20496,20497,20499,20501,20502,20503,20507,20509,20510,20512,20514,20515,20516,20519,20523,20527,20528,20529,20530,20531,20532,20533,20534,20535,20536,20537,20539,20541,20543,20544,20545,20546,20548,20549,20550,20553,20554,20555,20557,20560,20561,20562,20563,20564,20566,20567,20568,20569,20571,20573,20574,20575,20576,20577,20578,20579,20580,20582,20583,20584,20585,20586,20587,20589,20590,20591,20592,20593,20594,20595,20596,20597,20600,20601,20602,20604,20605,20609,20610,20611,20612,20614,20615,20617,20618,20619,20620,20622,20623,20624,20625,20626,20627,20628,20629,20630,20631,20632,20633,20634,20635,20636,20637,20638,20639,20640,20641,20642,20644,20646,20650,20651,20653,20654,20655,20656,20657,20659,20660,20661,20662,20663,20664,20665,20668,20669,20670,20671,20672,20673,20674,20675,20676,20677,20678,20679,20680,20681,20682,20683,20684,20685,20686,20688,20689,20690,20691,20692,20693,20695,20696,20697,20699,20700,20701,20702,20703,20704,20705,20706,20707,20708,20709,20712,20713,20714,20715,20719,20720,20721,20722,20724,20726,20727,20728,20729,20730,20732,20733,20734,20735,20736,20737,20738,20739,20740,20741,20744,20745,20746,20748,20749,20750,20751,20752,20753,20755,20756,20757,20758,20759,20760,20761,20762,20763,20764,20765,20766,20767,20768,20770,20771,20772,20773,20774,20775,20776,20777,20778,20779,20780,20781,20782,20783,20784,20785,20786,20787,20788,20789,20790,20791,20792,20793,20794,20795,20796,20797,20798,20802,20807,20810,20812,20814,20815,20816,20818,20819,20823,20824,20825,20827,20829,20830,20831,20832,20833,20835,20836,20838,20839,20841,20842,20847,20850,20858,20862,20863,20867,20868,20870,20871,20874,20875,20878,20879,20880,20881,20883,20884,20888,20890,20893,20894,20895,20897,20899,20902,20903,20904,20905,20906,20909,20910,20916,20920,20921,20922,20926,20927,20929,20930,20931,20933,20936,20938,20941,20942,20944,20946,20947,20948,20949,20950,20951,20952,20953,20954,20956,20958,20959,20962,20963,20965,20966,20967,20968,20969,20970,20972,20974,20977,20978,20980,20983,20990,20996,20997,21001,21003,21004,21007,21008,21011,21012,21013,21020,21022,21023,21025,21026,21027,21029,21030,21031,21034,21036,21039,21041,21042,21044,21045,21052,21054,21060,21061,21062,21063,21064,21065,21067,21070,21071,21074,21075,21077,21079,21080,21081,21082,21083,21085,21087,21088,21090,21091,21092,21094,21096,21099,21100,21101,21102,21104,21105,21107,21108,21109,21110,21111,21112,21113,21114,21115,21116,21118,21120,21123,21124,21125,21126,21127,21129,21130,21131,21132,21133,21134,21135,21137,21138,21140,21141,21142,21143,21144,21145,21146,21148,21156,21157,21158,21159,21166,21167,21168,21172,21173,21174,21175,21176,21177,21178,21179,21180,21181,21184,21185,21186,21188,21189,21190,21192,21194,21196,21197,21198,21199,21201,21203,21204,21205,21207,21209,21210,21211,21212,21213,21214,21216,21217,21218,21219,21221,21222,21223,21224,21225,21226,21227,21228,21229,21230,21231,21233,21234,21235,21236,21237,21238,21239,21240,21243,21244,21245,21249,21250,21251,21252,21255,21257,21258,21259,21260,21262,21265,21266,21267,21268,21272,21275,21276,21278,21279,21282,21284,21285,21287,21288,21289,21291,21292,21293,21295,21296,21297,21298,21299,21300,21301,21302,21303,21304,21308,21309,21312,21314,21316,21318,21323,21324,21325,21328,21332,21336,21337,21339,21341,21349,21352,21354,21356,21357,21362,21366,21369,21371,21372,21373,21374,21376,21377,21379,21383,21384,21386,21390,21391,21392,21393,21394,21395,21396,21398,21399,21401,21403,21404,21406,21408,21409,21412,21415,21418,21419,21420,21421,21423,21424,21425,21426,21427,21428,21429,21431,21432,21433,21434,21436,21437,21438,21440,21443,21444,21445,21446,21447,21454,21455,21456,21458,21459,21461,21466,21468,21469,21470,21473,21474,21479,21492,21498,21502,21503,21504,21506,21509,21511,21515,21524,21528,21529,21530,21532,21538,21540,21541,21546,21552,21555,21558,21559,21562,21565,21567,21569,21570,21572,21573,21575,21577,21580,21581,21582,21583,21585,21594,21597,21598,21599,21600,21601,21603,21605,21607,21609,21610,21611,21612,21613,21614,21615,21616,21620,21625,21626,21630,21631,21633,21635,21637,21639,21640,21641,21642,21645,21649,21651,21655,21656,21660,21662,21663,21664,21665,21666,21669,21678,21680,21682,21685,21686,21687,21689,21690,21692,21694,21699,21701,21706,21707,21718,21720,21723,21728,21729,21730,21731,21732,21739,21740,21743,21744,21745,21748,21749,21750,21751,21752,21753,21755,21758,21760,21762,21763,21764,21765,21768,21770,21771,21772,21773,21774,21778,21779,21781,21782,21783,21784,21785,21786,21788,21789,21790,21791,21793,21797,21798,21800,21801,21803,21805,21810,21812,21813,21814,21816,21817,21818,21819,21821,21824,21826,21829,21831,21832,21835,21836,21837,21838,21839,21841,21842,21843,21844,21847,21848,21849,21850,21851,21853,21854,21855,21856,21858,21859,21864,21865,21867,21871,21872,21873,21874,21875,21876,21881,21882,21885,21887,21893,21894,21900,21901,21902,21904,21906,21907,21909,21910,21911,21914,21915,21918,21920,21921,21922,21923,21924,21925,21926,21928,21929,21930,21931,21932,21933,21934,21935,21936,21938,21940,21942,21944,21946,21948,21951,21952,21953,21954,21955,21958,21959,21960,21962,21963,21966,21967,21968,21973,21975,21976,21977,21978,21979,21982,21984,21986,21991,21993,21997,21998,22000,22001,22004,22006,22008,22009,22010,22011,22012,22015,22018,22019,22020,22021,22022,22023,22026,22027,22029,22032,22033,22034,22035,22036,22037,22038,22039,22041,22042,22044,22045,22048,22049,22050,22053,22054,22056,22057,22058,22059,22062,22063,22064,22067,22069,22071,22072,22074,22076,22077,22078,22080,22081,22082,22083,22084,22085,22086,22087,22088,22089,22090,22091,22095,22096,22097,22098,22099,22101,22102,22106,22107,22109,22110,22111,22112,22113,22115,22117,22118,22119,22125,22126,22127,22128,22130,22131,22132,22133,22135,22136,22137,22138,22141,22142,22143,22144,22145,22146,22147,22148,22151,22152,22153,22154,22155,22156,22157,22160,22161,22162,22164,22165,22166,22167,22168,22169,22170,22171,22172,22173,22174,22175,22176,22177,22178,22180,22181,22182,22183,22184,22185,22186,22187,22188,22189,22190,22192,22193,22194,22195,22196,22197,22198,22200,22201,22202,22203,22205,22206,22207,22208,22209,22210,22211,22212,22213,22214,22215,22216,22217,22219,22220,22221,22222,22223,22224,22225,22226,22227,22229,22230,22232,22233,22236,22243,22245,22246,22247,22248,22249,22250,22252,22254,22255,22258,22259,22262,22263,22264,22267,22268,22272,22273,22274,22277,22279,22283,22284,22285,22286,22287,22288,22289,22290,22291,22292,22293,22294,22295,22296,22297,22298,22299,22301,22302,22304,22305,22306,22308,22309,22310,22311,22315,22321,22322,22324,22325,22326,22327,22328,22332,22333,22335,22337,22339,22340,22341,22342,22344,22345,22347,22354,22355,22356,22357,22358,22360,22361,22370,22371,22373,22375,22380,22382,22384,22385,22386,22388,22389,22392,22393,22394,22397,22398,22399,22400,22401,22407,22408,22409,22410,22413,22414,22415,22416,22417,22420,22421,22422,22423,22424,22425,22426,22428,22429,22430,22431,22437,22440,22442,22444,22447,22448,22449,22451,22453,22454,22455,22457,22458,22459,22460,22461,22462,22463,22464,22465,22468,22469,22470,22471,22472,22473,22474,22476,22477,22480,22481,22483,22486,22487,22491,22492,22494,22497,22498,22499,22501,22502,22503,22504,22505,22506,22507,22508,22510,22512,22513,22514,22515,22517,22518,22519,22523,22524,22526,22527,22529,22531,22532,22533,22536,22537,22538,22540,22542,22543,22544,22546,22547,22548,22550,22551,22552,22554,22555,22556,22557,22559,22562,22563,22565,22566,22567,22568,22569,22571,22572,22573,22574,22575,22577,22578,22579,22580,22582,22583,22584,22585,22586,22587,22588,22589,22590,22591,22592,22593,22594,22595,22597,22598,22599,22600,22601,22602,22603,22606,22607,22608,22610,22611,22613,22614,22615,22617,22618,22619,22620,22621,22623,22624,22625,22626,22627,22628,22630,22631,22632,22633,22634,22637,22638,22639,22640,22641,22642,22643,22644,22645,22646,22647,22648,22649,22650,22651,22652,22653,22655,22658,22660,22662,22663,22664,22666,22667,22668,22669,22670,22671,22672,22673,22676,22677,22678,22679,22680,22683,22684,22685,22688,22689,22690,22691,22692,22693,22694,22695,22698,22699,22700,22701,22702,22703,22704,22705,22706,22707,22708,22709,22710,22711,22712,22713,22714,22715,22717,22718,22719,22720,22722,22723,22724,22726,22727,22728,22729,22730,22731,22732,22733,22734,22735,22736,22738,22739,22740,22742,22743,22744,22745,22746,22747,22748,22749,22750,22751,22752,22753,22754,22755,22757,22758,22759,22760,22761,22762,22765,22767,22769,22770,22772,22773,22775,22776,22778,22779,22780,22781,22782,22783,22784,22785,22787,22789,22790,22792,22793,22794,22795,22796,22798,22800,22801,22802,22803,22807,22808,22811,22813,22814,22816,22817,22818,22819,22822,22824,22828,22832,22834,22835,22837,22838,22843,22845,22846,22847,22848,22851,22853,22854,22858,22860,22861,22864,22866,22867,22873,22875,22876,22877,22878,22879,22881,22883,22884,22886,22887,22888,22889,22890,22891,22892,22893,22894,22895,22896,22897,22898,22901,22903,22906,22907,22908,22910,22911,22912,22917,22921,22923,22924,22926,22927,22928,22929,22932,22933,22936,22938,22939,22940,22941,22943,22944,22945,22946,22950,22951,22956,22957,22960,22961,22963,22964,22965,22966,22967,22968,22970,22972,22973,22975,22976,22977,22978,22979,22980,22981,22983,22984,22985,22988,22989,22990,22991,22997,22998,23001,23003,23006,23007,23008,23009,23010,23012,23014,23015,23017,23018,23019,23021,23022,23023,23024,23025,23026,23027,23028,23029,23030,23031,23032,23034,23036,23037,23038,23040,23042,23050,23051,23053,23054,23055,23056,23058,23060,23061,23062,23063,23065,23066,23067,23069,23070,23073,23074,23076,23078,23079,23080,23082,23083,23084,23085,23086,23087,23088,23091,23093,23095,23096,23097,23098,23099,23101,23102,23103,23105,23106,23107,23108,23109,23111,23112,23115,23116,23117,23118,23119,23120,23121,23122,23123,23124,23126,23127,23128,23129,23131,23132,23133,23134,23135,23136,23137,23139,23140,23141,23142,23144,23145,23147,23148,23149,23150,23151,23152,23153,23154,23155,23160,23161,23163,23164,23165,23166,23168,23169,23170,23171,23172,23173,23174,23175,23176,23177,23178,23179,23180,23181,23182,23183,23184,23185,23187,23188,23189,23190,23191,23192,23193,23196,23197,23198,23199,23200,23201,23202,23203,23204,23205,23206,23207,23208,23209,23211,23212,23213,23214,23215,23216,23217,23220,23222,23223,23225,23226,23227,23228,23229,23231,23232,23235,23236,23237,23238,23239,23240,23242,23243,23245,23246,23247,23248,23249,23251,23253,23255,23257,23258,23259,23261,23262,23263,23266,23268,23269,23271,23272,23274,23276,23277,23278,23279,23280,23282,23283,23284,23285,23286,23287,23288,23289,23290,23291,23292,23293,23294,23295,23296,23297,23298,23299,23300,23301,23302,23303,23304,23306,23307,23308,23309,23310,23311,23312,23313,23314,23315,23316,23317,23320,23321,23322,23323,23324,23325,23326,23327,23328,23329,23330,23331,23332,23333,23334,23335,23336,23337,23338,23339,23340,23341,23342,23343,23344,23345,23347,23349,23350,23352,23353,23354,23355,23356,23357,23358,23359,23361,23362,23363,23364,23365,23366,23367,23368,23369,23370,23371,23372,23373,23374,23375,23378,23382,23390,23392,23393,23399,23400,23403,23405,23406,23407,23410,23412,23414,23415,23416,23417,23419,23420,23422,23423,23426,23430,23434,23437,23438,23440,23441,23442,23444,23446,23455,23463,23464,23465,23468,23469,23470,23471,23473,23474,23479,23482,23483,23484,23488,23489,23491,23496,23497,23498,23499,23501,23502,23503,23505,23508,23509,23510,23511,23512,23513,23514,23515,23516,23520,23522,23523,23526,23527,23529,23530,23531,23532,23533,23535,23537,23538,23539,23540,23541,23542,23543,23549,23550,23552,23554,23555,23557,23559,23560,23563,23564,23565,23566,23568,23570,23571,23575,23577,23579,23582,23583,23584,23585,23587,23590,23592,23593,23594,23595,23597,23598,23599,23600,23602,23603,23605,23606,23607,23619,23620,23622,23623,23628,23629,23634,23635,23636,23638,23639,23640,23642,23643,23644,23645,23647,23650,23652,23655,23656,23657,23658,23659,23660,23661,23664,23666,23667,23668,23669,23670,23671,23672,23675,23676,23677,23678,23680,23683,23684,23685,23686,23687,23689,23690,23691,23694,23695,23698,23699,23701,23709,23710,23711,23712,23713,23716,23717,23718,23719,23720,23722,23726,23727,23728,23730,23732,23734,23737,23738,23739,23740,23742,23744,23746,23747,23749,23750,23751,23752,23753,23754,23756,23757,23758,23759,23760,23761,23763,23764,23765,23766,23767,23768,23770,23771,23772,23773,23774,23775,23776,23778,23779,23783,23785,23787,23788,23790,23791,23793,23794,23795,23796,23797,23798,23799,23800,23801,23802,23804,23805,23806,23807,23808,23809,23812,23813,23816,23817,23818,23819,23820,23821,23823,23824,23825,23826,23827,23829,23831,23832,23833,23834,23836,23837,23839,23840,23841,23842,23843,23845,23848,23850,23851,23852,23855,23856,23857,23858,23859,23861,23862,23863,23864,23865,23866,23867,23868,23871,23872,23873,23874,23875,23876,23877,23878,23880,23881,23885,23886,23887,23888,23889,23890,23891,23892,23893,23894,23895,23897,23898,23900,23902,23903,23904,23905,23906,23907,23908,23909,23910,23911,23912,23914,23917,23918,23920,23921,23922,23923,23925,23926,23927,23928,23929,23930,23931,23932,23933,23934,23935,23936,23937,23939,23940,23941,23942,23943,23944,23945,23946,23947,23948,23949,23950,23951,23952,23953,23954,23955,23956,23957,23958,23959,23960,23962,23963,23964,23966,23967,23968,23969,23970,23971,23972,23973,23974,23975,23976,23977,23978,23979,23980,23981,23982,23983,23984,23985,23986,23987,23988,23989,23990,23992,23993,23994,23995,23996,23997,23998,23999,24000,24001,24002,24003,24004,24006,24007,24008,24009,24010,24011,24012,24014,24015,24016,24017,24018,24019,24020,24021,24022,24023,24024,24025,24026,24028,24031,24032,24035,24036,24042,24044,24045,24048,24053,24054,24056,24057,24058,24059,24060,24063,24064,24068,24071,24073,24074,24075,24077,24078,24082,24083,24087,24094,24095,24096,24097,24098,24099,24100,24101,24104,24105,24106,24107,24108,24111,24112,24114,24115,24116,24117,24118,24121,24122,24126,24127,24128,24129,24131,24134,24135,24136,24137,24138,24139,24141,24142,24143,24144,24145,24146,24147,24150,24151,24152,24153,24154,24156,24157,24159,24160,24163,24164,24165,24166,24167,24168,24169,24170,24171,24172,24173,24174,24175,24176,24177,24181,24183,24185,24190,24193,24194,24195,24197,24200,24201,24204,24205,24206,24210,24216,24219,24221,24225,24226,24227,24228,24232,24233,24234,24235,24236,24238,24239,24240,24241,24242,24244,24250,24251,24252,24253,24255,24256,24257,24258,24259,24260,24261,24262,24263,24264,24267,24268,24269,24270,24271,24272,24276,24277,24279,24280,24281,24282,24284,24285,24286,24287,24288,24289,24290,24291,24292,24293,24294,24295,24297,24299,24300,24301,24302,24303,24304,24305,24306,24307,24309,24312,24313,24315,24316,24317,24325,24326,24327,24329,24332,24333,24334,24336,24338,24340,24342,24345,24346,24348,24349,24350,24353,24354,24355,24356,24360,24363,24364,24366,24368,24370,24371,24372,24373,24374,24375,24376,24379,24381,24382,24383,24385,24386,24387,24388,24389,24390,24391,24392,24393,24394,24395,24396,24397,24398,24399,24401,24404,24409,24410,24411,24412,24414,24415,24416,24419,24421,24423,24424,24427,24430,24431,24434,24436,24437,24438,24440,24442,24445,24446,24447,24451,24454,24461,24462,24463,24465,24467,24468,24470,24474,24475,24477,24478,24479,24480,24482,24483,24484,24485,24486,24487,24489,24491,24492,24495,24496,24497,24498,24499,24500,24502,24504,24505,24506,24507,24510,24511,24512,24513,24514,24519,24520,24522,24523,24526,24531,24532,24533,24538,24539,24540,24542,24543,24546,24547,24549,24550,24552,24553,24556,24559,24560,24562,24563,24564,24566,24567,24569,24570,24572,24583,24584,24585,24587,24588,24592,24593,24595,24599,24600,24602,24606,24607,24610,24611,24612,24620,24621,24622,24624,24625,24626,24627,24628,24630,24631,24632,24633,24634,24637,24638,24640,24644,24645,24646,24647,24648,24649,24650,24652,24654,24655,24657,24659,24660,24662,24663,24664,24667,24668,24670,24671,24672,24673,24677,24678,24686,24689,24690,24692,24693,24695,24702,24704,24705,24706,24709,24710,24711,24712,24714,24715,24718,24719,24720,24721,24723,24725,24727,24728,24729,24732,24734,24737,24738,24740,24741,24743,24745,24746,24750,24752,24755,24757,24758,24759,24761,24762,24765,24766,24767,24768,24769,24770,24771,24772,24775,24776,24777,24780,24781,24782,24783,24784,24786,24787,24788,24790,24791,24793,24795,24798,24801,24802,24803,24804,24805,24810,24817,24818,24821,24823,24824,24827,24828,24829,24830,24831,24834,24835,24836,24837,24839,24842,24843,24844,24848,24849,24850,24851,24852,24854,24855,24856,24857,24859,24860,24861,24862,24865,24866,24869,24872,24873,24874,24876,24877,24878,24879,24880,24881,24882,24883,24884,24885,24886,24887,24888,24889,24890,24891,24892,24893,24894,24896,24897,24898,24899,24900,24901,24902,24903,24905,24907,24909,24911,24912,24914,24915,24916,24918,24919,24920,24921,24922,24923,24924,24926,24927,24928,24929,24931,24932,24933,24934,24937,24938,24939,24940,24941,24942,24943,24945,24946,24947,24948,24950,24952,24953,24954,24955,24956,24957,24958,24959,24960,24961,24962,24963,24964,24965,24966,24967,24968,24969,24970,24972,24973,24975,24976,24977,24978,24979,24981,24982,24983,24984,24985,24986,24987,24988,24990,24991,24992,24993,24994,24995,24996,24997,24998,25002,25003,25005,25006,25007,25008,25009,25010,25011,25012,25013,25014,25016,25017,25018,25019,25020,25021,25023,25024,25025,25027,25028,25029,25030,25031,25033,25036,25037,25038,25039,25040,25043,25045,25046,25047,25048,25049,25050,25051,25052,25053,25054,25055,25056,25057,25058,25059,25060,25061,25063,25064,25065,25066,25067,25068,25069,25070,25071,25072,25073,25074,25075,25076,25078,25079,25080,25081,25082,25083,25084,25085,25086,25088,25089,25090,25091,25092,25093,25095,25097,25107,25108,25113,25116,25117,25118,25120,25123,25126,25127,25128,25129,25131,25133,25135,25136,25137,25138,25141,25142,25144,25145,25146,25147,25148,25154,25156,25157,25158,25162,25167,25168,25173,25174,25175,25177,25178,25180,25181,25182,25183,25184,25185,25186,25188,25189,25192,25201,25202,25204,25205,25207,25208,25210,25211,25213,25217,25218,25219,25221,25222,25223,25224,25227,25228,25229,25230,25231,25232,25236,25241,25244,25245,25246,25251,25254,25255,25257,25258,25261,25262,25263,25264,25266,25267,25268,25270,25271,25272,25274,25278,25280,25281,25283,25291,25295,25297,25301,25309,25310,25312,25313,25316,25322,25323,25328,25330,25333,25336,25337,25338,25339,25344,25347,25348,25349,25350,25354,25355,25356,25357,25359,25360,25362,25363,25364,25365,25367,25368,25369,25372,25382,25383,25385,25388,25389,25390,25392,25393,25395,25396,25397,25398,25399,25400,25403,25404,25406,25407,25408,25409,25412,25415,25416,25418,25425,25426,25427,25428,25430,25431,25432,25433,25434,25435,25436,25437,25440,25444,25445,25446,25448,25450,25451,25452,25455,25456,25458,25459,25460,25461,25464,25465,25468,25469,25470,25471,25473,25475,25476,25477,25478,25483,25485,25489,25491,25492,25493,25495,25497,25498,25499,25500,25501,25502,25503,25505,25508,25510,25515,25519,25521,25522,25525,25526,25529,25531,25533,25535,25536,25537,25538,25539,25541,25543,25544,25546,25547,25548,25553,25555,25556,25557,25559,25560,25561,25562,25563,25564,25565,25567,25570,25572,25573,25574,25575,25576,25579,25580,25582,25583,25584,25585,25587,25589,25591,25593,25594,25595,25596,25598,25603,25604,25606,25607,25608,25609,25610,25613,25614,25617,25618,25621,25622,25623,25624,25625,25626,25629,25631,25634,25635,25636,25637,25639,25640,25641,25643,25646,25647,25648,25649,25650,25651,25653,25654,25655,25656,25657,25659,25660,25662,25664,25666,25667,25673,25675,25676,25677,25678,25679,25680,25681,25683,25685,25686,25687,25689,25690,25691,25692,25693,25695,25696,25697,25698,25699,25700,25701,25702,25704,25706,25707,25708,25710,25711,25712,25713,25714,25715,25716,25717,25718,25719,25723,25724,25725,25726,25727,25728,25729,25731,25734,25736,25737,25738,25739,25740,25741,25742,25743,25744,25747,25748,25751,25752,25754,25755,25756,25757,25759,25760,25761,25762,25763,25765,25766,25767,25768,25770,25771,25775,25777,25778,25779,25780,25782,25785,25787,25789,25790,25791,25793,25795,25796,25798,25799,25800,25801,25802,25803,25804,25807,25809,25811,25812,25813,25814,25817,25818,25819,25820,25821,25823,25824,25825,25827,25829,25831,25832,25833,25834,25835,25836,25837,25838,25839,25840,25841,25842,25843,25844,25845,25846,25847,25848,25849,25850,25851,25852,25853,25854,25855,25857,25858,25859,25860,25861,25862,25863,25864,25866,25867,25868,25869,25870,25871,25872,25873,25875,25876,25877,25878,25879,25881,25882,25883,25884,25885,25886,25887,25888,25889,25890,25891,25892,25894,25895,25896,25897,25898,25900,25901,25904,25905,25906,25907,25911,25914,25916,25917,25920,25921,25922,25923,25924,25926,25927,25930,25931,25933,25934,25936,25938,25939,25940,25943,25944,25946,25948,25951,25952,25953,25956,25957,25959,25960,25961,25962,25965,25966,25967,25969,25971,25973,25974,25976,25977,25978,25979,25980,25981,25982,25983,25984,25985,25986,25987,25988,25989,25990,25992,25993,25994,25997,25998,25999,26002,26004,26005,26006,26008,26010,26013,26014,26016,26018,26019,26022,26024,26026,26028,26030,26033,26034,26035,26036,26037,26038,26039,26040,26042,26043,26046,26047,26048,26050,26055,26056,26057,26058,26061,26064,26065,26067,26068,26069,26072,26073,26074,26075,26076,26077,26078,26079,26081,26083,26084,26090,26091,26098,26099,26100,26101,26104,26105,26107,26108,26109,26110,26111,26113,26116,26117,26119,26120,26121,26123,26125,26128,26129,26130,26134,26135,26136,26138,26139,26140,26142,26145,26146,26147,26148,26150,26153,26154,26155,26156,26158,26160,26162,26163,26167,26168,26169,26170,26171,26173,26175,26176,26178,26180,26181,26182,26183,26184,26185,26186,26189,26190,26192,26193,26200,26201,26203,26204,26205,26206,26208,26210,26211,26213,26215,26217,26218,26219,26220,26221,26225,26226,26227,26229,26232,26233,26235,26236,26237,26239,26240,26241,26243,26245,26246,26248,26249,26250,26251,26253,26254,26255,26256,26258,26259,26260,26261,26264,26265,26266,26267,26268,26270,26271,26272,26273,26274,26275,26276,26277,26278,26281,26282,26283,26284,26285,26287,26288,26289,26290,26291,26293,26294,26295,26296,26298,26299,26300,26301,26303,26304,26305,26306,26307,26308,26309,26310,26311,26312,26313,26314,26315,26316,26317,26318,26319,26320,26321,26322,26323,26324,26325,26326,26327,26328,26330,26334,26335,26336,26337,26338,26339,26340,26341,26343,26344,26346,26347,26348,26349,26350,26351,26353,26357,26358,26360,26362,26363,26365,26369,26370,26371,26372,26373,26374,26375,26380,26382,26383,26385,26386,26387,26390,26392,26393,26394,26396,26398,26400,26401,26402,26403,26404,26405,26407,26409,26414,26416,26418,26419,26422,26423,26424,26425,26427,26428,26430,26431,26433,26436,26437,26439,26442,26443,26445,26450,26452,26453,26455,26456,26457,26458,26459,26461,26466,26467,26468,26470,26471,26475,26476,26478,26481,26484,26486,26488,26489,26490,26491,26493,26496,26498,26499,26501,26502,26504,26506,26508,26509,26510,26511,26513,26514,26515,26516,26518,26521,26523,26527,26528,26529,26532,26534,26537,26540,26542,26545,26546,26548,26553,26554,26555,26556,26557,26558,26559,26560,26562,26565,26566,26567,26568,26569,26570,26571,26572,26573,26574,26581,26582,26583,26587,26591,26593,26595,26596,26598,26599,26600,26602,26603,26605,26606,26610,26613,26614,26615,26616,26617,26618,26619,26620,26622,26625,26626,26627,26628,26630,26637,26640,26642,26644,26645,26648,26649,26650,26651,26652,26654,26655,26656,26658,26659,26660,26661,26662,26663,26664,26667,26668,26669,26670,26671,26672,26673,26676,26677,26678,26682,26683,26687,26695,26699,26701,26703,26706,26710,26711,26712,26713,26714,26715,26716,26717,26718,26719,26730,26732,26733,26734,26735,26736,26737,26738,26739,26741,26744,26745,26746,26747,26748,26749,26750,26751,26752,26754,26756,26759,26760,26761,26762,26763,26764,26765,26766,26768,26769,26770,26772,26773,26774,26776,26777,26778,26779,26780,26781,26782,26783,26784,26785,26787,26788,26789,26793,26794,26795,26796,26798,26801,26802,26804,26806,26807,26808,26809,26810,26811,26812,26813,26814,26815,26817,26819,26820,26821,26822,26823,26824,26826,26828,26830,26831,26832,26833,26835,26836,26838,26839,26841,26843,26844,26845,26846,26847,26849,26850,26852,26853,26854,26855,26856,26857,26858,26859,26860,26861,26863,26866,26867,26868,26870,26871,26872,26875,26877,26878,26879,26880,26882,26883,26884,26886,26887,26888,26889,26890,26892,26895,26897,26899,26900,26901,26902,26903,26904,26905,26906,26907,26908,26909,26910,26913,26914,26915,26917,26918,26919,26920,26921,26922,26923,26924,26926,26927,26929,26930,26931,26933,26934,26935,26936,26938,26939,26940,26942,26944,26945,26947,26948,26949,26950,26951,26952,26953,26954,26955,26956,26957,26958,26959,26960,26961,26962,26963,26965,26966,26968,26969,26971,26972,26975,26977,26978,26980,26981,26983,26984,26985,26986,26988,26989,26991,26992,26994,26995,26996,26997,26998,27002,27003,27005,27006,27007,27009,27011,27013,27018,27019,27020,27022,27023,27024,27025,27026,27027,27030,27031,27033,27034,27037,27038,27039,27040,27041,27042,27043,27044,27045,27046,27049,27050,27052,27054,27055,27056,27058,27059,27061,27062,27064,27065,27066,27068,27069,27070,27071,27072,27074,27075,27076,27077,27078,27079,27080,27081,27083,27085,27087,27089,27090,27091,27093,27094,27095,27096,27097,27098,27100,27101,27102,27105,27106,27107,27108,27109,27110,27111,27112,27113,27114,27115,27116,27118,27119,27120,27121,27123,27124,27125,27126,27127,27128,27129,27130,27131,27132,27134,27136,27137,27138,27139,27140,27141,27142,27143,27144,27145,27147,27148,27149,27150,27151,27152,27153,27154,27155,27156,27157,27158,27161,27162,27163,27164,27165,27166,27168,27170,27171,27172,27173,27174,27175,27177,27179,27180,27181,27182,27184,27186,27187,27188,27190,27191,27192,27193,27194,27195,27196,27199,27200,27201,27202,27203,27205,27206,27208,27209,27210,27211,27212,27213,27214,27215,27217,27218,27219,27220,27221,27222,27223,27226,27228,27229,27230,27231,27232,27234,27235,27236,27238,27239,27240,27241,27242,27243,27244,27245,27246,27247,27248,27250,27251,27252,27253,27254,27255,27256,27258,27259,27261,27262,27263,27265,27266,27267,27269,27270,27271,27272,27273,27274,27275,27276,27277,27279,27282,27283,27284,27285,27286,27288,27289,27290,27291,27292,27293,27294,27295,27297,27298,27299,27300,27301,27302,27303,27304,27306,27309,27310,27311,27312,27313,27314,27315,27316,27317,27318,27319,27320,27321,27322,27323,27324,27325,27326,27327,27328,27329,27330,27331,27332,27333,27334,27335,27336,27337,27338,27339,27340,27341,27342,27343,27344,27345,27346,27347,27348,27349,27350,27351,27352,27353,27354,27355,27356,27357,27358,27359,27360,27361,27362,27363,27364,27365,27366,27367,27368,27369,27370,27371,27372,27373,27374,27375,27376,27377,27378,27379,27380,27381,27382,27383,27384,27385,27386,27387,27388,27389,27390,27391,27392,27393,27394,27395,27396,27397,27398,27399,27400,27401,27402,27403,27404,27405,27406,27407,27408,27409,27410,27411,27412,27413,27414,27415,27416,27417,27418,27419,27420,27421,27422,27423,27429,27430,27432,27433,27434,27435,27436,27437,27438,27439,27440,27441,27443,27444,27445,27446,27448,27451,27452,27453,27455,27456,27457,27458,27460,27461,27464,27466,27467,27469,27470,27471,27472,27473,27474,27475,27476,27477,27478,27479,27480,27482,27483,27484,27485,27486,27487,27488,27489,27496,27497,27499,27500,27501,27502,27503,27504,27505,27506,27507,27508,27509,27510,27511,27512,27514,27517,27518,27519,27520,27525,27528,27532,27534,27535,27536,27537,27540,27541,27543,27544,27545,27548,27549,27550,27551,27552,27554,27555,27556,27557,27558,27559,27560,27561,27563,27564,27565,27566,27567,27568,27569,27570,27574,27576,27577,27578,27579,27580,27581,27582,27584,27587,27588,27590,27591,27592,27593,27594,27596,27598,27600,27601,27608,27610,27612,27613,27614,27615,27616,27618,27619,27620,27621,27622,27623,27624,27625,27628,27629,27630,27632,27633,27634,27636,27638,27639,27640,27642,27643,27644,27646,27647,27648,27649,27650,27651,27652,27656,27657,27658,27659,27660,27662,27666,27671,27676,27677,27678,27680,27683,27685,27691,27692,27693,27697,27699,27702,27703,27705,27706,27707,27708,27710,27711,27715,27716,27717,27720,27723,27724,27725,27726,27727,27729,27730,27731,27734,27736,27737,27738,27746,27747,27749,27750,27751,27755,27756,27757,27758,27759,27761,27763,27765,27767,27768,27770,27771,27772,27775,27776,27780,27783,27786,27787,27789,27790,27793,27794,27797,27798,27799,27800,27802,27804,27805,27806,27808,27810,27816,27820,27823,27824,27828,27829,27830,27831,27834,27840,27841,27842,27843,27846,27847,27848,27851,27853,27854,27855,27857,27858,27864,27865,27866,27868,27869,27871,27876,27878,27879,27881,27884,27885,27890,27892,27897,27903,27904,27906,27907,27909,27910,27912,27913,27914,27917,27919,27920,27921,27923,27924,27925,27926,27928,27932,27933,27935,27936,27937,27938,27939,27940,27942,27944,27945,27948,27949,27951,27952,27956,27958,27959,27960,27962,27967,27968,27970,27972,27977,27980,27984,27989,27990,27991,27992,27995,27997,27999,28001,28002,28004,28005,28007,28008,28011,28012,28013,28016,28017,28018,28019,28021,28022,28025,28026,28027,28029,28030,28031,28032,28033,28035,28036,28038,28039,28042,28043,28045,28047,28048,28050,28054,28055,28056,28057,28058,28060,28066,28069,28076,28077,28080,28081,28083,28084,28086,28087,28089,28090,28091,28092,28093,28094,28097,28098,28099,28104,28105,28106,28109,28110,28111,28112,28114,28115,28116,28117,28119,28122,28123,28124,28127,28130,28131,28133,28135,28136,28137,28138,28141,28143,28144,28146,28148,28149,28150,28152,28154,28157,28158,28159,28160,28161,28162,28163,28164,28166,28167,28168,28169,28171,28175,28178,28179,28181,28184,28185,28187,28188,28190,28191,28194,28198,28199,28200,28202,28204,28206,28208,28209,28211,28213,28214,28215,28217,28219,28220,28221,28222,28223,28224,28225,28226,28229,28230,28231,28232,28233,28234,28235,28236,28239,28240,28241,28242,28245,28247,28249,28250,28252,28253,28254,28256,28257,28258,28259,28260,28261,28262,28263,28264,28265,28266,28268,28269,28271,28272,28273,28274,28275,28276,28277,28278,28279,28280,28281,28282,28283,28284,28285,28288,28289,28290,28292,28295,28296,28298,28299,28300,28301,28302,28305,28306,28307,28308,28309,28310,28311,28313,28314,28315,28317,28318,28320,28321,28323,28324,28326,28328,28329,28331,28332,28333,28334,28336,28339,28341,28344,28345,28348,28350,28351,28352,28355,28356,28357,28358,28360,28361,28362,28364,28365,28366,28368,28370,28374,28376,28377,28379,28380,28381,28387,28391,28394,28395,28396,28397,28398,28399,28400,28401,28402,28403,28405,28406,28407,28408,28410,28411,28412,28413,28414,28415,28416,28417,28419,28420,28421,28423,28424,28426,28427,28428,28429,28430,28432,28433,28434,28438,28439,28440,28441,28442,28443,28444,28445,28446,28447,28449,28450,28451,28453,28454,28455,28456,28460,28462,28464,28466,28468,28469,28471,28472,28473,28474,28475,28476,28477,28479,28480,28481,28482,28483,28484,28485,28488,28489,28490,28492,28494,28495,28496,28497,28498,28499,28500,28501,28502,28503,28505,28506,28507,28509,28511,28512,28513,28515,28516,28517,28519,28520,28521,28522,28523,28524,28527,28528,28529,28531,28533,28534,28535,28537,28539,28541,28542,28543,28544,28545,28546,28547,28549,28550,28551,28554,28555,28559,28560,28561,28562,28563,28564,28565,28566,28567,28568,28569,28570,28571,28573,28574,28575,28576,28578,28579,28580,28581,28582,28584,28585,28586,28587,28588,28589,28590,28591,28592,28593,28594,28596,28597,28599,28600,28602,28603,28604,28605,28606,28607,28609,28611,28612,28613,28614,28615,28616,28618,28619,28620,28621,28622,28623,28624,28627,28628,28629,28630,28631,28632,28633,28634,28635,28636,28637,28639,28642,28643,28644,28645,28646,28647,28648,28649,28650,28651,28652,28653,28656,28657,28658,28659,28660,28661,28662,28663,28664,28665,28666,28667,28668,28669,28670,28671,28672,28673,28674,28675,28676,28677,28678,28679,28680,28681,28682,28683,28684,28685,28686,28687,28688,28690,28691,28692,28693,28694,28695,28696,28697,28700,28701,28702,28703,28704,28705,28706,28708,28709,28710,28711,28712,28713,28714,28715,28716,28717,28718,28719,28720,28721,28722,28723,28724,28726,28727,28728,28730,28731,28732,28733,28734,28735,28736,28737,28738,28739,28740,28741,28742,28743,28744,28745,28746,28747,28749,28750,28752,28753,28754,28755,28756,28757,28758,28759,28760,28761,28762,28763,28764,28765,28767,28768,28769,28770,28771,28772,28773,28774,28775,28776,28777,28778,28782,28785,28786,28787,28788,28791,28793,28794,28795,28797,28801,28802,28803,28804,28806,28807,28808,28811,28812,28813,28815,28816,28817,28819,28823,28824,28826,28827,28830,28831,28832,28833,28834,28835,28836,28837,28838,28839,28840,28841,28842,28848,28850,28852,28853,28854,28858,28862,28863,28868,28869,28870,28871,28873,28875,28876,28877,28878,28879,28880,28881,28882,28883,28884,28885,28886,28887,28890,28892,28893,28894,28896,28897,28898,28899,28901,28906,28910,28912,28913,28914,28915,28916,28917,28918,28920,28922,28923,28924,28926,28927,28928,28929,28930,28931,28932,28933,28934,28935,28936,28939,28940,28941,28942,28943,28945,28946,28948,28951,28955,28956,28957,28958,28959,28960,28961,28962,28963,28964,28965,28967,28968,28969,28970,28971,28972,28973,28974,28978,28979,28980,28981,28983,28984,28985,28986,28987,28988,28989,28990,28991,28992,28993,28994,28995,28996,28998,28999,29000,29001,29003,29005,29007,29008,29009,29010,29011,29012,29013,29014,29015,29016,29017,29018,29019,29021,29023,29024,29025,29026,29027,29029,29033,29034,29035,29036,29037,29039,29040,29041,29044,29045,29046,29047,29049,29051,29052,29054,29055,29056,29057,29058,29059,29061,29062,29063,29064,29065,29067,29068,29069,29070,29072,29073,29074,29075,29077,29078,29079,29082,29083,29084,29085,29086,29089,29090,29091,29092,29093,29094,29095,29097,29098,29099,29101,29102,29103,29104,29105,29106,29108,29110,29111,29112,29114,29115,29116,29117,29118,29119,29120,29121,29122,29124,29125,29126,29127,29128,29129,29130,29131,29132,29133,29135,29136,29137,29138,29139,29142,29143,29144,29145,29146,29147,29148,29149,29150,29151,29153,29154,29155,29156,29158,29160,29161,29162,29163,29164,29165,29167,29168,29169,29170,29171,29172,29173,29174,29175,29176,29178,29179,29180,29181,29182,29183,29184,29185,29186,29187,29188,29189,29191,29192,29193,29194,29195,29196,29197,29198,29199,29200,29201,29202,29203,29204,29205,29206,29207,29208,29209,29210,29211,29212,29214,29215,29216,29217,29218,29219,29220,29221,29222,29223,29225,29227,29229,29230,29231,29234,29235,29236,29242,29244,29246,29248,29249,29250,29251,29252,29253,29254,29257,29258,29259,29262,29263,29264,29265,29267,29268,29269,29271,29272,29274,29276,29278,29280,29283,29284,29285,29288,29290,29291,29292,29293,29296,29297,29299,29300,29302,29303,29304,29307,29308,29309,29314,29315,29317,29318,29319,29320,29321,29324,29326,29328,29329,29331,29332,29333,29334,29335,29336,29337,29338,29339,29340,29341,29342,29344,29345,29346,29347,29348,29349,29350,29351,29352,29353,29354,29355,29358,29361,29362,29363,29365,29370,29371,29372,29373,29374,29375,29376,29381,29382,29383,29385,29386,29387,29388,29391,29393,29395,29396,29397,29398,29400,29402,29403,58566,58567,58568,58569,58570,58571,58572,58573,58574,58575,58576,58577,58578,58579,58580,58581,58582,58583,58584,58585,58586,58587,58588,58589,58590,58591,58592,58593,58594,58595,58596,58597,58598,58599,58600,58601,58602,58603,58604,58605,58606,58607,58608,58609,58610,58611,58612,58613,58614,58615,58616,58617,58618,58619,58620,58621,58622,58623,58624,58625,58626,58627,58628,58629,58630,58631,58632,58633,58634,58635,58636,58637,58638,58639,58640,58641,58642,58643,58644,58645,58646,58647,58648,58649,58650,58651,58652,58653,58654,58655,58656,58657,58658,58659,58660,58661,12288,12289,12290,183,713,711,168,12291,12293,8212,65374,8214,8230,8216,8217,8220,8221,12308,12309,12296,12297,12298,12299,12300,12301,12302,12303,12310,12311,12304,12305,177,215,247,8758,8743,8744,8721,8719,8746,8745,8712,8759,8730,8869,8741,8736,8978,8857,8747,8750,8801,8780,8776,8765,8733,8800,8814,8815,8804,8805,8734,8757,8756,9794,9792,176,8242,8243,8451,65284,164,65504,65505,8240,167,8470,9734,9733,9675,9679,9678,9671,9670,9633,9632,9651,9650,8251,8594,8592,8593,8595,12307,58662,58663,58664,58665,58666,58667,58668,58669,58670,58671,58672,58673,58674,58675,58676,58677,58678,58679,58680,58681,58682,58683,58684,58685,58686,58687,58688,58689,58690,58691,58692,58693,58694,58695,58696,58697,58698,58699,58700,58701,58702,58703,58704,58705,58706,58707,58708,58709,58710,58711,58712,58713,58714,58715,58716,58717,58718,58719,58720,58721,58722,58723,58724,58725,58726,58727,58728,58729,58730,58731,58732,58733,58734,58735,58736,58737,58738,58739,58740,58741,58742,58743,58744,58745,58746,58747,58748,58749,58750,58751,58752,58753,58754,58755,58756,58757,8560,8561,8562,8563,8564,8565,8566,8567,8568,8569,59238,59239,59240,59241,59242,59243,9352,9353,9354,9355,9356,9357,9358,9359,9360,9361,9362,9363,9364,9365,9366,9367,9368,9369,9370,9371,9332,9333,9334,9335,9336,9337,9338,9339,9340,9341,9342,9343,9344,9345,9346,9347,9348,9349,9350,9351,9312,9313,9314,9315,9316,9317,9318,9319,9320,9321,8364,59245,12832,12833,12834,12835,12836,12837,12838,12839,12840,12841,59246,59247,8544,8545,8546,8547,8548,8549,8550,8551,8552,8553,8554,8555,59248,59249,58758,58759,58760,58761,58762,58763,58764,58765,58766,58767,58768,58769,58770,58771,58772,58773,58774,58775,58776,58777,58778,58779,58780,58781,58782,58783,58784,58785,58786,58787,58788,58789,58790,58791,58792,58793,58794,58795,58796,58797,58798,58799,58800,58801,58802,58803,58804,58805,58806,58807,58808,58809,58810,58811,58812,58813,58814,58815,58816,58817,58818,58819,58820,58821,58822,58823,58824,58825,58826,58827,58828,58829,58830,58831,58832,58833,58834,58835,58836,58837,58838,58839,58840,58841,58842,58843,58844,58845,58846,58847,58848,58849,58850,58851,58852,12288,65281,65282,65283,65509,65285,65286,65287,65288,65289,65290,65291,65292,65293,65294,65295,65296,65297,65298,65299,65300,65301,65302,65303,65304,65305,65306,65307,65308,65309,65310,65311,65312,65313,65314,65315,65316,65317,65318,65319,65320,65321,65322,65323,65324,65325,65326,65327,65328,65329,65330,65331,65332,65333,65334,65335,65336,65337,65338,65339,65340,65341,65342,65343,65344,65345,65346,65347,65348,65349,65350,65351,65352,65353,65354,65355,65356,65357,65358,65359,65360,65361,65362,65363,65364,65365,65366,65367,65368,65369,65370,65371,65372,65373,65507,58854,58855,58856,58857,58858,58859,58860,58861,58862,58863,58864,58865,58866,58867,58868,58869,58870,58871,58872,58873,58874,58875,58876,58877,58878,58879,58880,58881,58882,58883,58884,58885,58886,58887,58888,58889,58890,58891,58892,58893,58894,58895,58896,58897,58898,58899,58900,58901,58902,58903,58904,58905,58906,58907,58908,58909,58910,58911,58912,58913,58914,58915,58916,58917,58918,58919,58920,58921,58922,58923,58924,58925,58926,58927,58928,58929,58930,58931,58932,58933,58934,58935,58936,58937,58938,58939,58940,58941,58942,58943,58944,58945,58946,58947,58948,58949,12353,12354,12355,12356,12357,12358,12359,12360,12361,12362,12363,12364,12365,12366,12367,12368,12369,12370,12371,12372,12373,12374,12375,12376,12377,12378,12379,12380,12381,12382,12383,12384,12385,12386,12387,12388,12389,12390,12391,12392,12393,12394,12395,12396,12397,12398,12399,12400,12401,12402,12403,12404,12405,12406,12407,12408,12409,12410,12411,12412,12413,12414,12415,12416,12417,12418,12419,12420,12421,12422,12423,12424,12425,12426,12427,12428,12429,12430,12431,12432,12433,12434,12435,59250,59251,59252,59253,59254,59255,59256,59257,59258,59259,59260,58950,58951,58952,58953,58954,58955,58956,58957,58958,58959,58960,58961,58962,58963,58964,58965,58966,58967,58968,58969,58970,58971,58972,58973,58974,58975,58976,58977,58978,58979,58980,58981,58982,58983,58984,58985,58986,58987,58988,58989,58990,58991,58992,58993,58994,58995,58996,58997,58998,58999,59000,59001,59002,59003,59004,59005,59006,59007,59008,59009,59010,59011,59012,59013,59014,59015,59016,59017,59018,59019,59020,59021,59022,59023,59024,59025,59026,59027,59028,59029,59030,59031,59032,59033,59034,59035,59036,59037,59038,59039,59040,59041,59042,59043,59044,59045,12449,12450,12451,12452,12453,12454,12455,12456,12457,12458,12459,12460,12461,12462,12463,12464,12465,12466,12467,12468,12469,12470,12471,12472,12473,12474,12475,12476,12477,12478,12479,12480,12481,12482,12483,12484,12485,12486,12487,12488,12489,12490,12491,12492,12493,12494,12495,12496,12497,12498,12499,12500,12501,12502,12503,12504,12505,12506,12507,12508,12509,12510,12511,12512,12513,12514,12515,12516,12517,12518,12519,12520,12521,12522,12523,12524,12525,12526,12527,12528,12529,12530,12531,12532,12533,12534,59261,59262,59263,59264,59265,59266,59267,59268,59046,59047,59048,59049,59050,59051,59052,59053,59054,59055,59056,59057,59058,59059,59060,59061,59062,59063,59064,59065,59066,59067,59068,59069,59070,59071,59072,59073,59074,59075,59076,59077,59078,59079,59080,59081,59082,59083,59084,59085,59086,59087,59088,59089,59090,59091,59092,59093,59094,59095,59096,59097,59098,59099,59100,59101,59102,59103,59104,59105,59106,59107,59108,59109,59110,59111,59112,59113,59114,59115,59116,59117,59118,59119,59120,59121,59122,59123,59124,59125,59126,59127,59128,59129,59130,59131,59132,59133,59134,59135,59136,59137,59138,59139,59140,59141,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,931,932,933,934,935,936,937,59269,59270,59271,59272,59273,59274,59275,59276,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,963,964,965,966,967,968,969,59277,59278,59279,59280,59281,59282,59283,65077,65078,65081,65082,65087,65088,65085,65086,65089,65090,65091,65092,59284,59285,65083,65084,65079,65080,65073,59286,65075,65076,59287,59288,59289,59290,59291,59292,59293,59294,59295,59142,59143,59144,59145,59146,59147,59148,59149,59150,59151,59152,59153,59154,59155,59156,59157,59158,59159,59160,59161,59162,59163,59164,59165,59166,59167,59168,59169,59170,59171,59172,59173,59174,59175,59176,59177,59178,59179,59180,59181,59182,59183,59184,59185,59186,59187,59188,59189,59190,59191,59192,59193,59194,59195,59196,59197,59198,59199,59200,59201,59202,59203,59204,59205,59206,59207,59208,59209,59210,59211,59212,59213,59214,59215,59216,59217,59218,59219,59220,59221,59222,59223,59224,59225,59226,59227,59228,59229,59230,59231,59232,59233,59234,59235,59236,59237,1040,1041,1042,1043,1044,1045,1025,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,59296,59297,59298,59299,59300,59301,59302,59303,59304,59305,59306,59307,59308,59309,59310,1072,1073,1074,1075,1076,1077,1105,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,59311,59312,59313,59314,59315,59316,59317,59318,59319,59320,59321,59322,59323,714,715,729,8211,8213,8229,8245,8453,8457,8598,8599,8600,8601,8725,8735,8739,8786,8806,8807,8895,9552,9553,9554,9555,9556,9557,9558,9559,9560,9561,9562,9563,9564,9565,9566,9567,9568,9569,9570,9571,9572,9573,9574,9575,9576,9577,9578,9579,9580,9581,9582,9583,9584,9585,9586,9587,9601,9602,9603,9604,9605,9606,9607,9608,9609,9610,9611,9612,9613,9614,9615,9619,9620,9621,9660,9661,9698,9699,9700,9701,9737,8853,12306,12317,12318,59324,59325,59326,59327,59328,59329,59330,59331,59332,59333,59334,257,225,462,224,275,233,283,232,299,237,464,236,333,243,466,242,363,250,468,249,470,472,474,476,252,234,593,7743,324,328,505,609,59337,59338,59339,59340,12549,12550,12551,12552,12553,12554,12555,12556,12557,12558,12559,12560,12561,12562,12563,12564,12565,12566,12567,12568,12569,12570,12571,12572,12573,12574,12575,12576,12577,12578,12579,12580,12581,12582,12583,12584,12585,59341,59342,59343,59344,59345,59346,59347,59348,59349,59350,59351,59352,59353,59354,59355,59356,59357,59358,59359,59360,59361,12321,12322,12323,12324,12325,12326,12327,12328,12329,12963,13198,13199,13212,13213,13214,13217,13252,13262,13265,13266,13269,65072,65506,65508,59362,8481,12849,59363,8208,59364,59365,59366,12540,12443,12444,12541,12542,12294,12445,12446,65097,65098,65099,65100,65101,65102,65103,65104,65105,65106,65108,65109,65110,65111,65113,65114,65115,65116,65117,65118,65119,65120,65121,65122,65123,65124,65125,65126,65128,65129,65130,65131,12350,12272,12273,12274,12275,12276,12277,12278,12279,12280,12281,12282,12283,12295,59380,59381,59382,59383,59384,59385,59386,59387,59388,59389,59390,59391,59392,9472,9473,9474,9475,9476,9477,9478,9479,9480,9481,9482,9483,9484,9485,9486,9487,9488,9489,9490,9491,9492,9493,9494,9495,9496,9497,9498,9499,9500,9501,9502,9503,9504,9505,9506,9507,9508,9509,9510,9511,9512,9513,9514,9515,9516,9517,9518,9519,9520,9521,9522,9523,9524,9525,9526,9527,9528,9529,9530,9531,9532,9533,9534,9535,9536,9537,9538,9539,9540,9541,9542,9543,9544,9545,9546,9547,59393,59394,59395,59396,59397,59398,59399,59400,59401,59402,59403,59404,59405,59406,59407,29404,29405,29407,29410,29411,29412,29413,29414,29415,29418,29419,29429,29430,29433,29437,29438,29439,29440,29442,29444,29445,29446,29447,29448,29449,29451,29452,29453,29455,29456,29457,29458,29460,29464,29465,29466,29471,29472,29475,29476,29478,29479,29480,29485,29487,29488,29490,29491,29493,29494,29498,29499,29500,29501,29504,29505,29506,29507,29508,29509,29510,29511,29512,29513,29514,29515,29516,29518,29519,29521,29523,29524,29525,29526,29528,29529,29530,29531,29532,29533,29534,29535,29537,29538,29539,29540,29541,29542,29543,29544,29545,29546,29547,29550,29552,29553,57344,57345,57346,57347,57348,57349,57350,57351,57352,57353,57354,57355,57356,57357,57358,57359,57360,57361,57362,57363,57364,57365,57366,57367,57368,57369,57370,57371,57372,57373,57374,57375,57376,57377,57378,57379,57380,57381,57382,57383,57384,57385,57386,57387,57388,57389,57390,57391,57392,57393,57394,57395,57396,57397,57398,57399,57400,57401,57402,57403,57404,57405,57406,57407,57408,57409,57410,57411,57412,57413,57414,57415,57416,57417,57418,57419,57420,57421,57422,57423,57424,57425,57426,57427,57428,57429,57430,57431,57432,57433,57434,57435,57436,57437,29554,29555,29556,29557,29558,29559,29560,29561,29562,29563,29564,29565,29567,29568,29569,29570,29571,29573,29574,29576,29578,29580,29581,29583,29584,29586,29587,29588,29589,29591,29592,29593,29594,29596,29597,29598,29600,29601,29603,29604,29605,29606,29607,29608,29610,29612,29613,29617,29620,29621,29622,29624,29625,29628,29629,29630,29631,29633,29635,29636,29637,29638,29639,29643,29644,29646,29650,29651,29652,29653,29654,29655,29656,29658,29659,29660,29661,29663,29665,29666,29667,29668,29670,29672,29674,29675,29676,29678,29679,29680,29681,29683,29684,29685,29686,29687,57438,57439,57440,57441,57442,57443,57444,57445,57446,57447,57448,57449,57450,57451,57452,57453,57454,57455,57456,57457,57458,57459,57460,57461,57462,57463,57464,57465,57466,57467,57468,57469,57470,57471,57472,57473,57474,57475,57476,57477,57478,57479,57480,57481,57482,57483,57484,57485,57486,57487,57488,57489,57490,57491,57492,57493,57494,57495,57496,57497,57498,57499,57500,57501,57502,57503,57504,57505,57506,57507,57508,57509,57510,57511,57512,57513,57514,57515,57516,57517,57518,57519,57520,57521,57522,57523,57524,57525,57526,57527,57528,57529,57530,57531,29688,29689,29690,29691,29692,29693,29694,29695,29696,29697,29698,29700,29703,29704,29707,29708,29709,29710,29713,29714,29715,29716,29717,29718,29719,29720,29721,29724,29725,29726,29727,29728,29729,29731,29732,29735,29737,29739,29741,29743,29745,29746,29751,29752,29753,29754,29755,29757,29758,29759,29760,29762,29763,29764,29765,29766,29767,29768,29769,29770,29771,29772,29773,29774,29775,29776,29777,29778,29779,29780,29782,29784,29789,29792,29793,29794,29795,29796,29797,29798,29799,29800,29801,29802,29803,29804,29806,29807,29809,29810,29811,29812,29813,29816,29817,29818,57532,57533,57534,57535,57536,57537,57538,57539,57540,57541,57542,57543,57544,57545,57546,57547,57548,57549,57550,57551,57552,57553,57554,57555,57556,57557,57558,57559,57560,57561,57562,57563,57564,57565,57566,57567,57568,57569,57570,57571,57572,57573,57574,57575,57576,57577,57578,57579,57580,57581,57582,57583,57584,57585,57586,57587,57588,57589,57590,57591,57592,57593,57594,57595,57596,57597,57598,57599,57600,57601,57602,57603,57604,57605,57606,57607,57608,57609,57610,57611,57612,57613,57614,57615,57616,57617,57618,57619,57620,57621,57622,57623,57624,57625,29819,29820,29821,29823,29826,29828,29829,29830,29832,29833,29834,29836,29837,29839,29841,29842,29843,29844,29845,29846,29847,29848,29849,29850,29851,29853,29855,29856,29857,29858,29859,29860,29861,29862,29866,29867,29868,29869,29870,29871,29872,29873,29874,29875,29876,29877,29878,29879,29880,29881,29883,29884,29885,29886,29887,29888,29889,29890,29891,29892,29893,29894,29895,29896,29897,29898,29899,29900,29901,29902,29903,29904,29905,29907,29908,29909,29910,29911,29912,29913,29914,29915,29917,29919,29921,29925,29927,29928,29929,29930,29931,29932,29933,29936,29937,29938,57626,57627,57628,57629,57630,57631,57632,57633,57634,57635,57636,57637,57638,57639,57640,57641,57642,57643,57644,57645,57646,57647,57648,57649,57650,57651,57652,57653,57654,57655,57656,57657,57658,57659,57660,57661,57662,57663,57664,57665,57666,57667,57668,57669,57670,57671,57672,57673,57674,57675,57676,57677,57678,57679,57680,57681,57682,57683,57684,57685,57686,57687,57688,57689,57690,57691,57692,57693,57694,57695,57696,57697,57698,57699,57700,57701,57702,57703,57704,57705,57706,57707,57708,57709,57710,57711,57712,57713,57714,57715,57716,57717,57718,57719,29939,29941,29944,29945,29946,29947,29948,29949,29950,29952,29953,29954,29955,29957,29958,29959,29960,29961,29962,29963,29964,29966,29968,29970,29972,29973,29974,29975,29979,29981,29982,29984,29985,29986,29987,29988,29990,29991,29994,29998,30004,30006,30009,30012,30013,30015,30017,30018,30019,30020,30022,30023,30025,30026,30029,30032,30033,30034,30035,30037,30038,30039,30040,30045,30046,30047,30048,30049,30050,30051,30052,30055,30056,30057,30059,30060,30061,30062,30063,30064,30065,30067,30069,30070,30071,30074,30075,30076,30077,30078,30080,30081,30082,30084,30085,30087,57720,57721,57722,57723,57724,57725,57726,57727,57728,57729,57730,57731,57732,57733,57734,57735,57736,57737,57738,57739,57740,57741,57742,57743,57744,57745,57746,57747,57748,57749,57750,57751,57752,57753,57754,57755,57756,57757,57758,57759,57760,57761,57762,57763,57764,57765,57766,57767,57768,57769,57770,57771,57772,57773,57774,57775,57776,57777,57778,57779,57780,57781,57782,57783,57784,57785,57786,57787,57788,57789,57790,57791,57792,57793,57794,57795,57796,57797,57798,57799,57800,57801,57802,57803,57804,57805,57806,57807,57808,57809,57810,57811,57812,57813,30088,30089,30090,30092,30093,30094,30096,30099,30101,30104,30107,30108,30110,30114,30118,30119,30120,30121,30122,30125,30134,30135,30138,30139,30143,30144,30145,30150,30155,30156,30158,30159,30160,30161,30163,30167,30169,30170,30172,30173,30175,30176,30177,30181,30185,30188,30189,30190,30191,30194,30195,30197,30198,30199,30200,30202,30203,30205,30206,30210,30212,30214,30215,30216,30217,30219,30221,30222,30223,30225,30226,30227,30228,30230,30234,30236,30237,30238,30241,30243,30247,30248,30252,30254,30255,30257,30258,30262,30263,30265,30266,30267,30269,30273,30274,30276,57814,57815,57816,57817,57818,57819,57820,57821,57822,57823,57824,57825,57826,57827,57828,57829,57830,57831,57832,57833,57834,57835,57836,57837,57838,57839,57840,57841,57842,57843,57844,57845,57846,57847,57848,57849,57850,57851,57852,57853,57854,57855,57856,57857,57858,57859,57860,57861,57862,57863,57864,57865,57866,57867,57868,57869,57870,57871,57872,57873,57874,57875,57876,57877,57878,57879,57880,57881,57882,57883,57884,57885,57886,57887,57888,57889,57890,57891,57892,57893,57894,57895,57896,57897,57898,57899,57900,57901,57902,57903,57904,57905,57906,57907,30277,30278,30279,30280,30281,30282,30283,30286,30287,30288,30289,30290,30291,30293,30295,30296,30297,30298,30299,30301,30303,30304,30305,30306,30308,30309,30310,30311,30312,30313,30314,30316,30317,30318,30320,30321,30322,30323,30324,30325,30326,30327,30329,30330,30332,30335,30336,30337,30339,30341,30345,30346,30348,30349,30351,30352,30354,30356,30357,30359,30360,30362,30363,30364,30365,30366,30367,30368,30369,30370,30371,30373,30374,30375,30376,30377,30378,30379,30380,30381,30383,30384,30387,30389,30390,30391,30392,30393,30394,30395,30396,30397,30398,30400,30401,30403,21834,38463,22467,25384,21710,21769,21696,30353,30284,34108,30702,33406,30861,29233,38552,38797,27688,23433,20474,25353,26263,23736,33018,26696,32942,26114,30414,20985,25942,29100,32753,34948,20658,22885,25034,28595,33453,25420,25170,21485,21543,31494,20843,30116,24052,25300,36299,38774,25226,32793,22365,38712,32610,29240,30333,26575,30334,25670,20336,36133,25308,31255,26001,29677,25644,25203,33324,39041,26495,29256,25198,25292,20276,29923,21322,21150,32458,37030,24110,26758,27036,33152,32465,26834,30917,34444,38225,20621,35876,33502,32990,21253,35090,21093,30404,30407,30409,30411,30412,30419,30421,30425,30426,30428,30429,30430,30432,30433,30434,30435,30436,30438,30439,30440,30441,30442,30443,30444,30445,30448,30451,30453,30454,30455,30458,30459,30461,30463,30464,30466,30467,30469,30470,30474,30476,30478,30479,30480,30481,30482,30483,30484,30485,30486,30487,30488,30491,30492,30493,30494,30497,30499,30500,30501,30503,30506,30507,30508,30510,30512,30513,30514,30515,30516,30521,30523,30525,30526,30527,30530,30532,30533,30534,30536,30537,30538,30539,30540,30541,30542,30543,30546,30547,30548,30549,30550,30551,30552,30553,30556,34180,38649,20445,22561,39281,23453,25265,25253,26292,35961,40077,29190,26479,30865,24754,21329,21271,36744,32972,36125,38049,20493,29384,22791,24811,28953,34987,22868,33519,26412,31528,23849,32503,29997,27893,36454,36856,36924,40763,27604,37145,31508,24444,30887,34006,34109,27605,27609,27606,24065,24199,30201,38381,25949,24330,24517,36767,22721,33218,36991,38491,38829,36793,32534,36140,25153,20415,21464,21342,36776,36777,36779,36941,26631,24426,33176,34920,40150,24971,21035,30250,24428,25996,28626,28392,23486,25672,20853,20912,26564,19993,31177,39292,28851,30557,30558,30559,30560,30564,30567,30569,30570,30573,30574,30575,30576,30577,30578,30579,30580,30581,30582,30583,30584,30586,30587,30588,30593,30594,30595,30598,30599,30600,30601,30602,30603,30607,30608,30611,30612,30613,30614,30615,30616,30617,30618,30619,30620,30621,30622,30625,30627,30628,30630,30632,30635,30637,30638,30639,30641,30642,30644,30646,30647,30648,30649,30650,30652,30654,30656,30657,30658,30659,30660,30661,30662,30663,30664,30665,30666,30667,30668,30670,30671,30672,30673,30674,30675,30676,30677,30678,30680,30681,30682,30685,30686,30687,30688,30689,30692,30149,24182,29627,33760,25773,25320,38069,27874,21338,21187,25615,38082,31636,20271,24091,33334,33046,33162,28196,27850,39539,25429,21340,21754,34917,22496,19981,24067,27493,31807,37096,24598,25830,29468,35009,26448,25165,36130,30572,36393,37319,24425,33756,34081,39184,21442,34453,27531,24813,24808,28799,33485,33329,20179,27815,34255,25805,31961,27133,26361,33609,21397,31574,20391,20876,27979,23618,36461,25554,21449,33580,33590,26597,30900,25661,23519,23700,24046,35815,25286,26612,35962,25600,25530,34633,39307,35863,32544,38130,20135,38416,39076,26124,29462,30694,30696,30698,30703,30704,30705,30706,30708,30709,30711,30713,30714,30715,30716,30723,30724,30725,30726,30727,30728,30730,30731,30734,30735,30736,30739,30741,30745,30747,30750,30752,30753,30754,30756,30760,30762,30763,30766,30767,30769,30770,30771,30773,30774,30781,30783,30785,30786,30787,30788,30790,30792,30793,30794,30795,30797,30799,30801,30803,30804,30808,30809,30810,30811,30812,30814,30815,30816,30817,30818,30819,30820,30821,30822,30823,30824,30825,30831,30832,30833,30834,30835,30836,30837,30838,30840,30841,30842,30843,30845,30846,30847,30848,30849,30850,30851,22330,23581,24120,38271,20607,32928,21378,25950,30021,21809,20513,36229,25220,38046,26397,22066,28526,24034,21557,28818,36710,25199,25764,25507,24443,28552,37108,33251,36784,23576,26216,24561,27785,38472,36225,34924,25745,31216,22478,27225,25104,21576,20056,31243,24809,28548,35802,25215,36894,39563,31204,21507,30196,25345,21273,27744,36831,24347,39536,32827,40831,20360,23610,36196,32709,26021,28861,20805,20914,34411,23815,23456,25277,37228,30068,36364,31264,24833,31609,20167,32504,30597,19985,33261,21021,20986,27249,21416,36487,38148,38607,28353,38500,26970,30852,30853,30854,30856,30858,30859,30863,30864,30866,30868,30869,30870,30873,30877,30878,30880,30882,30884,30886,30888,30889,30890,30891,30892,30893,30894,30895,30901,30902,30903,30904,30906,30907,30908,30909,30911,30912,30914,30915,30916,30918,30919,30920,30924,30925,30926,30927,30929,30930,30931,30934,30935,30936,30938,30939,30940,30941,30942,30943,30944,30945,30946,30947,30948,30949,30950,30951,30953,30954,30955,30957,30958,30959,30960,30961,30963,30965,30966,30968,30969,30971,30972,30973,30974,30975,30976,30978,30979,30980,30982,30983,30984,30985,30986,30987,30988,30784,20648,30679,25616,35302,22788,25571,24029,31359,26941,20256,33337,21912,20018,30126,31383,24162,24202,38383,21019,21561,28810,25462,38180,22402,26149,26943,37255,21767,28147,32431,34850,25139,32496,30133,33576,30913,38604,36766,24904,29943,35789,27492,21050,36176,27425,32874,33905,22257,21254,20174,19995,20945,31895,37259,31751,20419,36479,31713,31388,25703,23828,20652,33030,30209,31929,28140,32736,26449,23384,23544,30923,25774,25619,25514,25387,38169,25645,36798,31572,30249,25171,22823,21574,27513,20643,25140,24102,27526,20195,36151,34955,24453,36910,30989,30990,30991,30992,30993,30994,30996,30997,30998,30999,31000,31001,31002,31003,31004,31005,31007,31008,31009,31010,31011,31013,31014,31015,31016,31017,31018,31019,31020,31021,31022,31023,31024,31025,31026,31027,31029,31030,31031,31032,31033,31037,31039,31042,31043,31044,31045,31047,31050,31051,31052,31053,31054,31055,31056,31057,31058,31060,31061,31064,31065,31073,31075,31076,31078,31081,31082,31083,31084,31086,31088,31089,31090,31091,31092,31093,31094,31097,31099,31100,31101,31102,31103,31106,31107,31110,31111,31112,31113,31115,31116,31117,31118,31120,31121,31122,24608,32829,25285,20025,21333,37112,25528,32966,26086,27694,20294,24814,28129,35806,24377,34507,24403,25377,20826,33633,26723,20992,25443,36424,20498,23707,31095,23548,21040,31291,24764,36947,30423,24503,24471,30340,36460,28783,30331,31561,30634,20979,37011,22564,20302,28404,36842,25932,31515,29380,28068,32735,23265,25269,24213,22320,33922,31532,24093,24351,36882,32532,39072,25474,28359,30872,28857,20856,38747,22443,30005,20291,30008,24215,24806,22880,28096,27583,30857,21500,38613,20939,20993,25481,21514,38035,35843,36300,29241,30879,34678,36845,35853,21472,31123,31124,31125,31126,31127,31128,31129,31131,31132,31133,31134,31135,31136,31137,31138,31139,31140,31141,31142,31144,31145,31146,31147,31148,31149,31150,31151,31152,31153,31154,31156,31157,31158,31159,31160,31164,31167,31170,31172,31173,31175,31176,31178,31180,31182,31183,31184,31187,31188,31190,31191,31193,31194,31195,31196,31197,31198,31200,31201,31202,31205,31208,31210,31212,31214,31217,31218,31219,31220,31221,31222,31223,31225,31226,31228,31230,31231,31233,31236,31237,31239,31240,31241,31242,31244,31247,31248,31249,31250,31251,31253,31254,31256,31257,31259,31260,19969,30447,21486,38025,39030,40718,38189,23450,35746,20002,19996,20908,33891,25026,21160,26635,20375,24683,20923,27934,20828,25238,26007,38497,35910,36887,30168,37117,30563,27602,29322,29420,35835,22581,30585,36172,26460,38208,32922,24230,28193,22930,31471,30701,38203,27573,26029,32526,22534,20817,38431,23545,22697,21544,36466,25958,39039,22244,38045,30462,36929,25479,21702,22810,22842,22427,36530,26421,36346,33333,21057,24816,22549,34558,23784,40517,20420,39069,35769,23077,24694,21380,25212,36943,37122,39295,24681,32780,20799,32819,23572,39285,27953,20108,31261,31263,31265,31266,31268,31269,31270,31271,31272,31273,31274,31275,31276,31277,31278,31279,31280,31281,31282,31284,31285,31286,31288,31290,31294,31296,31297,31298,31299,31300,31301,31303,31304,31305,31306,31307,31308,31309,31310,31311,31312,31314,31315,31316,31317,31318,31320,31321,31322,31323,31324,31325,31326,31327,31328,31329,31330,31331,31332,31333,31334,31335,31336,31337,31338,31339,31340,31341,31342,31343,31345,31346,31347,31349,31355,31356,31357,31358,31362,31365,31367,31369,31370,31371,31372,31374,31375,31376,31379,31380,31385,31386,31387,31390,31393,31394,36144,21457,32602,31567,20240,20047,38400,27861,29648,34281,24070,30058,32763,27146,30718,38034,32321,20961,28902,21453,36820,33539,36137,29359,39277,27867,22346,33459,26041,32938,25151,38450,22952,20223,35775,32442,25918,33778,38750,21857,39134,32933,21290,35837,21536,32954,24223,27832,36153,33452,37210,21545,27675,20998,32439,22367,28954,27774,31881,22859,20221,24575,24868,31914,20016,23553,26539,34562,23792,38155,39118,30127,28925,36898,20911,32541,35773,22857,20964,20315,21542,22827,25975,32932,23413,25206,25282,36752,24133,27679,31526,20239,20440,26381,31395,31396,31399,31401,31402,31403,31406,31407,31408,31409,31410,31412,31413,31414,31415,31416,31417,31418,31419,31420,31421,31422,31424,31425,31426,31427,31428,31429,31430,31431,31432,31433,31434,31436,31437,31438,31439,31440,31441,31442,31443,31444,31445,31447,31448,31450,31451,31452,31453,31457,31458,31460,31463,31464,31465,31466,31467,31468,31470,31472,31473,31474,31475,31476,31477,31478,31479,31480,31483,31484,31486,31488,31489,31490,31493,31495,31497,31500,31501,31502,31504,31506,31507,31510,31511,31512,31514,31516,31517,31519,31521,31522,31523,31527,31529,31533,28014,28074,31119,34993,24343,29995,25242,36741,20463,37340,26023,33071,33105,24220,33104,36212,21103,35206,36171,22797,20613,20184,38428,29238,33145,36127,23500,35747,38468,22919,32538,21648,22134,22030,35813,25913,27010,38041,30422,28297,24178,29976,26438,26577,31487,32925,36214,24863,31174,25954,36195,20872,21018,38050,32568,32923,32434,23703,28207,26464,31705,30347,39640,33167,32660,31957,25630,38224,31295,21578,21733,27468,25601,25096,40509,33011,30105,21106,38761,33883,26684,34532,38401,38548,38124,20010,21508,32473,26681,36319,32789,26356,24218,32697,31535,31536,31538,31540,31541,31542,31543,31545,31547,31549,31551,31552,31553,31554,31555,31556,31558,31560,31562,31565,31566,31571,31573,31575,31577,31580,31582,31583,31585,31587,31588,31589,31590,31591,31592,31593,31594,31595,31596,31597,31599,31600,31603,31604,31606,31608,31610,31612,31613,31615,31617,31618,31619,31620,31622,31623,31624,31625,31626,31627,31628,31630,31631,31633,31634,31635,31638,31640,31641,31642,31643,31646,31647,31648,31651,31652,31653,31662,31663,31664,31666,31667,31669,31670,31671,31673,31674,31675,31676,31677,31678,31679,31680,31682,31683,31684,22466,32831,26775,24037,25915,21151,24685,40858,20379,36524,20844,23467,24339,24041,27742,25329,36129,20849,38057,21246,27807,33503,29399,22434,26500,36141,22815,36764,33735,21653,31629,20272,27837,23396,22993,40723,21476,34506,39592,35895,32929,25925,39038,22266,38599,21038,29916,21072,23521,25346,35074,20054,25296,24618,26874,20851,23448,20896,35266,31649,39302,32592,24815,28748,36143,20809,24191,36891,29808,35268,22317,30789,24402,40863,38394,36712,39740,35809,30328,26690,26588,36330,36149,21053,36746,28378,26829,38149,37101,22269,26524,35065,36807,21704,31685,31688,31689,31690,31691,31693,31694,31695,31696,31698,31700,31701,31702,31703,31704,31707,31708,31710,31711,31712,31714,31715,31716,31719,31720,31721,31723,31724,31725,31727,31728,31730,31731,31732,31733,31734,31736,31737,31738,31739,31741,31743,31744,31745,31746,31747,31748,31749,31750,31752,31753,31754,31757,31758,31760,31761,31762,31763,31764,31765,31767,31768,31769,31770,31771,31772,31773,31774,31776,31777,31778,31779,31780,31781,31784,31785,31787,31788,31789,31790,31791,31792,31793,31794,31795,31796,31797,31798,31799,31801,31802,31803,31804,31805,31806,31810,39608,23401,28023,27686,20133,23475,39559,37219,25000,37039,38889,21547,28085,23506,20989,21898,32597,32752,25788,25421,26097,25022,24717,28938,27735,27721,22831,26477,33322,22741,22158,35946,27627,37085,22909,32791,21495,28009,21621,21917,33655,33743,26680,31166,21644,20309,21512,30418,35977,38402,27827,28088,36203,35088,40548,36154,22079,40657,30165,24456,29408,24680,21756,20136,27178,34913,24658,36720,21700,28888,34425,40511,27946,23439,24344,32418,21897,20399,29492,21564,21402,20505,21518,21628,20046,24573,29786,22774,33899,32993,34676,29392,31946,28246,31811,31812,31813,31814,31815,31816,31817,31818,31819,31820,31822,31823,31824,31825,31826,31827,31828,31829,31830,31831,31832,31833,31834,31835,31836,31837,31838,31839,31840,31841,31842,31843,31844,31845,31846,31847,31848,31849,31850,31851,31852,31853,31854,31855,31856,31857,31858,31861,31862,31863,31864,31865,31866,31870,31871,31872,31873,31874,31875,31876,31877,31878,31879,31880,31882,31883,31884,31885,31886,31887,31888,31891,31892,31894,31897,31898,31899,31904,31905,31907,31910,31911,31912,31913,31915,31916,31917,31919,31920,31924,31925,31926,31927,31928,31930,31931,24359,34382,21804,25252,20114,27818,25143,33457,21719,21326,29502,28369,30011,21010,21270,35805,27088,24458,24576,28142,22351,27426,29615,26707,36824,32531,25442,24739,21796,30186,35938,28949,28067,23462,24187,33618,24908,40644,30970,34647,31783,30343,20976,24822,29004,26179,24140,24653,35854,28784,25381,36745,24509,24674,34516,22238,27585,24724,24935,21321,24800,26214,36159,31229,20250,28905,27719,35763,35826,32472,33636,26127,23130,39746,27985,28151,35905,27963,20249,28779,33719,25110,24785,38669,36135,31096,20987,22334,22522,26426,30072,31293,31215,31637,31935,31936,31938,31939,31940,31942,31945,31947,31950,31951,31952,31953,31954,31955,31956,31960,31962,31963,31965,31966,31969,31970,31971,31972,31973,31974,31975,31977,31978,31979,31980,31981,31982,31984,31985,31986,31987,31988,31989,31990,31991,31993,31994,31996,31997,31998,31999,32000,32001,32002,32003,32004,32005,32006,32007,32008,32009,32011,32012,32013,32014,32015,32016,32017,32018,32019,32020,32021,32022,32023,32024,32025,32026,32027,32028,32029,32030,32031,32033,32035,32036,32037,32038,32040,32041,32042,32044,32045,32046,32048,32049,32050,32051,32052,32053,32054,32908,39269,36857,28608,35749,40481,23020,32489,32521,21513,26497,26840,36753,31821,38598,21450,24613,30142,27762,21363,23241,32423,25380,20960,33034,24049,34015,25216,20864,23395,20238,31085,21058,24760,27982,23492,23490,35745,35760,26082,24524,38469,22931,32487,32426,22025,26551,22841,20339,23478,21152,33626,39050,36158,30002,38078,20551,31292,20215,26550,39550,23233,27516,30417,22362,23574,31546,38388,29006,20860,32937,33392,22904,32516,33575,26816,26604,30897,30839,25315,25441,31616,20461,21098,20943,33616,27099,37492,36341,36145,35265,38190,31661,20214,32055,32056,32057,32058,32059,32060,32061,32062,32063,32064,32065,32066,32067,32068,32069,32070,32071,32072,32073,32074,32075,32076,32077,32078,32079,32080,32081,32082,32083,32084,32085,32086,32087,32088,32089,32090,32091,32092,32093,32094,32095,32096,32097,32098,32099,32100,32101,32102,32103,32104,32105,32106,32107,32108,32109,32111,32112,32113,32114,32115,32116,32117,32118,32120,32121,32122,32123,32124,32125,32126,32127,32128,32129,32130,32131,32132,32133,32134,32135,32136,32137,32138,32139,32140,32141,32142,32143,32144,32145,32146,32147,32148,32149,32150,32151,32152,20581,33328,21073,39279,28176,28293,28071,24314,20725,23004,23558,27974,27743,30086,33931,26728,22870,35762,21280,37233,38477,34121,26898,30977,28966,33014,20132,37066,27975,39556,23047,22204,25605,38128,30699,20389,33050,29409,35282,39290,32564,32478,21119,25945,37237,36735,36739,21483,31382,25581,25509,30342,31224,34903,38454,25130,21163,33410,26708,26480,25463,30571,31469,27905,32467,35299,22992,25106,34249,33445,30028,20511,20171,30117,35819,23626,24062,31563,26020,37329,20170,27941,35167,32039,38182,20165,35880,36827,38771,26187,31105,36817,28908,28024,32153,32154,32155,32156,32157,32158,32159,32160,32161,32162,32163,32164,32165,32167,32168,32169,32170,32171,32172,32173,32175,32176,32177,32178,32179,32180,32181,32182,32183,32184,32185,32186,32187,32188,32189,32190,32191,32192,32193,32194,32195,32196,32197,32198,32199,32200,32201,32202,32203,32204,32205,32206,32207,32208,32209,32210,32211,32212,32213,32214,32215,32216,32217,32218,32219,32220,32221,32222,32223,32224,32225,32226,32227,32228,32229,32230,32231,32232,32233,32234,32235,32236,32237,32238,32239,32240,32241,32242,32243,32244,32245,32246,32247,32248,32249,32250,23613,21170,33606,20834,33550,30555,26230,40120,20140,24778,31934,31923,32463,20117,35686,26223,39048,38745,22659,25964,38236,24452,30153,38742,31455,31454,20928,28847,31384,25578,31350,32416,29590,38893,20037,28792,20061,37202,21417,25937,26087,33276,33285,21646,23601,30106,38816,25304,29401,30141,23621,39545,33738,23616,21632,30697,20030,27822,32858,25298,25454,24040,20855,36317,36382,38191,20465,21477,24807,28844,21095,25424,40515,23071,20518,30519,21367,32482,25733,25899,25225,25496,20500,29237,35273,20915,35776,32477,22343,33740,38055,20891,21531,23803,32251,32252,32253,32254,32255,32256,32257,32258,32259,32260,32261,32262,32263,32264,32265,32266,32267,32268,32269,32270,32271,32272,32273,32274,32275,32276,32277,32278,32279,32280,32281,32282,32283,32284,32285,32286,32287,32288,32289,32290,32291,32292,32293,32294,32295,32296,32297,32298,32299,32300,32301,32302,32303,32304,32305,32306,32307,32308,32309,32310,32311,32312,32313,32314,32316,32317,32318,32319,32320,32322,32323,32324,32325,32326,32328,32329,32330,32331,32332,32333,32334,32335,32336,32337,32338,32339,32340,32341,32342,32343,32344,32345,32346,32347,32348,32349,20426,31459,27994,37089,39567,21888,21654,21345,21679,24320,25577,26999,20975,24936,21002,22570,21208,22350,30733,30475,24247,24951,31968,25179,25239,20130,28821,32771,25335,28900,38752,22391,33499,26607,26869,30933,39063,31185,22771,21683,21487,28212,20811,21051,23458,35838,32943,21827,22438,24691,22353,21549,31354,24656,23380,25511,25248,21475,25187,23495,26543,21741,31391,33510,37239,24211,35044,22840,22446,25358,36328,33007,22359,31607,20393,24555,23485,27454,21281,31568,29378,26694,30719,30518,26103,20917,20111,30420,23743,31397,33909,22862,39745,20608,32350,32351,32352,32353,32354,32355,32356,32357,32358,32359,32360,32361,32362,32363,32364,32365,32366,32367,32368,32369,32370,32371,32372,32373,32374,32375,32376,32377,32378,32379,32380,32381,32382,32383,32384,32385,32387,32388,32389,32390,32391,32392,32393,32394,32395,32396,32397,32398,32399,32400,32401,32402,32403,32404,32405,32406,32407,32408,32409,32410,32412,32413,32414,32430,32436,32443,32444,32470,32484,32492,32505,32522,32528,32542,32567,32569,32571,32572,32573,32574,32575,32576,32577,32579,32582,32583,32584,32585,32586,32587,32588,32589,32590,32591,32594,32595,39304,24871,28291,22372,26118,25414,22256,25324,25193,24275,38420,22403,25289,21895,34593,33098,36771,21862,33713,26469,36182,34013,23146,26639,25318,31726,38417,20848,28572,35888,25597,35272,25042,32518,28866,28389,29701,27028,29436,24266,37070,26391,28010,25438,21171,29282,32769,20332,23013,37226,28889,28061,21202,20048,38647,38253,34174,30922,32047,20769,22418,25794,32907,31867,27882,26865,26974,20919,21400,26792,29313,40654,31729,29432,31163,28435,29702,26446,37324,40100,31036,33673,33620,21519,26647,20029,21385,21169,30782,21382,21033,20616,20363,20432,32598,32601,32603,32604,32605,32606,32608,32611,32612,32613,32614,32615,32619,32620,32621,32623,32624,32627,32629,32630,32631,32632,32634,32635,32636,32637,32639,32640,32642,32643,32644,32645,32646,32647,32648,32649,32651,32653,32655,32656,32657,32658,32659,32661,32662,32663,32664,32665,32667,32668,32672,32674,32675,32677,32678,32680,32681,32682,32683,32684,32685,32686,32689,32691,32692,32693,32694,32695,32698,32699,32702,32704,32706,32707,32708,32710,32711,32712,32713,32715,32717,32719,32720,32721,32722,32723,32726,32727,32729,32730,32731,32732,32733,32734,32738,32739,30178,31435,31890,27813,38582,21147,29827,21737,20457,32852,33714,36830,38256,24265,24604,28063,24088,25947,33080,38142,24651,28860,32451,31918,20937,26753,31921,33391,20004,36742,37327,26238,20142,35845,25769,32842,20698,30103,29134,23525,36797,28518,20102,25730,38243,24278,26009,21015,35010,28872,21155,29454,29747,26519,30967,38678,20020,37051,40158,28107,20955,36161,21533,25294,29618,33777,38646,40836,38083,20278,32666,20940,28789,38517,23725,39046,21478,20196,28316,29705,27060,30827,39311,30041,21016,30244,27969,26611,20845,40857,32843,21657,31548,31423,32740,32743,32744,32746,32747,32748,32749,32751,32754,32756,32757,32758,32759,32760,32761,32762,32765,32766,32767,32770,32775,32776,32777,32778,32782,32783,32785,32787,32794,32795,32797,32798,32799,32801,32803,32804,32811,32812,32813,32814,32815,32816,32818,32820,32825,32826,32828,32830,32832,32833,32836,32837,32839,32840,32841,32846,32847,32848,32849,32851,32853,32854,32855,32857,32859,32860,32861,32862,32863,32864,32865,32866,32867,32868,32869,32870,32871,32872,32875,32876,32877,32878,32879,32880,32882,32883,32884,32885,32886,32887,32888,32889,32890,32891,32892,32893,38534,22404,25314,38471,27004,23044,25602,31699,28431,38475,33446,21346,39045,24208,28809,25523,21348,34383,40065,40595,30860,38706,36335,36162,40575,28510,31108,24405,38470,25134,39540,21525,38109,20387,26053,23653,23649,32533,34385,27695,24459,29575,28388,32511,23782,25371,23402,28390,21365,20081,25504,30053,25249,36718,20262,20177,27814,32438,35770,33821,34746,32599,36923,38179,31657,39585,35064,33853,27931,39558,32476,22920,40635,29595,30721,34434,39532,39554,22043,21527,22475,20080,40614,21334,36808,33033,30610,39314,34542,28385,34067,26364,24930,28459,32894,32897,32898,32901,32904,32906,32909,32910,32911,32912,32913,32914,32916,32917,32919,32921,32926,32931,32934,32935,32936,32940,32944,32947,32949,32950,32952,32953,32955,32965,32967,32968,32969,32970,32971,32975,32976,32977,32978,32979,32980,32981,32984,32991,32992,32994,32995,32998,33006,33013,33015,33017,33019,33022,33023,33024,33025,33027,33028,33029,33031,33032,33035,33036,33045,33047,33049,33051,33052,33053,33055,33056,33057,33058,33059,33060,33061,33062,33063,33064,33065,33066,33067,33069,33070,33072,33075,33076,33077,33079,33081,33082,33083,33084,33085,33087,35881,33426,33579,30450,27667,24537,33725,29483,33541,38170,27611,30683,38086,21359,33538,20882,24125,35980,36152,20040,29611,26522,26757,37238,38665,29028,27809,30473,23186,38209,27599,32654,26151,23504,22969,23194,38376,38391,20204,33804,33945,27308,30431,38192,29467,26790,23391,30511,37274,38753,31964,36855,35868,24357,31859,31192,35269,27852,34588,23494,24130,26825,30496,32501,20885,20813,21193,23081,32517,38754,33495,25551,30596,34256,31186,28218,24217,22937,34065,28781,27665,25279,30399,25935,24751,38397,26126,34719,40483,38125,21517,21629,35884,25720,33088,33089,33090,33091,33092,33093,33095,33097,33101,33102,33103,33106,33110,33111,33112,33115,33116,33117,33118,33119,33121,33122,33123,33124,33126,33128,33130,33131,33132,33135,33138,33139,33141,33142,33143,33144,33153,33155,33156,33157,33158,33159,33161,33163,33164,33165,33166,33168,33170,33171,33172,33173,33174,33175,33177,33178,33182,33183,33184,33185,33186,33188,33189,33191,33193,33195,33196,33197,33198,33199,33200,33201,33202,33204,33205,33206,33207,33208,33209,33212,33213,33214,33215,33220,33221,33223,33224,33225,33227,33229,33230,33231,33232,33233,33234,33235,25721,34321,27169,33180,30952,25705,39764,25273,26411,33707,22696,40664,27819,28448,23518,38476,35851,29279,26576,25287,29281,20137,22982,27597,22675,26286,24149,21215,24917,26408,30446,30566,29287,31302,25343,21738,21584,38048,37027,23068,32435,27670,20035,22902,32784,22856,21335,30007,38590,22218,25376,33041,24700,38393,28118,21602,39297,20869,23273,33021,22958,38675,20522,27877,23612,25311,20320,21311,33147,36870,28346,34091,25288,24180,30910,25781,25467,24565,23064,37247,40479,23615,25423,32834,23421,21870,38218,38221,28037,24744,26592,29406,20957,23425,33236,33237,33238,33239,33240,33241,33242,33243,33244,33245,33246,33247,33248,33249,33250,33252,33253,33254,33256,33257,33259,33262,33263,33264,33265,33266,33269,33270,33271,33272,33273,33274,33277,33279,33283,33287,33288,33289,33290,33291,33294,33295,33297,33299,33301,33302,33303,33304,33305,33306,33309,33312,33316,33317,33318,33319,33321,33326,33330,33338,33340,33341,33343,33344,33345,33346,33347,33349,33350,33352,33354,33356,33357,33358,33360,33361,33362,33363,33364,33365,33366,33367,33369,33371,33372,33373,33374,33376,33377,33378,33379,33380,33381,33382,33383,33385,25319,27870,29275,25197,38062,32445,33043,27987,20892,24324,22900,21162,24594,22899,26262,34384,30111,25386,25062,31983,35834,21734,27431,40485,27572,34261,21589,20598,27812,21866,36276,29228,24085,24597,29750,25293,25490,29260,24472,28227,27966,25856,28504,30424,30928,30460,30036,21028,21467,20051,24222,26049,32810,32982,25243,21638,21032,28846,34957,36305,27873,21624,32986,22521,35060,36180,38506,37197,20329,27803,21943,30406,30768,25256,28921,28558,24429,34028,26842,30844,31735,33192,26379,40527,25447,30896,22383,30738,38713,25209,25259,21128,29749,27607,33386,33387,33388,33389,33393,33397,33398,33399,33400,33403,33404,33408,33409,33411,33413,33414,33415,33417,33420,33424,33427,33428,33429,33430,33434,33435,33438,33440,33442,33443,33447,33458,33461,33462,33466,33467,33468,33471,33472,33474,33475,33477,33478,33481,33488,33494,33497,33498,33501,33506,33511,33512,33513,33514,33516,33517,33518,33520,33522,33523,33525,33526,33528,33530,33532,33533,33534,33535,33536,33546,33547,33549,33552,33554,33555,33558,33560,33561,33565,33566,33567,33568,33569,33570,33571,33572,33573,33574,33577,33578,33582,33584,33586,33591,33595,33597,21860,33086,30130,30382,21305,30174,20731,23617,35692,31687,20559,29255,39575,39128,28418,29922,31080,25735,30629,25340,39057,36139,21697,32856,20050,22378,33529,33805,24179,20973,29942,35780,23631,22369,27900,39047,23110,30772,39748,36843,31893,21078,25169,38138,20166,33670,33889,33769,33970,22484,26420,22275,26222,28006,35889,26333,28689,26399,27450,26646,25114,22971,19971,20932,28422,26578,27791,20854,26827,22855,27495,30054,23822,33040,40784,26071,31048,31041,39569,36215,23682,20062,20225,21551,22865,30732,22120,27668,36804,24323,27773,27875,35755,25488,33598,33599,33601,33602,33604,33605,33608,33610,33611,33612,33613,33614,33619,33621,33622,33623,33624,33625,33629,33634,33648,33649,33650,33651,33652,33653,33654,33657,33658,33662,33663,33664,33665,33666,33667,33668,33671,33672,33674,33675,33676,33677,33679,33680,33681,33684,33685,33686,33687,33689,33690,33693,33695,33697,33698,33699,33700,33701,33702,33703,33708,33709,33710,33711,33717,33723,33726,33727,33730,33731,33732,33734,33736,33737,33739,33741,33742,33744,33745,33746,33747,33749,33751,33753,33754,33755,33758,33762,33763,33764,33766,33767,33768,33771,33772,33773,24688,27965,29301,25190,38030,38085,21315,36801,31614,20191,35878,20094,40660,38065,38067,21069,28508,36963,27973,35892,22545,23884,27424,27465,26538,21595,33108,32652,22681,34103,24378,25250,27207,38201,25970,24708,26725,30631,20052,20392,24039,38808,25772,32728,23789,20431,31373,20999,33540,19988,24623,31363,38054,20405,20146,31206,29748,21220,33465,25810,31165,23517,27777,38738,36731,27682,20542,21375,28165,25806,26228,27696,24773,39031,35831,24198,29756,31351,31179,19992,37041,29699,27714,22234,37195,27845,36235,21306,34502,26354,36527,23624,39537,28192,33774,33775,33779,33780,33781,33782,33783,33786,33787,33788,33790,33791,33792,33794,33797,33799,33800,33801,33802,33808,33810,33811,33812,33813,33814,33815,33817,33818,33819,33822,33823,33824,33825,33826,33827,33833,33834,33835,33836,33837,33838,33839,33840,33842,33843,33844,33845,33846,33847,33849,33850,33851,33854,33855,33856,33857,33858,33859,33860,33861,33863,33864,33865,33866,33867,33868,33869,33870,33871,33872,33874,33875,33876,33877,33878,33880,33885,33886,33887,33888,33890,33892,33893,33894,33895,33896,33898,33902,33903,33904,33906,33908,33911,33913,33915,33916,21462,23094,40843,36259,21435,22280,39079,26435,37275,27849,20840,30154,25331,29356,21048,21149,32570,28820,30264,21364,40522,27063,30830,38592,35033,32676,28982,29123,20873,26579,29924,22756,25880,22199,35753,39286,25200,32469,24825,28909,22764,20161,20154,24525,38887,20219,35748,20995,22922,32427,25172,20173,26085,25102,33592,33993,33635,34701,29076,28342,23481,32466,20887,25545,26580,32905,33593,34837,20754,23418,22914,36785,20083,27741,20837,35109,36719,38446,34122,29790,38160,38384,28070,33509,24369,25746,27922,33832,33134,40131,22622,36187,19977,21441,33917,33918,33919,33920,33921,33923,33924,33925,33926,33930,33933,33935,33936,33937,33938,33939,33940,33941,33942,33944,33946,33947,33949,33950,33951,33952,33954,33955,33956,33957,33958,33959,33960,33961,33962,33963,33964,33965,33966,33968,33969,33971,33973,33974,33975,33979,33980,33982,33984,33986,33987,33989,33990,33991,33992,33995,33996,33998,33999,34002,34004,34005,34007,34008,34009,34010,34011,34012,34014,34017,34018,34020,34023,34024,34025,34026,34027,34029,34030,34031,34033,34034,34035,34036,34037,34038,34039,34040,34041,34042,34043,34045,34046,34048,34049,34050,20254,25955,26705,21971,20007,25620,39578,25195,23234,29791,33394,28073,26862,20711,33678,30722,26432,21049,27801,32433,20667,21861,29022,31579,26194,29642,33515,26441,23665,21024,29053,34923,38378,38485,25797,36193,33203,21892,27733,25159,32558,22674,20260,21830,36175,26188,19978,23578,35059,26786,25422,31245,28903,33421,21242,38902,23569,21736,37045,32461,22882,36170,34503,33292,33293,36198,25668,23556,24913,28041,31038,35774,30775,30003,21627,20280,36523,28145,23072,32453,31070,27784,23457,23158,29978,32958,24910,28183,22768,29983,29989,29298,21319,32499,34051,34052,34053,34054,34055,34056,34057,34058,34059,34061,34062,34063,34064,34066,34068,34069,34070,34072,34073,34075,34076,34077,34078,34080,34082,34083,34084,34085,34086,34087,34088,34089,34090,34093,34094,34095,34096,34097,34098,34099,34100,34101,34102,34110,34111,34112,34113,34114,34116,34117,34118,34119,34123,34124,34125,34126,34127,34128,34129,34130,34131,34132,34133,34135,34136,34138,34139,34140,34141,34143,34144,34145,34146,34147,34149,34150,34151,34153,34154,34155,34156,34157,34158,34159,34160,34161,34163,34165,34166,34167,34168,34172,34173,34175,34176,34177,30465,30427,21097,32988,22307,24072,22833,29422,26045,28287,35799,23608,34417,21313,30707,25342,26102,20160,39135,34432,23454,35782,21490,30690,20351,23630,39542,22987,24335,31034,22763,19990,26623,20107,25325,35475,36893,21183,26159,21980,22124,36866,20181,20365,37322,39280,27663,24066,24643,23460,35270,35797,25910,25163,39318,23432,23551,25480,21806,21463,30246,20861,34092,26530,26803,27530,25234,36755,21460,33298,28113,30095,20070,36174,23408,29087,34223,26257,26329,32626,34560,40653,40736,23646,26415,36848,26641,26463,25101,31446,22661,24246,25968,28465,34178,34179,34182,34184,34185,34186,34187,34188,34189,34190,34192,34193,34194,34195,34196,34197,34198,34199,34200,34201,34202,34205,34206,34207,34208,34209,34210,34211,34213,34214,34215,34217,34219,34220,34221,34225,34226,34227,34228,34229,34230,34232,34234,34235,34236,34237,34238,34239,34240,34242,34243,34244,34245,34246,34247,34248,34250,34251,34252,34253,34254,34257,34258,34260,34262,34263,34264,34265,34266,34267,34269,34270,34271,34272,34273,34274,34275,34277,34278,34279,34280,34282,34283,34284,34285,34286,34287,34288,34289,34290,34291,34292,34293,34294,34295,34296,24661,21047,32781,25684,34928,29993,24069,26643,25332,38684,21452,29245,35841,27700,30561,31246,21550,30636,39034,33308,35828,30805,26388,28865,26031,25749,22070,24605,31169,21496,19997,27515,32902,23546,21987,22235,20282,20284,39282,24051,26494,32824,24578,39042,36865,23435,35772,35829,25628,33368,25822,22013,33487,37221,20439,32032,36895,31903,20723,22609,28335,23487,35785,32899,37240,33948,31639,34429,38539,38543,32485,39635,30862,23681,31319,36930,38567,31071,23385,25439,31499,34001,26797,21766,32553,29712,32034,38145,25152,22604,20182,23427,22905,22612,34297,34298,34300,34301,34302,34304,34305,34306,34307,34308,34310,34311,34312,34313,34314,34315,34316,34317,34318,34319,34320,34322,34323,34324,34325,34327,34328,34329,34330,34331,34332,34333,34334,34335,34336,34337,34338,34339,34340,34341,34342,34344,34346,34347,34348,34349,34350,34351,34352,34353,34354,34355,34356,34357,34358,34359,34361,34362,34363,34365,34366,34367,34368,34369,34370,34371,34372,34373,34374,34375,34376,34377,34378,34379,34380,34386,34387,34389,34390,34391,34392,34393,34395,34396,34397,34399,34400,34401,34403,34404,34405,34406,34407,34408,34409,34410,29549,25374,36427,36367,32974,33492,25260,21488,27888,37214,22826,24577,27760,22349,25674,36138,30251,28393,22363,27264,30192,28525,35885,35848,22374,27631,34962,30899,25506,21497,28845,27748,22616,25642,22530,26848,33179,21776,31958,20504,36538,28108,36255,28907,25487,28059,28372,32486,33796,26691,36867,28120,38518,35752,22871,29305,34276,33150,30140,35466,26799,21076,36386,38161,25552,39064,36420,21884,20307,26367,22159,24789,28053,21059,23625,22825,28155,22635,30000,29980,24684,33300,33094,25361,26465,36834,30522,36339,36148,38081,24086,21381,21548,28867,34413,34415,34416,34418,34419,34420,34421,34422,34423,34424,34435,34436,34437,34438,34439,34440,34441,34446,34447,34448,34449,34450,34452,34454,34455,34456,34457,34458,34459,34462,34463,34464,34465,34466,34469,34470,34475,34477,34478,34482,34483,34487,34488,34489,34491,34492,34493,34494,34495,34497,34498,34499,34501,34504,34508,34509,34514,34515,34517,34518,34519,34522,34524,34525,34528,34529,34530,34531,34533,34534,34535,34536,34538,34539,34540,34543,34549,34550,34551,34554,34555,34556,34557,34559,34561,34564,34565,34566,34571,34572,34574,34575,34576,34577,34580,34582,27712,24311,20572,20141,24237,25402,33351,36890,26704,37230,30643,21516,38108,24420,31461,26742,25413,31570,32479,30171,20599,25237,22836,36879,20984,31171,31361,22270,24466,36884,28034,23648,22303,21520,20820,28237,22242,25512,39059,33151,34581,35114,36864,21534,23663,33216,25302,25176,33073,40501,38464,39534,39548,26925,22949,25299,21822,25366,21703,34521,27964,23043,29926,34972,27498,22806,35916,24367,28286,29609,39037,20024,28919,23436,30871,25405,26202,30358,24779,23451,23113,19975,33109,27754,29579,20129,26505,32593,24448,26106,26395,24536,22916,23041,34585,34587,34589,34591,34592,34596,34598,34599,34600,34602,34603,34604,34605,34607,34608,34610,34611,34613,34614,34616,34617,34618,34620,34621,34624,34625,34626,34627,34628,34629,34630,34634,34635,34637,34639,34640,34641,34642,34644,34645,34646,34648,34650,34651,34652,34653,34654,34655,34657,34658,34662,34663,34664,34665,34666,34667,34668,34669,34671,34673,34674,34675,34677,34679,34680,34681,34682,34687,34688,34689,34692,34694,34695,34697,34698,34700,34702,34703,34704,34705,34706,34708,34709,34710,34712,34713,34714,34715,34716,34717,34718,34720,34721,34722,34723,34724,24013,24494,21361,38886,36829,26693,22260,21807,24799,20026,28493,32500,33479,33806,22996,20255,20266,23614,32428,26410,34074,21619,30031,32963,21890,39759,20301,28205,35859,23561,24944,21355,30239,28201,34442,25991,38395,32441,21563,31283,32010,38382,21985,32705,29934,25373,34583,28065,31389,25105,26017,21351,25569,27779,24043,21596,38056,20044,27745,35820,23627,26080,33436,26791,21566,21556,27595,27494,20116,25410,21320,33310,20237,20398,22366,25098,38654,26212,29289,21247,21153,24735,35823,26132,29081,26512,35199,30802,30717,26224,22075,21560,38177,29306,34725,34726,34727,34729,34730,34734,34736,34737,34738,34740,34742,34743,34744,34745,34747,34748,34750,34751,34753,34754,34755,34756,34757,34759,34760,34761,34764,34765,34766,34767,34768,34772,34773,34774,34775,34776,34777,34778,34780,34781,34782,34783,34785,34786,34787,34788,34790,34791,34792,34793,34795,34796,34797,34799,34800,34801,34802,34803,34804,34805,34806,34807,34808,34810,34811,34812,34813,34815,34816,34817,34818,34820,34821,34822,34823,34824,34825,34827,34828,34829,34830,34831,34832,34833,34834,34836,34839,34840,34841,34842,34844,34845,34846,34847,34848,34851,31232,24687,24076,24713,33181,22805,24796,29060,28911,28330,27728,29312,27268,34989,24109,20064,23219,21916,38115,27927,31995,38553,25103,32454,30606,34430,21283,38686,36758,26247,23777,20384,29421,19979,21414,22799,21523,25472,38184,20808,20185,40092,32420,21688,36132,34900,33335,38386,28046,24358,23244,26174,38505,29616,29486,21439,33146,39301,32673,23466,38519,38480,32447,30456,21410,38262,39321,31665,35140,28248,20065,32724,31077,35814,24819,21709,20139,39033,24055,27233,20687,21521,35937,33831,30813,38660,21066,21742,22179,38144,28040,23477,28102,26195,34852,34853,34854,34855,34856,34857,34858,34859,34860,34861,34862,34863,34864,34865,34867,34868,34869,34870,34871,34872,34874,34875,34877,34878,34879,34881,34882,34883,34886,34887,34888,34889,34890,34891,34894,34895,34896,34897,34898,34899,34901,34902,34904,34906,34907,34908,34909,34910,34911,34912,34918,34919,34922,34925,34927,34929,34931,34932,34933,34934,34936,34937,34938,34939,34940,34944,34947,34950,34951,34953,34954,34956,34958,34959,34960,34961,34963,34964,34965,34967,34968,34969,34970,34971,34973,34974,34975,34976,34977,34979,34981,34982,34983,34984,34985,34986,23567,23389,26657,32918,21880,31505,25928,26964,20123,27463,34638,38795,21327,25375,25658,37034,26012,32961,35856,20889,26800,21368,34809,25032,27844,27899,35874,23633,34218,33455,38156,27427,36763,26032,24571,24515,20449,34885,26143,33125,29481,24826,20852,21009,22411,24418,37026,34892,37266,24184,26447,24615,22995,20804,20982,33016,21256,27769,38596,29066,20241,20462,32670,26429,21957,38152,31168,34966,32483,22687,25100,38656,34394,22040,39035,24464,35768,33988,37207,21465,26093,24207,30044,24676,32110,23167,32490,32493,36713,21927,23459,24748,26059,29572,34988,34990,34991,34992,34994,34995,34996,34997,34998,35000,35001,35002,35003,35005,35006,35007,35008,35011,35012,35015,35016,35018,35019,35020,35021,35023,35024,35025,35027,35030,35031,35034,35035,35036,35037,35038,35040,35041,35046,35047,35049,35050,35051,35052,35053,35054,35055,35058,35061,35062,35063,35066,35067,35069,35071,35072,35073,35075,35076,35077,35078,35079,35080,35081,35083,35084,35085,35086,35087,35089,35092,35093,35094,35095,35096,35100,35101,35102,35103,35104,35106,35107,35108,35110,35111,35112,35113,35116,35117,35118,35119,35121,35122,35123,35125,35127,36873,30307,30505,32474,38772,34203,23398,31348,38634,34880,21195,29071,24490,26092,35810,23547,39535,24033,27529,27739,35757,35759,36874,36805,21387,25276,40486,40493,21568,20011,33469,29273,34460,23830,34905,28079,38597,21713,20122,35766,28937,21693,38409,28895,28153,30416,20005,30740,34578,23721,24310,35328,39068,38414,28814,27839,22852,25513,30524,34893,28436,33395,22576,29141,21388,30746,38593,21761,24422,28976,23476,35866,39564,27523,22830,40495,31207,26472,25196,20335,30113,32650,27915,38451,27687,20208,30162,20859,26679,28478,36992,33136,22934,29814,35128,35129,35130,35131,35132,35133,35134,35135,35136,35138,35139,35141,35142,35143,35144,35145,35146,35147,35148,35149,35150,35151,35152,35153,35154,35155,35156,35157,35158,35159,35160,35161,35162,35163,35164,35165,35168,35169,35170,35171,35172,35173,35175,35176,35177,35178,35179,35180,35181,35182,35183,35184,35185,35186,35187,35188,35189,35190,35191,35192,35193,35194,35196,35197,35198,35200,35202,35204,35205,35207,35208,35209,35210,35211,35212,35213,35214,35215,35216,35217,35218,35219,35220,35221,35222,35223,35224,35225,35226,35227,35228,35229,35230,35231,35232,35233,25671,23591,36965,31377,35875,23002,21676,33280,33647,35201,32768,26928,22094,32822,29239,37326,20918,20063,39029,25494,19994,21494,26355,33099,22812,28082,19968,22777,21307,25558,38129,20381,20234,34915,39056,22839,36951,31227,20202,33008,30097,27778,23452,23016,24413,26885,34433,20506,24050,20057,30691,20197,33402,25233,26131,37009,23673,20159,24441,33222,36920,32900,30123,20134,35028,24847,27589,24518,20041,30410,28322,35811,35758,35850,35793,24322,32764,32716,32462,33589,33643,22240,27575,38899,38452,23035,21535,38134,28139,23493,39278,23609,24341,38544,35234,35235,35236,35237,35238,35239,35240,35241,35242,35243,35244,35245,35246,35247,35248,35249,35250,35251,35252,35253,35254,35255,35256,35257,35258,35259,35260,35261,35262,35263,35264,35267,35277,35283,35284,35285,35287,35288,35289,35291,35293,35295,35296,35297,35298,35300,35303,35304,35305,35306,35308,35309,35310,35312,35313,35314,35316,35317,35318,35319,35320,35321,35322,35323,35324,35325,35326,35327,35329,35330,35331,35332,35333,35334,35336,35337,35338,35339,35340,35341,35342,35343,35344,35345,35346,35347,35348,35349,35350,35351,35352,35353,35354,35355,35356,35357,21360,33521,27185,23156,40560,24212,32552,33721,33828,33829,33639,34631,36814,36194,30408,24433,39062,30828,26144,21727,25317,20323,33219,30152,24248,38605,36362,34553,21647,27891,28044,27704,24703,21191,29992,24189,20248,24736,24551,23588,30001,37038,38080,29369,27833,28216,37193,26377,21451,21491,20305,37321,35825,21448,24188,36802,28132,20110,30402,27014,34398,24858,33286,20313,20446,36926,40060,24841,28189,28180,38533,20104,23089,38632,19982,23679,31161,23431,35821,32701,29577,22495,33419,37057,21505,36935,21947,23786,24481,24840,27442,29425,32946,35465,35358,35359,35360,35361,35362,35363,35364,35365,35366,35367,35368,35369,35370,35371,35372,35373,35374,35375,35376,35377,35378,35379,35380,35381,35382,35383,35384,35385,35386,35387,35388,35389,35391,35392,35393,35394,35395,35396,35397,35398,35399,35401,35402,35403,35404,35405,35406,35407,35408,35409,35410,35411,35412,35413,35414,35415,35416,35417,35418,35419,35420,35421,35422,35423,35424,35425,35426,35427,35428,35429,35430,35431,35432,35433,35434,35435,35436,35437,35438,35439,35440,35441,35442,35443,35444,35445,35446,35447,35448,35450,35451,35452,35453,35454,35455,35456,28020,23507,35029,39044,35947,39533,40499,28170,20900,20803,22435,34945,21407,25588,36757,22253,21592,22278,29503,28304,32536,36828,33489,24895,24616,38498,26352,32422,36234,36291,38053,23731,31908,26376,24742,38405,32792,20113,37095,21248,38504,20801,36816,34164,37213,26197,38901,23381,21277,30776,26434,26685,21705,28798,23472,36733,20877,22312,21681,25874,26242,36190,36163,33039,33900,36973,31967,20991,34299,26531,26089,28577,34468,36481,22122,36896,30338,28790,29157,36131,25321,21017,27901,36156,24590,22686,24974,26366,36192,25166,21939,28195,26413,36711,35457,35458,35459,35460,35461,35462,35463,35464,35467,35468,35469,35470,35471,35472,35473,35474,35476,35477,35478,35479,35480,35481,35482,35483,35484,35485,35486,35487,35488,35489,35490,35491,35492,35493,35494,35495,35496,35497,35498,35499,35500,35501,35502,35503,35504,35505,35506,35507,35508,35509,35510,35511,35512,35513,35514,35515,35516,35517,35518,35519,35520,35521,35522,35523,35524,35525,35526,35527,35528,35529,35530,35531,35532,35533,35534,35535,35536,35537,35538,35539,35540,35541,35542,35543,35544,35545,35546,35547,35548,35549,35550,35551,35552,35553,35554,35555,38113,38392,30504,26629,27048,21643,20045,28856,35784,25688,25995,23429,31364,20538,23528,30651,27617,35449,31896,27838,30415,26025,36759,23853,23637,34360,26632,21344,25112,31449,28251,32509,27167,31456,24432,28467,24352,25484,28072,26454,19976,24080,36134,20183,32960,30260,38556,25307,26157,25214,27836,36213,29031,32617,20806,32903,21484,36974,25240,21746,34544,36761,32773,38167,34071,36825,27993,29645,26015,30495,29956,30759,33275,36126,38024,20390,26517,30137,35786,38663,25391,38215,38453,33976,25379,30529,24449,29424,20105,24596,25972,25327,27491,25919,35556,35557,35558,35559,35560,35561,35562,35563,35564,35565,35566,35567,35568,35569,35570,35571,35572,35573,35574,35575,35576,35577,35578,35579,35580,35581,35582,35583,35584,35585,35586,35587,35588,35589,35590,35592,35593,35594,35595,35596,35597,35598,35599,35600,35601,35602,35603,35604,35605,35606,35607,35608,35609,35610,35611,35612,35613,35614,35615,35616,35617,35618,35619,35620,35621,35623,35624,35625,35626,35627,35628,35629,35630,35631,35632,35633,35634,35635,35636,35637,35638,35639,35640,35641,35642,35643,35644,35645,35646,35647,35648,35649,35650,35651,35652,35653,24103,30151,37073,35777,33437,26525,25903,21553,34584,30693,32930,33026,27713,20043,32455,32844,30452,26893,27542,25191,20540,20356,22336,25351,27490,36286,21482,26088,32440,24535,25370,25527,33267,33268,32622,24092,23769,21046,26234,31209,31258,36136,28825,30164,28382,27835,31378,20013,30405,24544,38047,34935,32456,31181,32959,37325,20210,20247,33311,21608,24030,27954,35788,31909,36724,32920,24090,21650,30385,23449,26172,39588,29664,26666,34523,26417,29482,35832,35803,36880,31481,28891,29038,25284,30633,22065,20027,33879,26609,21161,34496,36142,38136,31569,35654,35655,35656,35657,35658,35659,35660,35661,35662,35663,35664,35665,35666,35667,35668,35669,35670,35671,35672,35673,35674,35675,35676,35677,35678,35679,35680,35681,35682,35683,35684,35685,35687,35688,35689,35690,35691,35693,35694,35695,35696,35697,35698,35699,35700,35701,35702,35703,35704,35705,35706,35707,35708,35709,35710,35711,35712,35713,35714,35715,35716,35717,35718,35719,35720,35721,35722,35723,35724,35725,35726,35727,35728,35729,35730,35731,35732,35733,35734,35735,35736,35737,35738,35739,35740,35741,35742,35743,35756,35761,35771,35783,35792,35818,35849,35870,20303,27880,31069,39547,25235,29226,25341,19987,30742,36716,25776,36186,31686,26729,24196,35013,22918,25758,22766,29366,26894,38181,36861,36184,22368,32512,35846,20934,25417,25305,21331,26700,29730,33537,37196,21828,30528,28796,27978,20857,21672,36164,23039,28363,28100,23388,32043,20180,31869,28371,23376,33258,28173,23383,39683,26837,36394,23447,32508,24635,32437,37049,36208,22863,25549,31199,36275,21330,26063,31062,35781,38459,32452,38075,32386,22068,37257,26368,32618,23562,36981,26152,24038,20304,26590,20570,20316,22352,24231,59408,59409,59410,59411,59412,35896,35897,35898,35899,35900,35901,35902,35903,35904,35906,35907,35908,35909,35912,35914,35915,35917,35918,35919,35920,35921,35922,35923,35924,35926,35927,35928,35929,35931,35932,35933,35934,35935,35936,35939,35940,35941,35942,35943,35944,35945,35948,35949,35950,35951,35952,35953,35954,35956,35957,35958,35959,35963,35964,35965,35966,35967,35968,35969,35971,35972,35974,35975,35976,35979,35981,35982,35983,35984,35985,35986,35987,35989,35990,35991,35993,35994,35995,35996,35997,35998,35999,36000,36001,36002,36003,36004,36005,36006,36007,36008,36009,36010,36011,36012,36013,20109,19980,20800,19984,24319,21317,19989,20120,19998,39730,23404,22121,20008,31162,20031,21269,20039,22829,29243,21358,27664,22239,32996,39319,27603,30590,40727,20022,20127,40720,20060,20073,20115,33416,23387,21868,22031,20164,21389,21405,21411,21413,21422,38757,36189,21274,21493,21286,21294,21310,36188,21350,21347,20994,21000,21006,21037,21043,21055,21056,21068,21086,21089,21084,33967,21117,21122,21121,21136,21139,20866,32596,20155,20163,20169,20162,20200,20193,20203,20190,20251,20211,20258,20324,20213,20261,20263,20233,20267,20318,20327,25912,20314,20317,36014,36015,36016,36017,36018,36019,36020,36021,36022,36023,36024,36025,36026,36027,36028,36029,36030,36031,36032,36033,36034,36035,36036,36037,36038,36039,36040,36041,36042,36043,36044,36045,36046,36047,36048,36049,36050,36051,36052,36053,36054,36055,36056,36057,36058,36059,36060,36061,36062,36063,36064,36065,36066,36067,36068,36069,36070,36071,36072,36073,36074,36075,36076,36077,36078,36079,36080,36081,36082,36083,36084,36085,36086,36087,36088,36089,36090,36091,36092,36093,36094,36095,36096,36097,36098,36099,36100,36101,36102,36103,36104,36105,36106,36107,36108,36109,20319,20311,20274,20285,20342,20340,20369,20361,20355,20367,20350,20347,20394,20348,20396,20372,20454,20456,20458,20421,20442,20451,20444,20433,20447,20472,20521,20556,20467,20524,20495,20526,20525,20478,20508,20492,20517,20520,20606,20547,20565,20552,20558,20588,20603,20645,20647,20649,20666,20694,20742,20717,20716,20710,20718,20743,20747,20189,27709,20312,20325,20430,40864,27718,31860,20846,24061,40649,39320,20865,22804,21241,21261,35335,21264,20971,22809,20821,20128,20822,20147,34926,34980,20149,33044,35026,31104,23348,34819,32696,20907,20913,20925,20924,36110,36111,36112,36113,36114,36115,36116,36117,36118,36119,36120,36121,36122,36123,36124,36128,36177,36178,36183,36191,36197,36200,36201,36202,36204,36206,36207,36209,36210,36216,36217,36218,36219,36220,36221,36222,36223,36224,36226,36227,36230,36231,36232,36233,36236,36237,36238,36239,36240,36242,36243,36245,36246,36247,36248,36249,36250,36251,36252,36253,36254,36256,36257,36258,36260,36261,36262,36263,36264,36265,36266,36267,36268,36269,36270,36271,36272,36274,36278,36279,36281,36283,36285,36288,36289,36290,36293,36295,36296,36297,36298,36301,36304,36306,36307,36308,20935,20886,20898,20901,35744,35750,35751,35754,35764,35765,35767,35778,35779,35787,35791,35790,35794,35795,35796,35798,35800,35801,35804,35807,35808,35812,35816,35817,35822,35824,35827,35830,35833,35836,35839,35840,35842,35844,35847,35852,35855,35857,35858,35860,35861,35862,35865,35867,35864,35869,35871,35872,35873,35877,35879,35882,35883,35886,35887,35890,35891,35893,35894,21353,21370,38429,38434,38433,38449,38442,38461,38460,38466,38473,38484,38495,38503,38508,38514,38516,38536,38541,38551,38576,37015,37019,37021,37017,37036,37025,37044,37043,37046,37050,36309,36312,36313,36316,36320,36321,36322,36325,36326,36327,36329,36333,36334,36336,36337,36338,36340,36342,36348,36350,36351,36352,36353,36354,36355,36356,36358,36359,36360,36363,36365,36366,36368,36369,36370,36371,36373,36374,36375,36376,36377,36378,36379,36380,36384,36385,36388,36389,36390,36391,36392,36395,36397,36400,36402,36403,36404,36406,36407,36408,36411,36412,36414,36415,36419,36421,36422,36428,36429,36430,36431,36432,36435,36436,36437,36438,36439,36440,36442,36443,36444,36445,36446,36447,36448,36449,36450,36451,36452,36453,36455,36456,36458,36459,36462,36465,37048,37040,37071,37061,37054,37072,37060,37063,37075,37094,37090,37084,37079,37083,37099,37103,37118,37124,37154,37150,37155,37169,37167,37177,37187,37190,21005,22850,21154,21164,21165,21182,21759,21200,21206,21232,21471,29166,30669,24308,20981,20988,39727,21430,24321,30042,24047,22348,22441,22433,22654,22716,22725,22737,22313,22316,22314,22323,22329,22318,22319,22364,22331,22338,22377,22405,22379,22406,22396,22395,22376,22381,22390,22387,22445,22436,22412,22450,22479,22439,22452,22419,22432,22485,22488,22490,22489,22482,22456,22516,22511,22520,22500,22493,36467,36469,36471,36472,36473,36474,36475,36477,36478,36480,36482,36483,36484,36486,36488,36489,36490,36491,36492,36493,36494,36497,36498,36499,36501,36502,36503,36504,36505,36506,36507,36509,36511,36512,36513,36514,36515,36516,36517,36518,36519,36520,36521,36522,36525,36526,36528,36529,36531,36532,36533,36534,36535,36536,36537,36539,36540,36541,36542,36543,36544,36545,36546,36547,36548,36549,36550,36551,36552,36553,36554,36555,36556,36557,36559,36560,36561,36562,36563,36564,36565,36566,36567,36568,36569,36570,36571,36572,36573,36574,36575,36576,36577,36578,36579,36580,22539,22541,22525,22509,22528,22558,22553,22596,22560,22629,22636,22657,22665,22682,22656,39336,40729,25087,33401,33405,33407,33423,33418,33448,33412,33422,33425,33431,33433,33451,33464,33470,33456,33480,33482,33507,33432,33463,33454,33483,33484,33473,33449,33460,33441,33450,33439,33476,33486,33444,33505,33545,33527,33508,33551,33543,33500,33524,33490,33496,33548,33531,33491,33553,33562,33542,33556,33557,33504,33493,33564,33617,33627,33628,33544,33682,33596,33588,33585,33691,33630,33583,33615,33607,33603,33631,33600,33559,33632,33581,33594,33587,33638,33637,36581,36582,36583,36584,36585,36586,36587,36588,36589,36590,36591,36592,36593,36594,36595,36596,36597,36598,36599,36600,36601,36602,36603,36604,36605,36606,36607,36608,36609,36610,36611,36612,36613,36614,36615,36616,36617,36618,36619,36620,36621,36622,36623,36624,36625,36626,36627,36628,36629,36630,36631,36632,36633,36634,36635,36636,36637,36638,36639,36640,36641,36642,36643,36644,36645,36646,36647,36648,36649,36650,36651,36652,36653,36654,36655,36656,36657,36658,36659,36660,36661,36662,36663,36664,36665,36666,36667,36668,36669,36670,36671,36672,36673,36674,36675,36676,33640,33563,33641,33644,33642,33645,33646,33712,33656,33715,33716,33696,33706,33683,33692,33669,33660,33718,33705,33661,33720,33659,33688,33694,33704,33722,33724,33729,33793,33765,33752,22535,33816,33803,33757,33789,33750,33820,33848,33809,33798,33748,33759,33807,33795,33784,33785,33770,33733,33728,33830,33776,33761,33884,33873,33882,33881,33907,33927,33928,33914,33929,33912,33852,33862,33897,33910,33932,33934,33841,33901,33985,33997,34000,34022,33981,34003,33994,33983,33978,34016,33953,33977,33972,33943,34021,34019,34060,29965,34104,34032,34105,34079,34106,36677,36678,36679,36680,36681,36682,36683,36684,36685,36686,36687,36688,36689,36690,36691,36692,36693,36694,36695,36696,36697,36698,36699,36700,36701,36702,36703,36704,36705,36706,36707,36708,36709,36714,36736,36748,36754,36765,36768,36769,36770,36772,36773,36774,36775,36778,36780,36781,36782,36783,36786,36787,36788,36789,36791,36792,36794,36795,36796,36799,36800,36803,36806,36809,36810,36811,36812,36813,36815,36818,36822,36823,36826,36832,36833,36835,36839,36844,36847,36849,36850,36852,36853,36854,36858,36859,36860,36862,36863,36871,36872,36876,36878,36883,36885,36888,34134,34107,34047,34044,34137,34120,34152,34148,34142,34170,30626,34115,34162,34171,34212,34216,34183,34191,34169,34222,34204,34181,34233,34231,34224,34259,34241,34268,34303,34343,34309,34345,34326,34364,24318,24328,22844,22849,32823,22869,22874,22872,21263,23586,23589,23596,23604,25164,25194,25247,25275,25290,25306,25303,25326,25378,25334,25401,25419,25411,25517,25590,25457,25466,25486,25524,25453,25516,25482,25449,25518,25532,25586,25592,25568,25599,25540,25566,25550,25682,25542,25534,25669,25665,25611,25627,25632,25612,25638,25633,25694,25732,25709,25750,36889,36892,36899,36900,36901,36903,36904,36905,36906,36907,36908,36912,36913,36914,36915,36916,36919,36921,36922,36925,36927,36928,36931,36933,36934,36936,36937,36938,36939,36940,36942,36948,36949,36950,36953,36954,36956,36957,36958,36959,36960,36961,36964,36966,36967,36969,36970,36971,36972,36975,36976,36977,36978,36979,36982,36983,36984,36985,36986,36987,36988,36990,36993,36996,36997,36998,36999,37001,37002,37004,37005,37006,37007,37008,37010,37012,37014,37016,37018,37020,37022,37023,37024,37028,37029,37031,37032,37033,37035,37037,37042,37047,37052,37053,37055,37056,25722,25783,25784,25753,25786,25792,25808,25815,25828,25826,25865,25893,25902,24331,24530,29977,24337,21343,21489,21501,21481,21480,21499,21522,21526,21510,21579,21586,21587,21588,21590,21571,21537,21591,21593,21539,21554,21634,21652,21623,21617,21604,21658,21659,21636,21622,21606,21661,21712,21677,21698,21684,21714,21671,21670,21715,21716,21618,21667,21717,21691,21695,21708,21721,21722,21724,21673,21674,21668,21725,21711,21726,21787,21735,21792,21757,21780,21747,21794,21795,21775,21777,21799,21802,21863,21903,21941,21833,21869,21825,21845,21823,21840,21820,37058,37059,37062,37064,37065,37067,37068,37069,37074,37076,37077,37078,37080,37081,37082,37086,37087,37088,37091,37092,37093,37097,37098,37100,37102,37104,37105,37106,37107,37109,37110,37111,37113,37114,37115,37116,37119,37120,37121,37123,37125,37126,37127,37128,37129,37130,37131,37132,37133,37134,37135,37136,37137,37138,37139,37140,37141,37142,37143,37144,37146,37147,37148,37149,37151,37152,37153,37156,37157,37158,37159,37160,37161,37162,37163,37164,37165,37166,37168,37170,37171,37172,37173,37174,37175,37176,37178,37179,37180,37181,37182,37183,37184,37185,37186,37188,21815,21846,21877,21878,21879,21811,21808,21852,21899,21970,21891,21937,21945,21896,21889,21919,21886,21974,21905,21883,21983,21949,21950,21908,21913,21994,22007,21961,22047,21969,21995,21996,21972,21990,21981,21956,21999,21989,22002,22003,21964,21965,21992,22005,21988,36756,22046,22024,22028,22017,22052,22051,22014,22016,22055,22061,22104,22073,22103,22060,22093,22114,22105,22108,22092,22100,22150,22116,22129,22123,22139,22140,22149,22163,22191,22228,22231,22237,22241,22261,22251,22265,22271,22276,22282,22281,22300,24079,24089,24084,24081,24113,24123,24124,37189,37191,37192,37201,37203,37204,37205,37206,37208,37209,37211,37212,37215,37216,37222,37223,37224,37227,37229,37235,37242,37243,37244,37248,37249,37250,37251,37252,37254,37256,37258,37262,37263,37267,37268,37269,37270,37271,37272,37273,37276,37277,37278,37279,37280,37281,37284,37285,37286,37287,37288,37289,37291,37292,37296,37297,37298,37299,37302,37303,37304,37305,37307,37308,37309,37310,37311,37312,37313,37314,37315,37316,37317,37318,37320,37323,37328,37330,37331,37332,37333,37334,37335,37336,37337,37338,37339,37341,37342,37343,37344,37345,37346,37347,37348,37349,24119,24132,24148,24155,24158,24161,23692,23674,23693,23696,23702,23688,23704,23705,23697,23706,23708,23733,23714,23741,23724,23723,23729,23715,23745,23735,23748,23762,23780,23755,23781,23810,23811,23847,23846,23854,23844,23838,23814,23835,23896,23870,23860,23869,23916,23899,23919,23901,23915,23883,23882,23913,23924,23938,23961,23965,35955,23991,24005,24435,24439,24450,24455,24457,24460,24469,24473,24476,24488,24493,24501,24508,34914,24417,29357,29360,29364,29367,29368,29379,29377,29390,29389,29394,29416,29423,29417,29426,29428,29431,29441,29427,29443,29434,37350,37351,37352,37353,37354,37355,37356,37357,37358,37359,37360,37361,37362,37363,37364,37365,37366,37367,37368,37369,37370,37371,37372,37373,37374,37375,37376,37377,37378,37379,37380,37381,37382,37383,37384,37385,37386,37387,37388,37389,37390,37391,37392,37393,37394,37395,37396,37397,37398,37399,37400,37401,37402,37403,37404,37405,37406,37407,37408,37409,37410,37411,37412,37413,37414,37415,37416,37417,37418,37419,37420,37421,37422,37423,37424,37425,37426,37427,37428,37429,37430,37431,37432,37433,37434,37435,37436,37437,37438,37439,37440,37441,37442,37443,37444,37445,29435,29463,29459,29473,29450,29470,29469,29461,29474,29497,29477,29484,29496,29489,29520,29517,29527,29536,29548,29551,29566,33307,22821,39143,22820,22786,39267,39271,39272,39273,39274,39275,39276,39284,39287,39293,39296,39300,39303,39306,39309,39312,39313,39315,39316,39317,24192,24209,24203,24214,24229,24224,24249,24245,24254,24243,36179,24274,24273,24283,24296,24298,33210,24516,24521,24534,24527,24579,24558,24580,24545,24548,24574,24581,24582,24554,24557,24568,24601,24629,24614,24603,24591,24589,24617,24619,24586,24639,24609,24696,24697,24699,24698,24642,37446,37447,37448,37449,37450,37451,37452,37453,37454,37455,37456,37457,37458,37459,37460,37461,37462,37463,37464,37465,37466,37467,37468,37469,37470,37471,37472,37473,37474,37475,37476,37477,37478,37479,37480,37481,37482,37483,37484,37485,37486,37487,37488,37489,37490,37491,37493,37494,37495,37496,37497,37498,37499,37500,37501,37502,37503,37504,37505,37506,37507,37508,37509,37510,37511,37512,37513,37514,37515,37516,37517,37519,37520,37521,37522,37523,37524,37525,37526,37527,37528,37529,37530,37531,37532,37533,37534,37535,37536,37537,37538,37539,37540,37541,37542,37543,24682,24701,24726,24730,24749,24733,24707,24722,24716,24731,24812,24763,24753,24797,24792,24774,24794,24756,24864,24870,24853,24867,24820,24832,24846,24875,24906,24949,25004,24980,24999,25015,25044,25077,24541,38579,38377,38379,38385,38387,38389,38390,38396,38398,38403,38404,38406,38408,38410,38411,38412,38413,38415,38418,38421,38422,38423,38425,38426,20012,29247,25109,27701,27732,27740,27722,27811,27781,27792,27796,27788,27752,27753,27764,27766,27782,27817,27856,27860,27821,27895,27896,27889,27863,27826,27872,27862,27898,27883,27886,27825,27859,27887,27902,37544,37545,37546,37547,37548,37549,37551,37552,37553,37554,37555,37556,37557,37558,37559,37560,37561,37562,37563,37564,37565,37566,37567,37568,37569,37570,37571,37572,37573,37574,37575,37577,37578,37579,37580,37581,37582,37583,37584,37585,37586,37587,37588,37589,37590,37591,37592,37593,37594,37595,37596,37597,37598,37599,37600,37601,37602,37603,37604,37605,37606,37607,37608,37609,37610,37611,37612,37613,37614,37615,37616,37617,37618,37619,37620,37621,37622,37623,37624,37625,37626,37627,37628,37629,37630,37631,37632,37633,37634,37635,37636,37637,37638,37639,37640,37641,27961,27943,27916,27971,27976,27911,27908,27929,27918,27947,27981,27950,27957,27930,27983,27986,27988,27955,28049,28015,28062,28064,27998,28051,28052,27996,28000,28028,28003,28186,28103,28101,28126,28174,28095,28128,28177,28134,28125,28121,28182,28075,28172,28078,28203,28270,28238,28267,28338,28255,28294,28243,28244,28210,28197,28228,28383,28337,28312,28384,28461,28386,28325,28327,28349,28347,28343,28375,28340,28367,28303,28354,28319,28514,28486,28487,28452,28437,28409,28463,28470,28491,28532,28458,28425,28457,28553,28557,28556,28536,28530,28540,28538,28625,37642,37643,37644,37645,37646,37647,37648,37649,37650,37651,37652,37653,37654,37655,37656,37657,37658,37659,37660,37661,37662,37663,37664,37665,37666,37667,37668,37669,37670,37671,37672,37673,37674,37675,37676,37677,37678,37679,37680,37681,37682,37683,37684,37685,37686,37687,37688,37689,37690,37691,37692,37693,37695,37696,37697,37698,37699,37700,37701,37702,37703,37704,37705,37706,37707,37708,37709,37710,37711,37712,37713,37714,37715,37716,37717,37718,37719,37720,37721,37722,37723,37724,37725,37726,37727,37728,37729,37730,37731,37732,37733,37734,37735,37736,37737,37739,28617,28583,28601,28598,28610,28641,28654,28638,28640,28655,28698,28707,28699,28729,28725,28751,28766,23424,23428,23445,23443,23461,23480,29999,39582,25652,23524,23534,35120,23536,36423,35591,36790,36819,36821,36837,36846,36836,36841,36838,36851,36840,36869,36868,36875,36902,36881,36877,36886,36897,36917,36918,36909,36911,36932,36945,36946,36944,36968,36952,36962,36955,26297,36980,36989,36994,37000,36995,37003,24400,24407,24406,24408,23611,21675,23632,23641,23409,23651,23654,32700,24362,24361,24365,33396,24380,39739,23662,22913,22915,22925,22953,22954,22947,37740,37741,37742,37743,37744,37745,37746,37747,37748,37749,37750,37751,37752,37753,37754,37755,37756,37757,37758,37759,37760,37761,37762,37763,37764,37765,37766,37767,37768,37769,37770,37771,37772,37773,37774,37776,37777,37778,37779,37780,37781,37782,37783,37784,37785,37786,37787,37788,37789,37790,37791,37792,37793,37794,37795,37796,37797,37798,37799,37800,37801,37802,37803,37804,37805,37806,37807,37808,37809,37810,37811,37812,37813,37814,37815,37816,37817,37818,37819,37820,37821,37822,37823,37824,37825,37826,37827,37828,37829,37830,37831,37832,37833,37835,37836,37837,22935,22986,22955,22942,22948,22994,22962,22959,22999,22974,23045,23046,23005,23048,23011,23000,23033,23052,23049,23090,23092,23057,23075,23059,23104,23143,23114,23125,23100,23138,23157,33004,23210,23195,23159,23162,23230,23275,23218,23250,23252,23224,23264,23267,23281,23254,23270,23256,23260,23305,23319,23318,23346,23351,23360,23573,23580,23386,23397,23411,23377,23379,23394,39541,39543,39544,39546,39551,39549,39552,39553,39557,39560,39562,39568,39570,39571,39574,39576,39579,39580,39581,39583,39584,39586,39587,39589,39591,32415,32417,32419,32421,32424,32425,37838,37839,37840,37841,37842,37843,37844,37845,37847,37848,37849,37850,37851,37852,37853,37854,37855,37856,37857,37858,37859,37860,37861,37862,37863,37864,37865,37866,37867,37868,37869,37870,37871,37872,37873,37874,37875,37876,37877,37878,37879,37880,37881,37882,37883,37884,37885,37886,37887,37888,37889,37890,37891,37892,37893,37894,37895,37896,37897,37898,37899,37900,37901,37902,37903,37904,37905,37906,37907,37908,37909,37910,37911,37912,37913,37914,37915,37916,37917,37918,37919,37920,37921,37922,37923,37924,37925,37926,37927,37928,37929,37930,37931,37932,37933,37934,32429,32432,32446,32448,32449,32450,32457,32459,32460,32464,32468,32471,32475,32480,32481,32488,32491,32494,32495,32497,32498,32525,32502,32506,32507,32510,32513,32514,32515,32519,32520,32523,32524,32527,32529,32530,32535,32537,32540,32539,32543,32545,32546,32547,32548,32549,32550,32551,32554,32555,32556,32557,32559,32560,32561,32562,32563,32565,24186,30079,24027,30014,37013,29582,29585,29614,29602,29599,29647,29634,29649,29623,29619,29632,29641,29640,29669,29657,39036,29706,29673,29671,29662,29626,29682,29711,29738,29787,29734,29733,29736,29744,29742,29740,37935,37936,37937,37938,37939,37940,37941,37942,37943,37944,37945,37946,37947,37948,37949,37951,37952,37953,37954,37955,37956,37957,37958,37959,37960,37961,37962,37963,37964,37965,37966,37967,37968,37969,37970,37971,37972,37973,37974,37975,37976,37977,37978,37979,37980,37981,37982,37983,37984,37985,37986,37987,37988,37989,37990,37991,37992,37993,37994,37996,37997,37998,37999,38000,38001,38002,38003,38004,38005,38006,38007,38008,38009,38010,38011,38012,38013,38014,38015,38016,38017,38018,38019,38020,38033,38038,38040,38087,38095,38099,38100,38106,38118,38139,38172,38176,29723,29722,29761,29788,29783,29781,29785,29815,29805,29822,29852,29838,29824,29825,29831,29835,29854,29864,29865,29840,29863,29906,29882,38890,38891,38892,26444,26451,26462,26440,26473,26533,26503,26474,26483,26520,26535,26485,26536,26526,26541,26507,26487,26492,26608,26633,26584,26634,26601,26544,26636,26585,26549,26586,26547,26589,26624,26563,26552,26594,26638,26561,26621,26674,26675,26720,26721,26702,26722,26692,26724,26755,26653,26709,26726,26689,26727,26688,26686,26698,26697,26665,26805,26767,26740,26743,26771,26731,26818,26990,26876,26911,26912,26873,38183,38195,38205,38211,38216,38219,38229,38234,38240,38254,38260,38261,38263,38264,38265,38266,38267,38268,38269,38270,38272,38273,38274,38275,38276,38277,38278,38279,38280,38281,38282,38283,38284,38285,38286,38287,38288,38289,38290,38291,38292,38293,38294,38295,38296,38297,38298,38299,38300,38301,38302,38303,38304,38305,38306,38307,38308,38309,38310,38311,38312,38313,38314,38315,38316,38317,38318,38319,38320,38321,38322,38323,38324,38325,38326,38327,38328,38329,38330,38331,38332,38333,38334,38335,38336,38337,38338,38339,38340,38341,38342,38343,38344,38345,38346,38347,26916,26864,26891,26881,26967,26851,26896,26993,26937,26976,26946,26973,27012,26987,27008,27032,27000,26932,27084,27015,27016,27086,27017,26982,26979,27001,27035,27047,27067,27051,27053,27092,27057,27073,27082,27103,27029,27104,27021,27135,27183,27117,27159,27160,27237,27122,27204,27198,27296,27216,27227,27189,27278,27257,27197,27176,27224,27260,27281,27280,27305,27287,27307,29495,29522,27521,27522,27527,27524,27538,27539,27533,27546,27547,27553,27562,36715,36717,36721,36722,36723,36725,36726,36728,36727,36729,36730,36732,36734,36737,36738,36740,36743,36747,38348,38349,38350,38351,38352,38353,38354,38355,38356,38357,38358,38359,38360,38361,38362,38363,38364,38365,38366,38367,38368,38369,38370,38371,38372,38373,38374,38375,38380,38399,38407,38419,38424,38427,38430,38432,38435,38436,38437,38438,38439,38440,38441,38443,38444,38445,38447,38448,38455,38456,38457,38458,38462,38465,38467,38474,38478,38479,38481,38482,38483,38486,38487,38488,38489,38490,38492,38493,38494,38496,38499,38501,38502,38507,38509,38510,38511,38512,38513,38515,38520,38521,38522,38523,38524,38525,38526,38527,38528,38529,38530,38531,38532,38535,38537,38538,36749,36750,36751,36760,36762,36558,25099,25111,25115,25119,25122,25121,25125,25124,25132,33255,29935,29940,29951,29967,29969,29971,25908,26094,26095,26096,26122,26137,26482,26115,26133,26112,28805,26359,26141,26164,26161,26166,26165,32774,26207,26196,26177,26191,26198,26209,26199,26231,26244,26252,26279,26269,26302,26331,26332,26342,26345,36146,36147,36150,36155,36157,36160,36165,36166,36168,36169,36167,36173,36181,36185,35271,35274,35275,35276,35278,35279,35280,35281,29294,29343,29277,29286,29295,29310,29311,29316,29323,29325,29327,29330,25352,25394,25520,38540,38542,38545,38546,38547,38549,38550,38554,38555,38557,38558,38559,38560,38561,38562,38563,38564,38565,38566,38568,38569,38570,38571,38572,38573,38574,38575,38577,38578,38580,38581,38583,38584,38586,38587,38591,38594,38595,38600,38602,38603,38608,38609,38611,38612,38614,38615,38616,38617,38618,38619,38620,38621,38622,38623,38625,38626,38627,38628,38629,38630,38631,38635,38636,38637,38638,38640,38641,38642,38644,38645,38648,38650,38651,38652,38653,38655,38658,38659,38661,38666,38667,38668,38672,38673,38674,38676,38677,38679,38680,38681,38682,38683,38685,38687,38688,25663,25816,32772,27626,27635,27645,27637,27641,27653,27655,27654,27661,27669,27672,27673,27674,27681,27689,27684,27690,27698,25909,25941,25963,29261,29266,29270,29232,34402,21014,32927,32924,32915,32956,26378,32957,32945,32939,32941,32948,32951,32999,33000,33001,33002,32987,32962,32964,32985,32973,32983,26384,32989,33003,33009,33012,33005,33037,33038,33010,33020,26389,33042,35930,33078,33054,33068,33048,33074,33096,33100,33107,33140,33113,33114,33137,33120,33129,33148,33149,33133,33127,22605,23221,33160,33154,33169,28373,33187,33194,33228,26406,33226,33211,38689,38690,38691,38692,38693,38694,38695,38696,38697,38699,38700,38702,38703,38705,38707,38708,38709,38710,38711,38714,38715,38716,38717,38719,38720,38721,38722,38723,38724,38725,38726,38727,38728,38729,38730,38731,38732,38733,38734,38735,38736,38737,38740,38741,38743,38744,38746,38748,38749,38751,38755,38756,38758,38759,38760,38762,38763,38764,38765,38766,38767,38768,38769,38770,38773,38775,38776,38777,38778,38779,38781,38782,38783,38784,38785,38786,38787,38788,38790,38791,38792,38793,38794,38796,38798,38799,38800,38803,38805,38806,38807,38809,38810,38811,38812,38813,33217,33190,27428,27447,27449,27459,27462,27481,39121,39122,39123,39125,39129,39130,27571,24384,27586,35315,26000,40785,26003,26044,26054,26052,26051,26060,26062,26066,26070,28800,28828,28822,28829,28859,28864,28855,28843,28849,28904,28874,28944,28947,28950,28975,28977,29043,29020,29032,28997,29042,29002,29048,29050,29080,29107,29109,29096,29088,29152,29140,29159,29177,29213,29224,28780,28952,29030,29113,25150,25149,25155,25160,25161,31035,31040,31046,31049,31067,31068,31059,31066,31074,31063,31072,31087,31079,31098,31109,31114,31130,31143,31155,24529,24528,38814,38815,38817,38818,38820,38821,38822,38823,38824,38825,38826,38828,38830,38832,38833,38835,38837,38838,38839,38840,38841,38842,38843,38844,38845,38846,38847,38848,38849,38850,38851,38852,38853,38854,38855,38856,38857,38858,38859,38860,38861,38862,38863,38864,38865,38866,38867,38868,38869,38870,38871,38872,38873,38874,38875,38876,38877,38878,38879,38880,38881,38882,38883,38884,38885,38888,38894,38895,38896,38897,38898,38900,38903,38904,38905,38906,38907,38908,38909,38910,38911,38912,38913,38914,38915,38916,38917,38918,38919,38920,38921,38922,38923,38924,38925,38926,24636,24669,24666,24679,24641,24665,24675,24747,24838,24845,24925,25001,24989,25035,25041,25094,32896,32895,27795,27894,28156,30710,30712,30720,30729,30743,30744,30737,26027,30765,30748,30749,30777,30778,30779,30751,30780,30757,30764,30755,30761,30798,30829,30806,30807,30758,30800,30791,30796,30826,30875,30867,30874,30855,30876,30881,30883,30898,30905,30885,30932,30937,30921,30956,30962,30981,30964,30995,31012,31006,31028,40859,40697,40699,40700,30449,30468,30477,30457,30471,30472,30490,30498,30489,30509,30502,30517,30520,30544,30545,30535,30531,30554,30568,38927,38928,38929,38930,38931,38932,38933,38934,38935,38936,38937,38938,38939,38940,38941,38942,38943,38944,38945,38946,38947,38948,38949,38950,38951,38952,38953,38954,38955,38956,38957,38958,38959,38960,38961,38962,38963,38964,38965,38966,38967,38968,38969,38970,38971,38972,38973,38974,38975,38976,38977,38978,38979,38980,38981,38982,38983,38984,38985,38986,38987,38988,38989,38990,38991,38992,38993,38994,38995,38996,38997,38998,38999,39000,39001,39002,39003,39004,39005,39006,39007,39008,39009,39010,39011,39012,39013,39014,39015,39016,39017,39018,39019,39020,39021,39022,30562,30565,30591,30605,30589,30592,30604,30609,30623,30624,30640,30645,30653,30010,30016,30030,30027,30024,30043,30066,30073,30083,32600,32609,32607,35400,32616,32628,32625,32633,32641,32638,30413,30437,34866,38021,38022,38023,38027,38026,38028,38029,38031,38032,38036,38039,38037,38042,38043,38044,38051,38052,38059,38058,38061,38060,38063,38064,38066,38068,38070,38071,38072,38073,38074,38076,38077,38079,38084,38088,38089,38090,38091,38092,38093,38094,38096,38097,38098,38101,38102,38103,38105,38104,38107,38110,38111,38112,38114,38116,38117,38119,38120,38122,39023,39024,39025,39026,39027,39028,39051,39054,39058,39061,39065,39075,39080,39081,39082,39083,39084,39085,39086,39087,39088,39089,39090,39091,39092,39093,39094,39095,39096,39097,39098,39099,39100,39101,39102,39103,39104,39105,39106,39107,39108,39109,39110,39111,39112,39113,39114,39115,39116,39117,39119,39120,39124,39126,39127,39131,39132,39133,39136,39137,39138,39139,39140,39141,39142,39145,39146,39147,39148,39149,39150,39151,39152,39153,39154,39155,39156,39157,39158,39159,39160,39161,39162,39163,39164,39165,39166,39167,39168,39169,39170,39171,39172,39173,39174,39175,38121,38123,38126,38127,38131,38132,38133,38135,38137,38140,38141,38143,38147,38146,38150,38151,38153,38154,38157,38158,38159,38162,38163,38164,38165,38166,38168,38171,38173,38174,38175,38178,38186,38187,38185,38188,38193,38194,38196,38198,38199,38200,38204,38206,38207,38210,38197,38212,38213,38214,38217,38220,38222,38223,38226,38227,38228,38230,38231,38232,38233,38235,38238,38239,38237,38241,38242,38244,38245,38246,38247,38248,38249,38250,38251,38252,38255,38257,38258,38259,38202,30695,30700,38601,31189,31213,31203,31211,31238,23879,31235,31234,31262,31252,39176,39177,39178,39179,39180,39182,39183,39185,39186,39187,39188,39189,39190,39191,39192,39193,39194,39195,39196,39197,39198,39199,39200,39201,39202,39203,39204,39205,39206,39207,39208,39209,39210,39211,39212,39213,39215,39216,39217,39218,39219,39220,39221,39222,39223,39224,39225,39226,39227,39228,39229,39230,39231,39232,39233,39234,39235,39236,39237,39238,39239,39240,39241,39242,39243,39244,39245,39246,39247,39248,39249,39250,39251,39254,39255,39256,39257,39258,39259,39260,39261,39262,39263,39264,39265,39266,39268,39270,39283,39288,39289,39291,39294,39298,39299,39305,31289,31287,31313,40655,39333,31344,30344,30350,30355,30361,30372,29918,29920,29996,40480,40482,40488,40489,40490,40491,40492,40498,40497,40502,40504,40503,40505,40506,40510,40513,40514,40516,40518,40519,40520,40521,40523,40524,40526,40529,40533,40535,40538,40539,40540,40542,40547,40550,40551,40552,40553,40554,40555,40556,40561,40557,40563,30098,30100,30102,30112,30109,30124,30115,30131,30132,30136,30148,30129,30128,30147,30146,30166,30157,30179,30184,30182,30180,30187,30183,30211,30193,30204,30207,30224,30208,30213,30220,30231,30218,30245,30232,30229,30233,39308,39310,39322,39323,39324,39325,39326,39327,39328,39329,39330,39331,39332,39334,39335,39337,39338,39339,39340,39341,39342,39343,39344,39345,39346,39347,39348,39349,39350,39351,39352,39353,39354,39355,39356,39357,39358,39359,39360,39361,39362,39363,39364,39365,39366,39367,39368,39369,39370,39371,39372,39373,39374,39375,39376,39377,39378,39379,39380,39381,39382,39383,39384,39385,39386,39387,39388,39389,39390,39391,39392,39393,39394,39395,39396,39397,39398,39399,39400,39401,39402,39403,39404,39405,39406,39407,39408,39409,39410,39411,39412,39413,39414,39415,39416,39417,30235,30268,30242,30240,30272,30253,30256,30271,30261,30275,30270,30259,30285,30302,30292,30300,30294,30315,30319,32714,31462,31352,31353,31360,31366,31368,31381,31398,31392,31404,31400,31405,31411,34916,34921,34930,34941,34943,34946,34978,35014,34999,35004,35017,35042,35022,35043,35045,35057,35098,35068,35048,35070,35056,35105,35097,35091,35099,35082,35124,35115,35126,35137,35174,35195,30091,32997,30386,30388,30684,32786,32788,32790,32796,32800,32802,32805,32806,32807,32809,32808,32817,32779,32821,32835,32838,32845,32850,32873,32881,35203,39032,39040,39043,39418,39419,39420,39421,39422,39423,39424,39425,39426,39427,39428,39429,39430,39431,39432,39433,39434,39435,39436,39437,39438,39439,39440,39441,39442,39443,39444,39445,39446,39447,39448,39449,39450,39451,39452,39453,39454,39455,39456,39457,39458,39459,39460,39461,39462,39463,39464,39465,39466,39467,39468,39469,39470,39471,39472,39473,39474,39475,39476,39477,39478,39479,39480,39481,39482,39483,39484,39485,39486,39487,39488,39489,39490,39491,39492,39493,39494,39495,39496,39497,39498,39499,39500,39501,39502,39503,39504,39505,39506,39507,39508,39509,39510,39511,39512,39513,39049,39052,39053,39055,39060,39066,39067,39070,39071,39073,39074,39077,39078,34381,34388,34412,34414,34431,34426,34428,34427,34472,34445,34443,34476,34461,34471,34467,34474,34451,34473,34486,34500,34485,34510,34480,34490,34481,34479,34505,34511,34484,34537,34545,34546,34541,34547,34512,34579,34526,34548,34527,34520,34513,34563,34567,34552,34568,34570,34573,34569,34595,34619,34590,34597,34606,34586,34622,34632,34612,34609,34601,34615,34623,34690,34594,34685,34686,34683,34656,34672,34636,34670,34699,34643,34659,34684,34660,34649,34661,34707,34735,34728,34770,39514,39515,39516,39517,39518,39519,39520,39521,39522,39523,39524,39525,39526,39527,39528,39529,39530,39531,39538,39555,39561,39565,39566,39572,39573,39577,39590,39593,39594,39595,39596,39597,39598,39599,39602,39603,39604,39605,39609,39611,39613,39614,39615,39619,39620,39622,39623,39624,39625,39626,39629,39630,39631,39632,39634,39636,39637,39638,39639,39641,39642,39643,39644,39645,39646,39648,39650,39651,39652,39653,39655,39656,39657,39658,39660,39662,39664,39665,39666,39667,39668,39669,39670,39671,39672,39674,39676,39677,39678,39679,39680,39681,39682,39684,39685,39686,34758,34696,34693,34733,34711,34691,34731,34789,34732,34741,34739,34763,34771,34749,34769,34752,34762,34779,34794,34784,34798,34838,34835,34814,34826,34843,34849,34873,34876,32566,32578,32580,32581,33296,31482,31485,31496,31491,31492,31509,31498,31531,31503,31559,31544,31530,31513,31534,31537,31520,31525,31524,31539,31550,31518,31576,31578,31557,31605,31564,31581,31584,31598,31611,31586,31602,31601,31632,31654,31655,31672,31660,31645,31656,31621,31658,31644,31650,31659,31668,31697,31681,31692,31709,31706,31717,31718,31722,31756,31742,31740,31759,31766,31755,39687,39689,39690,39691,39692,39693,39694,39696,39697,39698,39700,39701,39702,39703,39704,39705,39706,39707,39708,39709,39710,39712,39713,39714,39716,39717,39718,39719,39720,39721,39722,39723,39724,39725,39726,39728,39729,39731,39732,39733,39734,39735,39736,39737,39738,39741,39742,39743,39744,39750,39754,39755,39756,39758,39760,39762,39763,39765,39766,39767,39768,39769,39770,39771,39772,39773,39774,39775,39776,39777,39778,39779,39780,39781,39782,39783,39784,39785,39786,39787,39788,39789,39790,39791,39792,39793,39794,39795,39796,39797,39798,39799,39800,39801,39802,39803,31775,31786,31782,31800,31809,31808,33278,33281,33282,33284,33260,34884,33313,33314,33315,33325,33327,33320,33323,33336,33339,33331,33332,33342,33348,33353,33355,33359,33370,33375,33384,34942,34949,34952,35032,35039,35166,32669,32671,32679,32687,32688,32690,31868,25929,31889,31901,31900,31902,31906,31922,31932,31933,31937,31943,31948,31949,31944,31941,31959,31976,33390,26280,32703,32718,32725,32741,32737,32742,32745,32750,32755,31992,32119,32166,32174,32327,32411,40632,40628,36211,36228,36244,36241,36273,36199,36205,35911,35913,37194,37200,37198,37199,37220,39804,39805,39806,39807,39808,39809,39810,39811,39812,39813,39814,39815,39816,39817,39818,39819,39820,39821,39822,39823,39824,39825,39826,39827,39828,39829,39830,39831,39832,39833,39834,39835,39836,39837,39838,39839,39840,39841,39842,39843,39844,39845,39846,39847,39848,39849,39850,39851,39852,39853,39854,39855,39856,39857,39858,39859,39860,39861,39862,39863,39864,39865,39866,39867,39868,39869,39870,39871,39872,39873,39874,39875,39876,39877,39878,39879,39880,39881,39882,39883,39884,39885,39886,39887,39888,39889,39890,39891,39892,39893,39894,39895,39896,39897,39898,39899,37218,37217,37232,37225,37231,37245,37246,37234,37236,37241,37260,37253,37264,37261,37265,37282,37283,37290,37293,37294,37295,37301,37300,37306,35925,40574,36280,36331,36357,36441,36457,36277,36287,36284,36282,36292,36310,36311,36314,36318,36302,36303,36315,36294,36332,36343,36344,36323,36345,36347,36324,36361,36349,36372,36381,36383,36396,36398,36387,36399,36410,36416,36409,36405,36413,36401,36425,36417,36418,36433,36434,36426,36464,36470,36476,36463,36468,36485,36495,36500,36496,36508,36510,35960,35970,35978,35973,35992,35988,26011,35286,35294,35290,35292,39900,39901,39902,39903,39904,39905,39906,39907,39908,39909,39910,39911,39912,39913,39914,39915,39916,39917,39918,39919,39920,39921,39922,39923,39924,39925,39926,39927,39928,39929,39930,39931,39932,39933,39934,39935,39936,39937,39938,39939,39940,39941,39942,39943,39944,39945,39946,39947,39948,39949,39950,39951,39952,39953,39954,39955,39956,39957,39958,39959,39960,39961,39962,39963,39964,39965,39966,39967,39968,39969,39970,39971,39972,39973,39974,39975,39976,39977,39978,39979,39980,39981,39982,39983,39984,39985,39986,39987,39988,39989,39990,39991,39992,39993,39994,39995,35301,35307,35311,35390,35622,38739,38633,38643,38639,38662,38657,38664,38671,38670,38698,38701,38704,38718,40832,40835,40837,40838,40839,40840,40841,40842,40844,40702,40715,40717,38585,38588,38589,38606,38610,30655,38624,37518,37550,37576,37694,37738,37834,37775,37950,37995,40063,40066,40069,40070,40071,40072,31267,40075,40078,40080,40081,40082,40084,40085,40090,40091,40094,40095,40096,40097,40098,40099,40101,40102,40103,40104,40105,40107,40109,40110,40112,40113,40114,40115,40116,40117,40118,40119,40122,40123,40124,40125,40132,40133,40134,40135,40138,40139,39996,39997,39998,39999,40000,40001,40002,40003,40004,40005,40006,40007,40008,40009,40010,40011,40012,40013,40014,40015,40016,40017,40018,40019,40020,40021,40022,40023,40024,40025,40026,40027,40028,40029,40030,40031,40032,40033,40034,40035,40036,40037,40038,40039,40040,40041,40042,40043,40044,40045,40046,40047,40048,40049,40050,40051,40052,40053,40054,40055,40056,40057,40058,40059,40061,40062,40064,40067,40068,40073,40074,40076,40079,40083,40086,40087,40088,40089,40093,40106,40108,40111,40121,40126,40127,40128,40129,40130,40136,40137,40145,40146,40154,40155,40160,40161,40140,40141,40142,40143,40144,40147,40148,40149,40151,40152,40153,40156,40157,40159,40162,38780,38789,38801,38802,38804,38831,38827,38819,38834,38836,39601,39600,39607,40536,39606,39610,39612,39617,39616,39621,39618,39627,39628,39633,39749,39747,39751,39753,39752,39757,39761,39144,39181,39214,39253,39252,39647,39649,39654,39663,39659,39675,39661,39673,39688,39695,39699,39711,39715,40637,40638,32315,40578,40583,40584,40587,40594,37846,40605,40607,40667,40668,40669,40672,40671,40674,40681,40679,40677,40682,40687,40738,40748,40751,40761,40759,40765,40766,40772,40163,40164,40165,40166,40167,40168,40169,40170,40171,40172,40173,40174,40175,40176,40177,40178,40179,40180,40181,40182,40183,40184,40185,40186,40187,40188,40189,40190,40191,40192,40193,40194,40195,40196,40197,40198,40199,40200,40201,40202,40203,40204,40205,40206,40207,40208,40209,40210,40211,40212,40213,40214,40215,40216,40217,40218,40219,40220,40221,40222,40223,40224,40225,40226,40227,40228,40229,40230,40231,40232,40233,40234,40235,40236,40237,40238,40239,40240,40241,40242,40243,40244,40245,40246,40247,40248,40249,40250,40251,40252,40253,40254,40255,40256,40257,40258,57908,57909,57910,57911,57912,57913,57914,57915,57916,57917,57918,57919,57920,57921,57922,57923,57924,57925,57926,57927,57928,57929,57930,57931,57932,57933,57934,57935,57936,57937,57938,57939,57940,57941,57942,57943,57944,57945,57946,57947,57948,57949,57950,57951,57952,57953,57954,57955,57956,57957,57958,57959,57960,57961,57962,57963,57964,57965,57966,57967,57968,57969,57970,57971,57972,57973,57974,57975,57976,57977,57978,57979,57980,57981,57982,57983,57984,57985,57986,57987,57988,57989,57990,57991,57992,57993,57994,57995,57996,57997,57998,57999,58000,58001,40259,40260,40261,40262,40263,40264,40265,40266,40267,40268,40269,40270,40271,40272,40273,40274,40275,40276,40277,40278,40279,40280,40281,40282,40283,40284,40285,40286,40287,40288,40289,40290,40291,40292,40293,40294,40295,40296,40297,40298,40299,40300,40301,40302,40303,40304,40305,40306,40307,40308,40309,40310,40311,40312,40313,40314,40315,40316,40317,40318,40319,40320,40321,40322,40323,40324,40325,40326,40327,40328,40329,40330,40331,40332,40333,40334,40335,40336,40337,40338,40339,40340,40341,40342,40343,40344,40345,40346,40347,40348,40349,40350,40351,40352,40353,40354,58002,58003,58004,58005,58006,58007,58008,58009,58010,58011,58012,58013,58014,58015,58016,58017,58018,58019,58020,58021,58022,58023,58024,58025,58026,58027,58028,58029,58030,58031,58032,58033,58034,58035,58036,58037,58038,58039,58040,58041,58042,58043,58044,58045,58046,58047,58048,58049,58050,58051,58052,58053,58054,58055,58056,58057,58058,58059,58060,58061,58062,58063,58064,58065,58066,58067,58068,58069,58070,58071,58072,58073,58074,58075,58076,58077,58078,58079,58080,58081,58082,58083,58084,58085,58086,58087,58088,58089,58090,58091,58092,58093,58094,58095,40355,40356,40357,40358,40359,40360,40361,40362,40363,40364,40365,40366,40367,40368,40369,40370,40371,40372,40373,40374,40375,40376,40377,40378,40379,40380,40381,40382,40383,40384,40385,40386,40387,40388,40389,40390,40391,40392,40393,40394,40395,40396,40397,40398,40399,40400,40401,40402,40403,40404,40405,40406,40407,40408,40409,40410,40411,40412,40413,40414,40415,40416,40417,40418,40419,40420,40421,40422,40423,40424,40425,40426,40427,40428,40429,40430,40431,40432,40433,40434,40435,40436,40437,40438,40439,40440,40441,40442,40443,40444,40445,40446,40447,40448,40449,40450,58096,58097,58098,58099,58100,58101,58102,58103,58104,58105,58106,58107,58108,58109,58110,58111,58112,58113,58114,58115,58116,58117,58118,58119,58120,58121,58122,58123,58124,58125,58126,58127,58128,58129,58130,58131,58132,58133,58134,58135,58136,58137,58138,58139,58140,58141,58142,58143,58144,58145,58146,58147,58148,58149,58150,58151,58152,58153,58154,58155,58156,58157,58158,58159,58160,58161,58162,58163,58164,58165,58166,58167,58168,58169,58170,58171,58172,58173,58174,58175,58176,58177,58178,58179,58180,58181,58182,58183,58184,58185,58186,58187,58188,58189,40451,40452,40453,40454,40455,40456,40457,40458,40459,40460,40461,40462,40463,40464,40465,40466,40467,40468,40469,40470,40471,40472,40473,40474,40475,40476,40477,40478,40484,40487,40494,40496,40500,40507,40508,40512,40525,40528,40530,40531,40532,40534,40537,40541,40543,40544,40545,40546,40549,40558,40559,40562,40564,40565,40566,40567,40568,40569,40570,40571,40572,40573,40576,40577,40579,40580,40581,40582,40585,40586,40588,40589,40590,40591,40592,40593,40596,40597,40598,40599,40600,40601,40602,40603,40604,40606,40608,40609,40610,40611,40612,40613,40615,40616,40617,40618,58190,58191,58192,58193,58194,58195,58196,58197,58198,58199,58200,58201,58202,58203,58204,58205,58206,58207,58208,58209,58210,58211,58212,58213,58214,58215,58216,58217,58218,58219,58220,58221,58222,58223,58224,58225,58226,58227,58228,58229,58230,58231,58232,58233,58234,58235,58236,58237,58238,58239,58240,58241,58242,58243,58244,58245,58246,58247,58248,58249,58250,58251,58252,58253,58254,58255,58256,58257,58258,58259,58260,58261,58262,58263,58264,58265,58266,58267,58268,58269,58270,58271,58272,58273,58274,58275,58276,58277,58278,58279,58280,58281,58282,58283,40619,40620,40621,40622,40623,40624,40625,40626,40627,40629,40630,40631,40633,40634,40636,40639,40640,40641,40642,40643,40645,40646,40647,40648,40650,40651,40652,40656,40658,40659,40661,40662,40663,40665,40666,40670,40673,40675,40676,40678,40680,40683,40684,40685,40686,40688,40689,40690,40691,40692,40693,40694,40695,40696,40698,40701,40703,40704,40705,40706,40707,40708,40709,40710,40711,40712,40713,40714,40716,40719,40721,40722,40724,40725,40726,40728,40730,40731,40732,40733,40734,40735,40737,40739,40740,40741,40742,40743,40744,40745,40746,40747,40749,40750,40752,40753,58284,58285,58286,58287,58288,58289,58290,58291,58292,58293,58294,58295,58296,58297,58298,58299,58300,58301,58302,58303,58304,58305,58306,58307,58308,58309,58310,58311,58312,58313,58314,58315,58316,58317,58318,58319,58320,58321,58322,58323,58324,58325,58326,58327,58328,58329,58330,58331,58332,58333,58334,58335,58336,58337,58338,58339,58340,58341,58342,58343,58344,58345,58346,58347,58348,58349,58350,58351,58352,58353,58354,58355,58356,58357,58358,58359,58360,58361,58362,58363,58364,58365,58366,58367,58368,58369,58370,58371,58372,58373,58374,58375,58376,58377,40754,40755,40756,40757,40758,40760,40762,40764,40767,40768,40769,40770,40771,40773,40774,40775,40776,40777,40778,40779,40780,40781,40782,40783,40786,40787,40788,40789,40790,40791,40792,40793,40794,40795,40796,40797,40798,40799,40800,40801,40802,40803,40804,40805,40806,40807,40808,40809,40810,40811,40812,40813,40814,40815,40816,40817,40818,40819,40820,40821,40822,40823,40824,40825,40826,40827,40828,40829,40830,40833,40834,40845,40846,40847,40848,40849,40850,40851,40852,40853,40854,40855,40856,40860,40861,40862,40865,40866,40867,40868,40869,63788,63865,63893,63975,63985,58378,58379,58380,58381,58382,58383,58384,58385,58386,58387,58388,58389,58390,58391,58392,58393,58394,58395,58396,58397,58398,58399,58400,58401,58402,58403,58404,58405,58406,58407,58408,58409,58410,58411,58412,58413,58414,58415,58416,58417,58418,58419,58420,58421,58422,58423,58424,58425,58426,58427,58428,58429,58430,58431,58432,58433,58434,58435,58436,58437,58438,58439,58440,58441,58442,58443,58444,58445,58446,58447,58448,58449,58450,58451,58452,58453,58454,58455,58456,58457,58458,58459,58460,58461,58462,58463,58464,58465,58466,58467,58468,58469,58470,58471,64012,64013,64014,64015,64017,64019,64020,64024,64031,64032,64033,64035,64036,64039,64040,64041,11905,59414,59415,59416,11908,13427,13383,11912,11915,59422,13726,13850,13838,11916,11927,14702,14616,59430,14799,14815,14963,14800,59435,59436,15182,15470,15584,11943,59441,59442,11946,16470,16735,11950,17207,11955,11958,11959,59451,17329,17324,11963,17373,17622,18017,17996,59459,18211,18217,18300,18317,11978,18759,18810,18813,18818,18819,18821,18822,18847,18843,18871,18870,59476,59477,19619,19615,19616,19617,19575,19618,19731,19732,19733,19734,19735,19736,19737,19886,59492,58472,58473,58474,58475,58476,58477,58478,58479,58480,58481,58482,58483,58484,58485,58486,58487,58488,58489,58490,58491,58492,58493,58494,58495,58496,58497,58498,58499,58500,58501,58502,58503,58504,58505,58506,58507,58508,58509,58510,58511,58512,58513,58514,58515,58516,58517,58518,58519,58520,58521,58522,58523,58524,58525,58526,58527,58528,58529,58530,58531,58532,58533,58534,58535,58536,58537,58538,58539,58540,58541,58542,58543,58544,58545,58546,58547,58548,58549,58550,58551,58552,58553,58554,58555,58556,58557,58558,58559,58560,58561,58562,58563,58564,58565}; +gb18030_decoder::range gb18030_decoder::m_ranges[] = {{0,128},{36,165},{38,169},{45,178},{50,184},{81,216},{89,226},{95,235},{96,238},{100,244},{103,248},{104,251},{105,253},{109,258},{126,276},{133,284},{148,300},{172,325},{175,329},{179,334},{208,364},{306,463},{307,465},{308,467},{309,469},{310,471},{311,473},{312,475},{313,477},{341,506},{428,594},{443,610},{544,712},{545,716},{558,730},{741,930},{742,938},{749,962},{750,970},{805,1026},{819,1104},{820,1106},{7922,8209},{7924,8215},{7925,8218},{7927,8222},{7934,8231},{7943,8241},{7944,8244},{7945,8246},{7950,8252},{8062,8365},{8148,8452},{8149,8454},{8152,8458},{8164,8471},{8174,8482},{8236,8556},{8240,8570},{8262,8596},{8264,8602},{8374,8713},{8380,8720},{8381,8722},{8384,8726},{8388,8731},{8390,8737},{8392,8740},{8393,8742},{8394,8748},{8396,8751},{8401,8760},{8406,8766},{8416,8777},{8419,8781},{8424,8787},{8437,8802},{8439,8808},{8445,8816},{8482,8854},{8485,8858},{8496,8870},{8521,8896},{8603,8979},{8936,9322},{8946,9372},{9046,9548},{9050,9588},{9063,9616},{9066,9622},{9076,9634},{9092,9652},{9100,9662},{9108,9672},{9111,9676},{9113,9680},{9131,9702},{9162,9735},{9164,9738},{9218,9793},{9219,9795},{11329,11906},{11331,11909},{11334,11913},{11336,11917},{11346,11928},{11361,11944},{11363,11947},{11366,11951},{11370,11956},{11372,11960},{11375,11964},{11389,11979},{11682,12284},{11686,12292},{11687,12312},{11692,12319},{11694,12330},{11714,12351},{11716,12436},{11723,12447},{11725,12535},{11730,12543},{11736,12586},{11982,12842},{11989,12850},{12102,12964},{12336,13200},{12348,13215},{12350,13218},{12384,13253},{12393,13263},{12395,13267},{12397,13270},{12510,13384},{12553,13428},{12851,13727},{12962,13839},{12973,13851},{13738,14617},{13823,14703},{13919,14801},{13933,14816},{14080,14964},{14298,15183},{14585,15471},{14698,15585},{15583,16471},{15847,16736},{16318,17208},{16434,17325},{16438,17330},{16481,17374},{16729,17623},{17102,17997},{17122,18018},{17315,18212},{17320,18218},{17402,18301},{17418,18318},{17859,18760},{17909,18811},{17911,18814},{17915,18820},{17916,18823},{17936,18844},{17939,18848},{17961,18872},{18664,19576},{18703,19620},{18814,19738},{18962,19887},{19043,40870},{33469,59244},{33470,59336},{33471,59367},{33484,59413},{33485,59417},{33490,59423},{33497,59431},{33501,59437},{33505,59443},{33513,59452},{33520,59460},{33536,59478},{33550,59493},{37845,63789},{37921,63866},{37948,63894},{38029,63976},{38038,63986},{38064,64016},{38065,64018},{38066,64021},{38069,64025},{38075,64034},{38076,64037},{38078,64042},{39108,65074},{39109,65093},{39113,65107},{39114,65112},{39115,65127},{39116,65132},{39265,65375},{39394,65510},{189000,65536}}; + +// https://encoding.spec.whatwg.org/#index-gb18030-ranges-code-point +int gb18030_decoder::ranges_code_point(int pointer) +{ + // 1. + if ((pointer > 39419 && pointer < 189000) || pointer > 1237575) + return null; + + // 2. + if (pointer == 7457) + return 0xE7C7; + + // 3. Let offset be the last pointer in index gb18030 ranges that is less than or equal to pointer and + // let code point offset be its corresponding code point. + int i = 0; + while (i < countof(m_ranges) && m_ranges[i].pointer <= pointer) i++; + int offset = m_ranges[i-1].pointer; + int code_point_offset = m_ranges[i-1].code_point; + + // 4. + return code_point_offset + pointer - offset; +} + +// https://encoding.spec.whatwg.org/#gb18030-decoder +decoder::result gb18030_decoder::handler(string& input, int& index, int ch[2]) +{ + int b = index == (int)input.size() ? EOF : (byte)input[index++]; // read input byte + + // 1. If byte is end-of-queue and gb18030 first, gb18030 second, and gb18030 third are 0x00, return finished. + if (b == EOF && m_first == 0 && m_second == 0 && m_third == 0) + return result_finished; + + // 2. + if (b == EOF && (m_first != 0 || m_second != 0 || m_third != 0)) + { + m_first = 0; + m_second = 0; + m_third = 0; + return result_error; + } + + // 3. If gb18030 third is not 0x00, then: + if (m_third != 0) + { + // 1. If byte is not in the range 0x30 to 0x39, inclusive, then: + if (!(b >= 0x30 && b <= 0x39)) + { + // 1. Restore « gb18030 second, gb18030 third, byte » to ioQueue. + input.insert(index, {(char)m_second, (char)m_third, (char)b}); + + // 2. Set gb18030 first, gb18030 second, and gb18030 third to 0x00. + m_first = 0; + m_second = 0; + m_third = 0; + + // 3. Return error. + return result_error; + } + + // 2. Let code point be the index gb18030 ranges code point for ((gb18030 first − 0x81) × (10 × 126 × 10)) + ((gb18030 second − 0x30) × (10 × 126)) + ((gb18030 third − 0x81) × 10) + byte − 0x30. + int code_point = ranges_code_point ( + (m_first - 0x81) * 10 * 126 * 10 + + (m_second - 0x30) * 10 * 126 + + (m_third - 0x81) * 10 + + b - 0x30 ); + + // 3. + m_first = 0; + m_second = 0; + m_third = 0; + + // 4. + if (code_point == null) + return result_error; + + // 5. + *ch = code_point; + return result_codepoint; + } + + // 4. + if (m_second != 0) + { + if (b >= 0x81 && b <= 0xFE) + { + m_third = b; + return result_continue; + } + + // Restore « gb18030 second, byte » to ioQueue + input.insert(index, {(char)m_second, (char)b}); + m_first = 0; + m_second = 0; + return result_error; + } + + // 5. + if (m_first != 0) + { + // 1. + if (b >= 0x30 && b <= 0x39) + { + m_second = b; + return result_continue; + } + + // 2. + int lead = m_first; + int pointer = null; + m_first = 0; + + // 3. + int offset = b < 0x7F ? 0x40 : 0x41; + + // 4. + if ((b >= 0x40 && b <= 0x7E) || (b >= 0x80 && b <= 0xFE)) + pointer = (lead - 0x81) * 190 + (b - offset); + + // 5. + int code_point = pointer != null ? index_code_point(pointer, m_index) : null; + + // 6. + if (code_point != null) + { + *ch = code_point; + return result_codepoint; + } + + // 7. If byte is an ASCII byte, restore byte to ioQueue. + if (b >= 0 && b <= 0x7F) index--; + + // 8. + return result_error; + } + + // 6. + if (b >= 0 && b <= 0x7F) + { + *ch = b; + return result_codepoint; + } + + // 7. + if (b == 0x80) + { + *ch = 0x20AC; + return result_codepoint; + } + + // 8. + if (b >= 0x81 && b <= 0xFE) + { + m_first = b; + return result_continue; + } + + // 9. + return result_error; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +struct big5_decoder final : decoder +{ + int m_lead = 0; + + result handler(string& input, int& index, int ch[2]) override; + + static int m_index[19782]; +}; + +// https://encoding.spec.whatwg.org/indexes.json +int big5_decoder::m_index[] = {null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,17392,19506,17923,17830,17784,160359,19831,17843,162993,19682,163013,15253,18230,18244,19527,19520,148159,144919,160594,159371,159954,19543,172881,18255,17882,19589,162924,19719,19108,18081,158499,29221,154196,137827,146950,147297,26189,22267,null,32149,22813,166841,15860,38708,162799,23515,138590,23204,13861,171696,23249,23479,23804,26478,34195,170309,29793,29853,14453,138579,145054,155681,16108,153822,15093,31484,40855,147809,166157,143850,133770,143966,17162,33924,40854,37935,18736,34323,22678,38730,37400,31184,31282,26208,27177,34973,29772,31685,26498,31276,21071,36934,13542,29636,155065,29894,40903,22451,18735,21580,16689,145038,22552,31346,162661,35727,18094,159368,16769,155033,31662,140476,40904,140481,140489,140492,40905,34052,144827,16564,40906,17633,175615,25281,28782,40907,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,12736,12737,12738,12739,12740,131340,12741,131281,131277,12742,12743,131275,139240,12744,131274,12745,12746,12747,12748,131342,12749,12750,256,193,461,192,274,201,282,200,332,211,465,210,null,7870,null,7872,202,257,225,462,224,593,275,233,283,232,299,237,464,236,333,243,466,242,363,250,468,249,470,472,474,476,252,null,7871,null,7873,234,609,9178,9179,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,172969,135493,null,25866,null,null,20029,28381,40270,37343,null,null,161589,25745,20250,20264,20392,20822,20852,20892,20964,21153,21160,21307,21326,21457,21464,22242,22768,22788,22791,22834,22836,23398,23454,23455,23706,24198,24635,25993,26622,26628,26725,27982,28860,30005,32420,32428,32442,32455,32463,32479,32518,32567,33402,33487,33647,35270,35774,35810,36710,36711,36718,29713,31996,32205,26950,31433,21031,null,null,null,null,37260,30904,37214,32956,null,36107,33014,133607,null,null,32927,40647,19661,40393,40460,19518,171510,159758,40458,172339,13761,null,28314,33342,29977,null,18705,39532,39567,40857,31111,164972,138698,132560,142054,20004,20097,20096,20103,20159,20203,20279,13388,20413,15944,20483,20616,13437,13459,13477,20870,22789,20955,20988,20997,20105,21113,21136,21287,13767,21417,13649,21424,13651,21442,21539,13677,13682,13953,21651,21667,21684,21689,21712,21743,21784,21795,21800,13720,21823,13733,13759,21975,13765,163204,21797,null,134210,134421,151851,21904,142534,14828,131905,36422,150968,169189,16467,164030,30586,142392,14900,18389,164189,158194,151018,25821,134524,135092,134357,135412,25741,36478,134806,134155,135012,142505,164438,148691,null,134470,170573,164073,18420,151207,142530,39602,14951,169460,16365,13574,152263,169940,161992,142660,40302,38933,null,17369,155813,25780,21731,142668,142282,135287,14843,135279,157402,157462,162208,25834,151634,134211,36456,139681,166732,132913,null,18443,131497,16378,22643,142733,null,148936,132348,155799,134988,134550,21881,16571,17338,null,19124,141926,135325,33194,39157,134556,25465,14846,141173,36288,22177,25724,15939,null,173569,134665,142031,142537,null,135368,145858,14738,14854,164507,13688,155209,139463,22098,134961,142514,169760,13500,27709,151099,null,null,161140,142987,139784,173659,167117,134778,134196,157724,32659,135375,141315,141625,13819,152035,134796,135053,134826,16275,134960,134471,135503,134732,null,134827,134057,134472,135360,135485,16377,140950,25650,135085,144372,161337,142286,134526,134527,142417,142421,14872,134808,135367,134958,173618,158544,167122,167321,167114,38314,21708,33476,21945,null,171715,39974,39606,161630,142830,28992,33133,33004,23580,157042,33076,14231,21343,164029,37302,134906,134671,134775,134907,13789,151019,13833,134358,22191,141237,135369,134672,134776,135288,135496,164359,136277,134777,151120,142756,23124,135197,135198,135413,135414,22428,134673,161428,164557,135093,134779,151934,14083,135094,135552,152280,172733,149978,137274,147831,164476,22681,21096,13850,153405,31666,23400,18432,19244,40743,18919,39967,39821,154484,143677,22011,13810,22153,20008,22786,138177,194680,38737,131206,20059,20155,13630,23587,24401,24516,14586,25164,25909,27514,27701,27706,28780,29227,20012,29357,149737,32594,31035,31993,32595,156266,13505,null,156491,32770,32896,157202,158033,21341,34916,35265,161970,35744,36125,38021,38264,38271,38376,167439,38886,39029,39118,39134,39267,170000,40060,40479,40644,27503,63751,20023,131207,38429,25143,38050,null,20539,28158,171123,40870,15817,34959,147790,28791,23797,19232,152013,13657,154928,24866,166450,36775,37366,29073,26393,29626,144001,172295,15499,137600,19216,30948,29698,20910,165647,16393,27235,172730,16931,34319,133743,31274,170311,166634,38741,28749,21284,139390,37876,30425,166371,40871,30685,20131,20464,20668,20015,20247,40872,21556,32139,22674,22736,138678,24210,24217,24514,141074,25995,144377,26905,27203,146531,27903,null,29184,148741,29580,16091,150035,23317,29881,35715,154788,153237,31379,31724,31939,32364,33528,34199,40873,34960,40874,36537,40875,36815,34143,39392,37409,40876,167353,136255,16497,17058,23066,null,null,null,39016,26475,17014,22333,null,34262,149883,33471,160013,19585,159092,23931,158485,159678,40877,40878,23446,40879,26343,32347,28247,31178,15752,17603,143958,141206,17306,17718,null,23765,146202,35577,23672,15634,144721,23928,40882,29015,17752,147692,138787,19575,14712,13386,131492,158785,35532,20404,131641,22975,33132,38998,170234,24379,134047,null,139713,166253,16642,18107,168057,16135,40883,172469,16632,14294,18167,158790,16764,165554,160767,17773,14548,152730,17761,17691,19849,19579,19830,17898,16328,150287,13921,17630,17597,16877,23870,23880,23894,15868,14351,23972,23993,14368,14392,24130,24253,24357,24451,14600,14612,14655,14669,24791,24893,23781,14729,25015,25017,25039,14776,25132,25232,25317,25368,14840,22193,14851,25570,25595,25607,25690,14923,25792,23829,22049,40863,14999,25990,15037,26111,26195,15090,26258,15138,26390,15170,26532,26624,15192,26698,26756,15218,15217,15227,26889,26947,29276,26980,27039,27013,15292,27094,15325,27237,27252,27249,27266,15340,27289,15346,27307,27317,27348,27382,27521,27585,27626,27765,27818,15563,27906,27910,27942,28033,15599,28068,28081,28181,28184,28201,28294,166336,28347,28386,28378,40831,28392,28393,28452,28468,15686,147265,28545,28606,15722,15733,29111,23705,15754,28716,15761,28752,28756,28783,28799,28809,131877,17345,13809,134872,147159,22462,159443,28990,153568,13902,27042,166889,23412,31305,153825,169177,31333,31357,154028,31419,31408,31426,31427,29137,156813,16842,31450,31453,31466,16879,21682,154625,31499,31573,31529,152334,154878,31650,31599,33692,154548,158847,31696,33825,31634,31672,154912,15789,154725,33938,31738,31750,31797,154817,31812,31875,149634,31910,26237,148856,31945,31943,31974,31860,31987,31989,31950,32359,17693,159300,32093,159446,29837,32137,32171,28981,32179,32210,147543,155689,32228,15635,32245,137209,32229,164717,32285,155937,155994,32366,32402,17195,37996,32295,32576,32577,32583,31030,156368,39393,32663,156497,32675,136801,131176,17756,145254,17667,164666,32762,156809,32773,32776,32797,32808,32815,172167,158915,32827,32828,32865,141076,18825,157222,146915,157416,26405,32935,166472,33031,33050,22704,141046,27775,156824,151480,25831,136330,33304,137310,27219,150117,150165,17530,33321,133901,158290,146814,20473,136445,34018,33634,158474,149927,144688,137075,146936,33450,26907,194964,16859,34123,33488,33562,134678,137140,14017,143741,144730,33403,33506,33560,147083,159139,158469,158615,144846,15807,33565,21996,33669,17675,159141,33708,33729,33747,13438,159444,27223,34138,13462,159298,143087,33880,154596,33905,15827,17636,27303,33866,146613,31064,33960,158614,159351,159299,34014,33807,33681,17568,33939,34020,154769,16960,154816,17731,34100,23282,159385,17703,34163,17686,26559,34326,165413,165435,34241,159880,34306,136578,159949,194994,17770,34344,13896,137378,21495,160666,34430,34673,172280,34798,142375,34737,34778,34831,22113,34412,26710,17935,34885,34886,161248,146873,161252,34910,34972,18011,34996,34997,25537,35013,30583,161551,35207,35210,35238,35241,35239,35260,166437,35303,162084,162493,35484,30611,37374,35472,162393,31465,162618,147343,18195,162616,29052,35596,35615,152624,152933,35647,35660,35661,35497,150138,35728,35739,35503,136927,17941,34895,35995,163156,163215,195028,14117,163155,36054,163224,163261,36114,36099,137488,36059,28764,36113,150729,16080,36215,36265,163842,135188,149898,15228,164284,160012,31463,36525,36534,36547,37588,36633,36653,164709,164882,36773,37635,172703,133712,36787,18730,166366,165181,146875,24312,143970,36857,172052,165564,165121,140069,14720,159447,36919,165180,162494,36961,165228,165387,37032,165651,37060,165606,37038,37117,37223,15088,37289,37316,31916,166195,138889,37390,27807,37441,37474,153017,37561,166598,146587,166668,153051,134449,37676,37739,166625,166891,28815,23235,166626,166629,18789,37444,166892,166969,166911,37747,37979,36540,38277,38310,37926,38304,28662,17081,140922,165592,135804,146990,18911,27676,38523,38550,16748,38563,159445,25050,38582,30965,166624,38589,21452,18849,158904,131700,156688,168111,168165,150225,137493,144138,38705,34370,38710,18959,17725,17797,150249,28789,23361,38683,38748,168405,38743,23370,168427,38751,37925,20688,143543,143548,38793,38815,38833,38846,38848,38866,38880,152684,38894,29724,169011,38911,38901,168989,162170,19153,38964,38963,38987,39014,15118,160117,15697,132656,147804,153350,39114,39095,39112,39111,19199,159015,136915,21936,39137,39142,39148,37752,39225,150057,19314,170071,170245,39413,39436,39483,39440,39512,153381,14020,168113,170965,39648,39650,170757,39668,19470,39700,39725,165376,20532,39732,158120,14531,143485,39760,39744,171326,23109,137315,39822,148043,39938,39935,39948,171624,40404,171959,172434,172459,172257,172323,172511,40318,40323,172340,40462,26760,40388,139611,172435,172576,137531,172595,40249,172217,172724,40592,40597,40606,40610,19764,40618,40623,148324,40641,15200,14821,15645,20274,14270,166955,40706,40712,19350,37924,159138,40727,40726,40761,22175,22154,40773,39352,168075,38898,33919,40802,40809,31452,40846,29206,19390,149877,149947,29047,150008,148296,150097,29598,166874,137466,31135,166270,167478,37737,37875,166468,37612,37761,37835,166252,148665,29207,16107,30578,31299,28880,148595,148472,29054,137199,28835,137406,144793,16071,137349,152623,137208,14114,136955,137273,14049,137076,137425,155467,14115,136896,22363,150053,136190,135848,136134,136374,34051,145062,34051,33877,149908,160101,146993,152924,147195,159826,17652,145134,170397,159526,26617,14131,15381,15847,22636,137506,26640,16471,145215,147681,147595,147727,158753,21707,22174,157361,22162,135135,134056,134669,37830,166675,37788,20216,20779,14361,148534,20156,132197,131967,20299,20362,153169,23144,131499,132043,14745,131850,132116,13365,20265,131776,167603,131701,35546,131596,20120,20685,20749,20386,20227,150030,147082,20290,20526,20588,20609,20428,20453,20568,20732,20825,20827,20829,20830,28278,144789,147001,147135,28018,137348,147081,20904,20931,132576,17629,132259,132242,132241,36218,166556,132878,21081,21156,133235,21217,37742,18042,29068,148364,134176,149932,135396,27089,134685,29817,16094,29849,29716,29782,29592,19342,150204,147597,21456,13700,29199,147657,21940,131909,21709,134086,22301,37469,38644,37734,22493,22413,22399,13886,22731,23193,166470,136954,137071,136976,23084,22968,37519,23166,23247,23058,153926,137715,137313,148117,14069,27909,29763,23073,155267,23169,166871,132115,37856,29836,135939,28933,18802,37896,166395,37821,14240,23582,23710,24158,24136,137622,137596,146158,24269,23375,137475,137476,14081,137376,14045,136958,14035,33066,166471,138682,144498,166312,24332,24334,137511,137131,23147,137019,23364,34324,161277,34912,24702,141408,140843,24539,16056,140719,140734,168072,159603,25024,131134,131142,140827,24985,24984,24693,142491,142599,149204,168269,25713,149093,142186,14889,142114,144464,170218,142968,25399,173147,25782,25393,25553,149987,142695,25252,142497,25659,25963,26994,15348,143502,144045,149897,144043,21773,144096,137433,169023,26318,144009,143795,15072,16784,152964,166690,152975,136956,152923,152613,30958,143619,137258,143924,13412,143887,143746,148169,26254,159012,26219,19347,26160,161904,138731,26211,144082,144097,26142,153714,14545,145466,145340,15257,145314,144382,29904,15254,26511,149034,26806,26654,15300,27326,14435,145365,148615,27187,27218,27337,27397,137490,25873,26776,27212,15319,27258,27479,147392,146586,37792,37618,166890,166603,37513,163870,166364,37991,28069,28427,149996,28007,147327,15759,28164,147516,23101,28170,22599,27940,30786,28987,148250,148086,28913,29264,29319,29332,149391,149285,20857,150180,132587,29818,147192,144991,150090,149783,155617,16134,16049,150239,166947,147253,24743,16115,29900,29756,37767,29751,17567,159210,17745,30083,16227,150745,150790,16216,30037,30323,173510,15129,29800,166604,149931,149902,15099,15821,150094,16127,149957,149747,37370,22322,37698,166627,137316,20703,152097,152039,30584,143922,30478,30479,30587,149143,145281,14942,149744,29752,29851,16063,150202,150215,16584,150166,156078,37639,152961,30750,30861,30856,30930,29648,31065,161601,153315,16654,31131,33942,31141,27181,147194,31290,31220,16750,136934,16690,37429,31217,134476,149900,131737,146874,137070,13719,21867,13680,13994,131540,134157,31458,23129,141045,154287,154268,23053,131675,30960,23082,154566,31486,16889,31837,31853,16913,154547,155324,155302,31949,150009,137136,31886,31868,31918,27314,32220,32263,32211,32590,156257,155996,162632,32151,155266,17002,158581,133398,26582,131150,144847,22468,156690,156664,149858,32733,31527,133164,154345,154947,31500,155150,39398,34373,39523,27164,144447,14818,150007,157101,39455,157088,33920,160039,158929,17642,33079,17410,32966,33033,33090,157620,39107,158274,33378,33381,158289,33875,159143,34320,160283,23174,16767,137280,23339,137377,23268,137432,34464,195004,146831,34861,160802,23042,34926,20293,34951,35007,35046,35173,35149,153219,35156,161669,161668,166901,166873,166812,166393,16045,33955,18165,18127,14322,35389,35356,169032,24397,37419,148100,26068,28969,28868,137285,40301,35999,36073,163292,22938,30659,23024,17262,14036,36394,36519,150537,36656,36682,17140,27736,28603,140065,18587,28537,28299,137178,39913,14005,149807,37051,37015,21873,18694,37307,37892,166475,16482,166652,37927,166941,166971,34021,35371,38297,38311,38295,38294,167220,29765,16066,149759,150082,148458,16103,143909,38543,167655,167526,167525,16076,149997,150136,147438,29714,29803,16124,38721,168112,26695,18973,168083,153567,38749,37736,166281,166950,166703,156606,37562,23313,35689,18748,29689,147995,38811,38769,39224,134950,24001,166853,150194,38943,169178,37622,169431,37349,17600,166736,150119,166756,39132,166469,16128,37418,18725,33812,39227,39245,162566,15869,39323,19311,39338,39516,166757,153800,27279,39457,23294,39471,170225,19344,170312,39356,19389,19351,37757,22642,135938,22562,149944,136424,30788,141087,146872,26821,15741,37976,14631,24912,141185,141675,24839,40015,40019,40059,39989,39952,39807,39887,171565,39839,172533,172286,40225,19630,147716,40472,19632,40204,172468,172269,172275,170287,40357,33981,159250,159711,158594,34300,17715,159140,159364,159216,33824,34286,159232,145367,155748,31202,144796,144960,18733,149982,15714,37851,37566,37704,131775,30905,37495,37965,20452,13376,36964,152925,30781,30804,30902,30795,137047,143817,149825,13978,20338,28634,28633,28702,28702,21524,147893,22459,22771,22410,40214,22487,28980,13487,147884,29163,158784,151447,23336,137141,166473,24844,23246,23051,17084,148616,14124,19323,166396,37819,37816,137430,134941,33906,158912,136211,148218,142374,148417,22932,146871,157505,32168,155995,155812,149945,149899,166394,37605,29666,16105,29876,166755,137375,16097,150195,27352,29683,29691,16086,150078,150164,137177,150118,132007,136228,149989,29768,149782,28837,149878,37508,29670,37727,132350,37681,166606,166422,37766,166887,153045,18741,166530,29035,149827,134399,22180,132634,134123,134328,21762,31172,137210,32254,136898,150096,137298,17710,37889,14090,166592,149933,22960,137407,137347,160900,23201,14050,146779,14000,37471,23161,166529,137314,37748,15565,133812,19094,14730,20724,15721,15692,136092,29045,17147,164376,28175,168164,17643,27991,163407,28775,27823,15574,147437,146989,28162,28428,15727,132085,30033,14012,13512,18048,16090,18545,22980,37486,18750,36673,166940,158656,22546,22472,14038,136274,28926,148322,150129,143331,135856,140221,26809,26983,136088,144613,162804,145119,166531,145366,144378,150687,27162,145069,158903,33854,17631,17614,159014,159057,158850,159710,28439,160009,33597,137018,33773,158848,159827,137179,22921,23170,137139,23137,23153,137477,147964,14125,23023,137020,14023,29070,37776,26266,148133,23150,23083,148115,27179,147193,161590,148571,148170,28957,148057,166369,20400,159016,23746,148686,163405,148413,27148,148054,135940,28838,28979,148457,15781,27871,194597,150095,32357,23019,23855,15859,24412,150109,137183,32164,33830,21637,146170,144128,131604,22398,133333,132633,16357,139166,172726,28675,168283,23920,29583,31955,166489,168992,20424,32743,29389,29456,162548,29496,29497,153334,29505,29512,16041,162584,36972,29173,149746,29665,33270,16074,30476,16081,27810,22269,29721,29726,29727,16098,16112,16116,16122,29907,16142,16211,30018,30061,30066,30093,16252,30152,30172,16320,30285,16343,30324,16348,30330,151388,29064,22051,35200,22633,16413,30531,16441,26465,16453,13787,30616,16490,16495,23646,30654,30667,22770,30744,28857,30748,16552,30777,30791,30801,30822,33864,152885,31027,26627,31026,16643,16649,31121,31129,36795,31238,36796,16743,31377,16818,31420,33401,16836,31439,31451,16847,20001,31586,31596,31611,31762,31771,16992,17018,31867,31900,17036,31928,17044,31981,36755,28864,134351,32207,32212,32208,32253,32686,32692,29343,17303,32800,32805,31545,32814,32817,32852,15820,22452,28832,32951,33001,17389,33036,29482,33038,33042,30048,33044,17409,15161,33110,33113,33114,17427,22586,33148,33156,17445,33171,17453,33189,22511,33217,33252,33364,17551,33446,33398,33482,33496,33535,17584,33623,38505,27018,33797,28917,33892,24803,33928,17668,33982,34017,34040,34064,34104,34130,17723,34159,34160,34272,17783,34418,34450,34482,34543,38469,34699,17926,17943,34990,35071,35108,35143,35217,162151,35369,35384,35476,35508,35921,36052,36082,36124,18328,22623,36291,18413,20206,36410,21976,22356,36465,22005,36528,18487,36558,36578,36580,36589,36594,36791,36801,36810,36812,36915,39364,18605,39136,37395,18718,37416,37464,37483,37553,37550,37567,37603,37611,37619,37620,37629,37699,37764,37805,18757,18769,40639,37911,21249,37917,37933,37950,18794,37972,38009,38189,38306,18855,38388,38451,18917,26528,18980,38720,18997,38834,38850,22100,19172,24808,39097,19225,39153,22596,39182,39193,20916,39196,39223,39234,39261,39266,19312,39365,19357,39484,39695,31363,39785,39809,39901,39921,39924,19565,39968,14191,138178,40265,39994,40702,22096,40339,40381,40384,40444,38134,36790,40571,40620,40625,40637,40646,38108,40674,40689,40696,31432,40772,131220,131767,132000,26906,38083,22956,132311,22592,38081,14265,132565,132629,132726,136890,22359,29043,133826,133837,134079,21610,194619,134091,21662,134139,134203,134227,134245,134268,24807,134285,22138,134325,134365,134381,134511,134578,134600,26965,39983,34725,134660,134670,134871,135056,134957,134771,23584,135100,24075,135260,135247,135286,26398,135291,135304,135318,13895,135359,135379,135471,135483,21348,33965,135907,136053,135990,35713,136567,136729,137155,137159,20088,28859,137261,137578,137773,137797,138282,138352,138412,138952,25283,138965,139029,29080,26709,139333,27113,14024,139900,140247,140282,141098,141425,141647,33533,141671,141715,142037,35237,142056,36768,142094,38840,142143,38983,39613,142412,null,142472,142519,154600,142600,142610,142775,142741,142914,143220,143308,143411,143462,144159,144350,24497,26184,26303,162425,144743,144883,29185,149946,30679,144922,145174,32391,131910,22709,26382,26904,146087,161367,155618,146961,147129,161278,139418,18640,19128,147737,166554,148206,148237,147515,148276,148374,150085,132554,20946,132625,22943,138920,15294,146687,148484,148694,22408,149108,14747,149295,165352,170441,14178,139715,35678,166734,39382,149522,149755,150037,29193,150208,134264,22885,151205,151430,132985,36570,151596,21135,22335,29041,152217,152601,147274,150183,21948,152646,152686,158546,37332,13427,152895,161330,152926,18200,152930,152934,153543,149823,153693,20582,13563,144332,24798,153859,18300,166216,154286,154505,154630,138640,22433,29009,28598,155906,162834,36950,156082,151450,35682,156674,156746,23899,158711,36662,156804,137500,35562,150006,156808,147439,156946,19392,157119,157365,141083,37989,153569,24981,23079,194765,20411,22201,148769,157436,20074,149812,38486,28047,158909,13848,35191,157593,157806,156689,157790,29151,157895,31554,168128,133649,157990,37124,158009,31301,40432,158202,39462,158253,13919,156777,131105,31107,158260,158555,23852,144665,33743,158621,18128,158884,30011,34917,159150,22710,14108,140685,159819,160205,15444,160384,160389,37505,139642,160395,37680,160486,149968,27705,38047,160848,134904,34855,35061,141606,164979,137137,28344,150058,137248,14756,14009,23568,31203,17727,26294,171181,170148,35139,161740,161880,22230,16607,136714,14753,145199,164072,136133,29101,33638,162269,168360,23143,19639,159919,166315,162301,162314,162571,163174,147834,31555,31102,163849,28597,172767,27139,164632,21410,159239,37823,26678,38749,164207,163875,158133,136173,143919,163912,23941,166960,163971,22293,38947,166217,23979,149896,26046,27093,21458,150181,147329,15377,26422,163984,164084,164142,139169,164175,164233,164271,164378,164614,164655,164746,13770,164968,165546,18682,25574,166230,30728,37461,166328,17394,166375,17375,166376,166726,166868,23032,166921,36619,167877,168172,31569,168208,168252,15863,168286,150218,36816,29327,22155,169191,169449,169392,169400,169778,170193,170313,170346,170435,170536,170766,171354,171419,32415,171768,171811,19620,38215,172691,29090,172799,19857,36882,173515,19868,134300,36798,21953,36794,140464,36793,150163,17673,32383,28502,27313,20202,13540,166700,161949,14138,36480,137205,163876,166764,166809,162366,157359,15851,161365,146615,153141,153942,20122,155265,156248,22207,134765,36366,23405,147080,150686,25566,25296,137206,137339,25904,22061,154698,21530,152337,15814,171416,19581,22050,22046,32585,155352,22901,146752,34672,19996,135146,134473,145082,33047,40286,36120,30267,40005,30286,30649,37701,21554,33096,33527,22053,33074,33816,32957,21994,31074,22083,21526,134813,13774,22021,22001,26353,164578,13869,30004,22000,21946,21655,21874,134209,134294,24272,151880,134774,142434,134818,40619,32090,21982,135285,25245,38765,21652,36045,29174,37238,25596,25529,25598,21865,142147,40050,143027,20890,13535,134567,20903,21581,21790,21779,30310,36397,157834,30129,32950,34820,34694,35015,33206,33820,135361,17644,29444,149254,23440,33547,157843,22139,141044,163119,147875,163187,159440,160438,37232,135641,37384,146684,173737,134828,134905,29286,138402,18254,151490,163833,135147,16634,40029,25887,142752,18675,149472,171388,135148,134666,24674,161187,135149,null,155720,135559,29091,32398,40272,19994,19972,13687,23309,27826,21351,13996,14812,21373,13989,149016,22682,150382,33325,21579,22442,154261,133497,null,14930,140389,29556,171692,19721,39917,146686,171824,19547,151465,169374,171998,33884,146870,160434,157619,145184,25390,32037,147191,146988,14890,36872,21196,15988,13946,17897,132238,30272,23280,134838,30842,163630,22695,16575,22140,39819,23924,30292,173108,40581,19681,30201,14331,24857,143578,148466,null,22109,135849,22439,149859,171526,21044,159918,13741,27722,40316,31830,39737,22494,137068,23635,25811,169168,156469,160100,34477,134440,159010,150242,134513,null,20990,139023,23950,38659,138705,40577,36940,31519,39682,23761,31651,25192,25397,39679,31695,39722,31870,39726,31810,31878,39957,31740,39689,40727,39963,149822,40794,21875,23491,20477,40600,20466,21088,15878,21201,22375,20566,22967,24082,38856,40363,36700,21609,38836,39232,38842,21292,24880,26924,21466,39946,40194,19515,38465,27008,20646,30022,137069,39386,21107,null,37209,38529,37212,null,37201,167575,25471,159011,27338,22033,37262,30074,25221,132092,29519,31856,154657,146685,null,149785,30422,39837,20010,134356,33726,34882,null,23626,27072,20717,22394,21023,24053,20174,27697,131570,20281,21660,21722,21146,36226,13822,24332,13811,null,27474,37244,40869,39831,38958,39092,39610,40616,40580,29050,31508,null,27642,34840,32632,null,22048,173642,36471,40787,null,36308,36431,40476,36353,25218,164733,36392,36469,31443,150135,31294,30936,27882,35431,30215,166490,40742,27854,34774,30147,172722,30803,194624,36108,29410,29553,35629,29442,29937,36075,150203,34351,24506,34976,17591,null,137275,159237,null,35454,140571,null,24829,30311,39639,40260,37742,39823,34805,null,34831,36087,29484,38689,39856,13782,29362,19463,31825,39242,155993,24921,19460,40598,24957,null,22367,24943,25254,25145,25294,14940,25058,21418,144373,25444,26626,13778,23895,166850,36826,167481,null,20697,138566,30982,21298,38456,134971,16485,null,30718,null,31938,155418,31962,31277,32870,32867,32077,29957,29938,35220,33306,26380,32866,160902,32859,29936,33027,30500,35209,157644,30035,159441,34729,34766,33224,34700,35401,36013,35651,30507,29944,34010,13877,27058,36262,null,35241,29800,28089,34753,147473,29927,15835,29046,24740,24988,15569,29026,24695,null,32625,166701,29264,24809,19326,21024,15384,146631,155351,161366,152881,137540,135934,170243,159196,159917,23745,156077,166415,145015,131310,157766,151310,17762,23327,156492,40784,40614,156267,12288,65292,12289,12290,65294,8231,65307,65306,65311,65281,65072,8230,8229,65104,65105,65106,183,65108,65109,65110,65111,65372,8211,65073,8212,65075,9588,65076,65103,65288,65289,65077,65078,65371,65373,65079,65080,12308,12309,65081,65082,12304,12305,65083,65084,12298,12299,65085,65086,12296,12297,65087,65088,12300,12301,65089,65090,12302,12303,65091,65092,65113,65114,65115,65116,65117,65118,8216,8217,8220,8221,12317,12318,8245,8242,65283,65286,65290,8251,167,12291,9675,9679,9651,9650,9678,9734,9733,9671,9670,9633,9632,9661,9660,12963,8453,175,65507,65343,717,65097,65098,65101,65102,65099,65100,65119,65120,65121,65291,65293,215,247,177,8730,65308,65310,65309,8806,8807,8800,8734,8786,8801,65122,65123,65124,65125,65126,65374,8745,8746,8869,8736,8735,8895,13266,13265,8747,8750,8757,8756,9792,9794,8853,8857,8593,8595,8592,8594,8598,8599,8601,8600,8741,8739,65295,65340,8725,65128,65284,65509,12306,65504,65505,65285,65312,8451,8457,65129,65130,65131,13269,13212,13213,13214,13262,13217,13198,13199,13252,176,20825,20827,20830,20829,20833,20835,21991,29929,31950,9601,9602,9603,9604,9605,9606,9607,9608,9615,9614,9613,9612,9611,9610,9609,9532,9524,9516,9508,9500,9620,9472,9474,9621,9484,9488,9492,9496,9581,9582,9584,9583,9552,9566,9578,9569,9698,9699,9701,9700,9585,9586,9587,65296,65297,65298,65299,65300,65301,65302,65303,65304,65305,8544,8545,8546,8547,8548,8549,8550,8551,8552,8553,12321,12322,12323,12324,12325,12326,12327,12328,12329,21313,21316,21317,65313,65314,65315,65316,65317,65318,65319,65320,65321,65322,65323,65324,65325,65326,65327,65328,65329,65330,65331,65332,65333,65334,65335,65336,65337,65338,65345,65346,65347,65348,65349,65350,65351,65352,65353,65354,65355,65356,65357,65358,65359,65360,65361,65362,65363,65364,65365,65366,65367,65368,65369,65370,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,931,932,933,934,935,936,937,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,963,964,965,966,967,968,969,12549,12550,12551,12552,12553,12554,12555,12556,12557,12558,12559,12560,12561,12562,12563,12564,12565,12566,12567,12568,12569,12570,12571,12572,12573,12574,12575,12576,12577,12578,12579,12580,12581,12582,12583,12584,12585,729,713,714,711,715,9216,9217,9218,9219,9220,9221,9222,9223,9224,9225,9226,9227,9228,9229,9230,9231,9232,9233,9234,9235,9236,9237,9238,9239,9240,9241,9242,9243,9244,9245,9246,9247,9249,8364,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,19968,20057,19969,19971,20035,20061,20102,20108,20154,20799,20837,20843,20960,20992,20993,21147,21269,21313,21340,21448,19977,19979,19976,19978,20011,20024,20961,20037,20040,20063,20062,20110,20129,20800,20995,21242,21315,21449,21475,22303,22763,22805,22823,22899,23376,23377,23379,23544,23567,23586,23608,23665,24029,24037,24049,24050,24051,24062,24178,24318,24331,24339,25165,19985,19984,19981,20013,20016,20025,20043,23609,20104,20113,20117,20114,20116,20130,20161,20160,20163,20166,20167,20173,20170,20171,20164,20803,20801,20839,20845,20846,20844,20887,20982,20998,20999,21000,21243,21246,21247,21270,21305,21320,21319,21317,21342,21380,21451,21450,21453,22764,22825,22827,22826,22829,23380,23569,23588,23610,23663,24052,24187,24319,24340,24341,24515,25096,25142,25163,25166,25903,25991,26007,26020,26041,26085,26352,26376,26408,27424,27490,27513,27595,27604,27611,27663,27700,28779,29226,29238,29243,29255,29273,29275,29356,29579,19993,19990,19989,19988,19992,20027,20045,20047,20046,20197,20184,20180,20181,20182,20183,20195,20196,20185,20190,20805,20804,20873,20874,20908,20985,20986,20984,21002,21152,21151,21253,21254,21271,21277,20191,21322,21321,21345,21344,21359,21358,21435,21487,21476,21491,21484,21486,21481,21480,21500,21496,21493,21483,21478,21482,21490,21489,21488,21477,21485,21499,22235,22234,22806,22830,22833,22900,22902,23381,23427,23612,24040,24039,24038,24066,24067,24179,24188,24321,24344,24343,24517,25098,25171,25172,25170,25169,26021,26086,26414,26412,26410,26411,26413,27491,27597,27665,27664,27704,27713,27712,27710,29359,29572,29577,29916,29926,29976,29983,29992,29993,30000,30001,30002,30003,30091,30333,30382,30399,30446,30683,30690,30707,31034,31166,31348,31435,19998,19999,20050,20051,20073,20121,20132,20134,20133,20223,20233,20249,20234,20245,20237,20240,20241,20239,20210,20214,20219,20208,20211,20221,20225,20235,20809,20807,20806,20808,20840,20849,20877,20912,21015,21009,21010,21006,21014,21155,21256,21281,21280,21360,21361,21513,21519,21516,21514,21520,21505,21515,21508,21521,21517,21512,21507,21518,21510,21522,22240,22238,22237,22323,22320,22312,22317,22316,22319,22313,22809,22810,22839,22840,22916,22904,22915,22909,22905,22914,22913,23383,23384,23431,23432,23429,23433,23546,23574,23673,24030,24070,24182,24180,24335,24347,24537,24534,25102,25100,25101,25104,25187,25179,25176,25910,26089,26088,26092,26093,26354,26355,26377,26429,26420,26417,26421,27425,27492,27515,27670,27741,27735,27737,27743,27744,27728,27733,27745,27739,27725,27726,28784,29279,29277,30334,31481,31859,31992,32566,32650,32701,32769,32771,32780,32786,32819,32895,32905,32907,32908,33251,33258,33267,33276,33292,33307,33311,33390,33394,33406,34411,34880,34892,34915,35199,38433,20018,20136,20301,20303,20295,20311,20318,20276,20315,20309,20272,20304,20305,20285,20282,20280,20291,20308,20284,20294,20323,20316,20320,20271,20302,20278,20313,20317,20296,20314,20812,20811,20813,20853,20918,20919,21029,21028,21033,21034,21032,21163,21161,21162,21164,21283,21363,21365,21533,21549,21534,21566,21542,21582,21543,21574,21571,21555,21576,21570,21531,21545,21578,21561,21563,21560,21550,21557,21558,21536,21564,21568,21553,21547,21535,21548,22250,22256,22244,22251,22346,22353,22336,22349,22343,22350,22334,22352,22351,22331,22767,22846,22941,22930,22952,22942,22947,22937,22934,22925,22948,22931,22922,22949,23389,23388,23386,23387,23436,23435,23439,23596,23616,23617,23615,23614,23696,23697,23700,23692,24043,24076,24207,24199,24202,24311,24324,24351,24420,24418,24439,24441,24536,24524,24535,24525,24561,24555,24568,24554,25106,25105,25220,25239,25238,25216,25206,25225,25197,25226,25212,25214,25209,25203,25234,25199,25240,25198,25237,25235,25233,25222,25913,25915,25912,26097,26356,26463,26446,26447,26448,26449,26460,26454,26462,26441,26438,26464,26451,26455,27493,27599,27714,27742,27801,27777,27784,27785,27781,27803,27754,27770,27792,27760,27788,27752,27798,27794,27773,27779,27762,27774,27764,27782,27766,27789,27796,27800,27778,28790,28796,28797,28792,29282,29281,29280,29380,29378,29590,29996,29995,30007,30008,30338,30447,30691,31169,31168,31167,31350,31995,32597,32918,32915,32925,32920,32923,32922,32946,33391,33426,33419,33421,35211,35282,35328,35895,35910,35925,35997,36196,36208,36275,36523,36554,36763,36784,36802,36806,36805,36804,24033,37009,37026,37034,37030,37027,37193,37318,37324,38450,38446,38449,38442,38444,20006,20054,20083,20107,20123,20126,20139,20140,20335,20381,20365,20339,20351,20332,20379,20363,20358,20355,20336,20341,20360,20329,20347,20374,20350,20367,20369,20346,20820,20818,20821,20841,20855,20854,20856,20925,20989,21051,21048,21047,21050,21040,21038,21046,21057,21182,21179,21330,21332,21331,21329,21350,21367,21368,21369,21462,21460,21463,21619,21621,21654,21624,21653,21632,21627,21623,21636,21650,21638,21628,21648,21617,21622,21644,21658,21602,21608,21643,21629,21646,22266,22403,22391,22378,22377,22369,22374,22372,22396,22812,22857,22855,22856,22852,22868,22974,22971,22996,22969,22958,22993,22982,22992,22989,22987,22995,22986,22959,22963,22994,22981,23391,23396,23395,23447,23450,23448,23452,23449,23451,23578,23624,23621,23622,23735,23713,23736,23721,23723,23729,23731,24088,24090,24086,24085,24091,24081,24184,24218,24215,24220,24213,24214,24310,24358,24359,24361,24448,24449,24447,24444,24541,24544,24573,24565,24575,24591,24596,24623,24629,24598,24618,24597,24609,24615,24617,24619,24603,25110,25109,25151,25150,25152,25215,25289,25292,25284,25279,25282,25273,25298,25307,25259,25299,25300,25291,25288,25256,25277,25276,25296,25305,25287,25293,25269,25306,25265,25304,25302,25303,25286,25260,25294,25918,26023,26044,26106,26132,26131,26124,26118,26114,26126,26112,26127,26133,26122,26119,26381,26379,26477,26507,26517,26481,26524,26483,26487,26503,26525,26519,26479,26480,26495,26505,26494,26512,26485,26522,26515,26492,26474,26482,27427,27494,27495,27519,27667,27675,27875,27880,27891,27825,27852,27877,27827,27837,27838,27836,27874,27819,27861,27859,27832,27844,27833,27841,27822,27863,27845,27889,27839,27835,27873,27867,27850,27820,27887,27868,27862,27872,28821,28814,28818,28810,28825,29228,29229,29240,29256,29287,29289,29376,29390,29401,29399,29392,29609,29608,29599,29611,29605,30013,30109,30105,30106,30340,30402,30450,30452,30693,30717,31038,31040,31041,31177,31176,31354,31353,31482,31998,32596,32652,32651,32773,32954,32933,32930,32945,32929,32939,32937,32948,32938,32943,33253,33278,33293,33459,33437,33433,33453,33469,33439,33465,33457,33452,33445,33455,33464,33443,33456,33470,33463,34382,34417,21021,34920,36555,36814,36820,36817,37045,37048,37041,37046,37319,37329,38263,38272,38428,38464,38463,38459,38468,38466,38585,38632,38738,38750,20127,20141,20142,20449,20405,20399,20415,20448,20433,20431,20445,20419,20406,20440,20447,20426,20439,20398,20432,20420,20418,20442,20430,20446,20407,20823,20882,20881,20896,21070,21059,21066,21069,21068,21067,21063,21191,21193,21187,21185,21261,21335,21371,21402,21467,21676,21696,21672,21710,21705,21688,21670,21683,21703,21698,21693,21674,21697,21700,21704,21679,21675,21681,21691,21673,21671,21695,22271,22402,22411,22432,22435,22434,22478,22446,22419,22869,22865,22863,22862,22864,23004,23000,23039,23011,23016,23043,23013,23018,23002,23014,23041,23035,23401,23459,23462,23460,23458,23461,23553,23630,23631,23629,23627,23769,23762,24055,24093,24101,24095,24189,24224,24230,24314,24328,24365,24421,24456,24453,24458,24459,24455,24460,24457,24594,24605,24608,24613,24590,24616,24653,24688,24680,24674,24646,24643,24684,24683,24682,24676,25153,25308,25366,25353,25340,25325,25345,25326,25341,25351,25329,25335,25327,25324,25342,25332,25361,25346,25919,25925,26027,26045,26082,26149,26157,26144,26151,26159,26143,26152,26161,26148,26359,26623,26579,26609,26580,26576,26604,26550,26543,26613,26601,26607,26564,26577,26548,26586,26597,26552,26575,26590,26611,26544,26585,26594,26589,26578,27498,27523,27526,27573,27602,27607,27679,27849,27915,27954,27946,27969,27941,27916,27953,27934,27927,27963,27965,27966,27958,27931,27893,27961,27943,27960,27945,27950,27957,27918,27947,28843,28858,28851,28844,28847,28845,28856,28846,28836,29232,29298,29295,29300,29417,29408,29409,29623,29642,29627,29618,29645,29632,29619,29978,29997,30031,30028,30030,30027,30123,30116,30117,30114,30115,30328,30342,30343,30344,30408,30406,30403,30405,30465,30457,30456,30473,30475,30462,30460,30471,30684,30722,30740,30732,30733,31046,31049,31048,31047,31161,31162,31185,31186,31179,31359,31361,31487,31485,31869,32002,32005,32000,32009,32007,32004,32006,32568,32654,32703,32772,32784,32781,32785,32822,32982,32997,32986,32963,32964,32972,32993,32987,32974,32990,32996,32989,33268,33314,33511,33539,33541,33507,33499,33510,33540,33509,33538,33545,33490,33495,33521,33537,33500,33492,33489,33502,33491,33503,33519,33542,34384,34425,34427,34426,34893,34923,35201,35284,35336,35330,35331,35998,36000,36212,36211,36276,36557,36556,36848,36838,36834,36842,36837,36845,36843,36836,36840,37066,37070,37057,37059,37195,37194,37325,38274,38480,38475,38476,38477,38754,38761,38859,38893,38899,38913,39080,39131,39135,39318,39321,20056,20147,20492,20493,20515,20463,20518,20517,20472,20521,20502,20486,20540,20511,20506,20498,20497,20474,20480,20500,20520,20465,20513,20491,20505,20504,20467,20462,20525,20522,20478,20523,20489,20860,20900,20901,20898,20941,20940,20934,20939,21078,21084,21076,21083,21085,21290,21375,21407,21405,21471,21736,21776,21761,21815,21756,21733,21746,21766,21754,21780,21737,21741,21729,21769,21742,21738,21734,21799,21767,21757,21775,22275,22276,22466,22484,22475,22467,22537,22799,22871,22872,22874,23057,23064,23068,23071,23067,23059,23020,23072,23075,23081,23077,23052,23049,23403,23640,23472,23475,23478,23476,23470,23477,23481,23480,23556,23633,23637,23632,23789,23805,23803,23786,23784,23792,23798,23809,23796,24046,24109,24107,24235,24237,24231,24369,24466,24465,24464,24665,24675,24677,24656,24661,24685,24681,24687,24708,24735,24730,24717,24724,24716,24709,24726,25159,25331,25352,25343,25422,25406,25391,25429,25410,25414,25423,25417,25402,25424,25405,25386,25387,25384,25421,25420,25928,25929,26009,26049,26053,26178,26185,26191,26179,26194,26188,26181,26177,26360,26388,26389,26391,26657,26680,26696,26694,26707,26681,26690,26708,26665,26803,26647,26700,26705,26685,26612,26704,26688,26684,26691,26666,26693,26643,26648,26689,27530,27529,27575,27683,27687,27688,27686,27684,27888,28010,28053,28040,28039,28006,28024,28023,27993,28051,28012,28041,28014,27994,28020,28009,28044,28042,28025,28037,28005,28052,28874,28888,28900,28889,28872,28879,29241,29305,29436,29433,29437,29432,29431,29574,29677,29705,29678,29664,29674,29662,30036,30045,30044,30042,30041,30142,30149,30151,30130,30131,30141,30140,30137,30146,30136,30347,30384,30410,30413,30414,30505,30495,30496,30504,30697,30768,30759,30776,30749,30772,30775,30757,30765,30752,30751,30770,31061,31056,31072,31071,31062,31070,31069,31063,31066,31204,31203,31207,31199,31206,31209,31192,31364,31368,31449,31494,31505,31881,32033,32023,32011,32010,32032,32034,32020,32016,32021,32026,32028,32013,32025,32027,32570,32607,32660,32709,32705,32774,32792,32789,32793,32791,32829,32831,33009,33026,33008,33029,33005,33012,33030,33016,33011,33032,33021,33034,33020,33007,33261,33260,33280,33296,33322,33323,33320,33324,33467,33579,33618,33620,33610,33592,33616,33609,33589,33588,33615,33586,33593,33590,33559,33600,33585,33576,33603,34388,34442,34474,34451,34468,34473,34444,34467,34460,34928,34935,34945,34946,34941,34937,35352,35344,35342,35340,35349,35338,35351,35347,35350,35343,35345,35912,35962,35961,36001,36002,36215,36524,36562,36564,36559,36785,36865,36870,36855,36864,36858,36852,36867,36861,36869,36856,37013,37089,37085,37090,37202,37197,37196,37336,37341,37335,37340,37337,38275,38498,38499,38497,38491,38493,38500,38488,38494,38587,39138,39340,39592,39640,39717,39730,39740,20094,20602,20605,20572,20551,20547,20556,20570,20553,20581,20598,20558,20565,20597,20596,20599,20559,20495,20591,20589,20828,20885,20976,21098,21103,21202,21209,21208,21205,21264,21263,21273,21311,21312,21310,21443,26364,21830,21866,21862,21828,21854,21857,21827,21834,21809,21846,21839,21845,21807,21860,21816,21806,21852,21804,21859,21811,21825,21847,22280,22283,22281,22495,22533,22538,22534,22496,22500,22522,22530,22581,22519,22521,22816,22882,23094,23105,23113,23142,23146,23104,23100,23138,23130,23110,23114,23408,23495,23493,23492,23490,23487,23494,23561,23560,23559,23648,23644,23645,23815,23814,23822,23835,23830,23842,23825,23849,23828,23833,23844,23847,23831,24034,24120,24118,24115,24119,24247,24248,24246,24245,24254,24373,24375,24407,24428,24425,24427,24471,24473,24478,24472,24481,24480,24476,24703,24739,24713,24736,24744,24779,24756,24806,24765,24773,24763,24757,24796,24764,24792,24789,24774,24799,24760,24794,24775,25114,25115,25160,25504,25511,25458,25494,25506,25509,25463,25447,25496,25514,25457,25513,25481,25475,25499,25451,25512,25476,25480,25497,25505,25516,25490,25487,25472,25467,25449,25448,25466,25949,25942,25937,25945,25943,21855,25935,25944,25941,25940,26012,26011,26028,26063,26059,26060,26062,26205,26202,26212,26216,26214,26206,26361,21207,26395,26753,26799,26786,26771,26805,26751,26742,26801,26791,26775,26800,26755,26820,26797,26758,26757,26772,26781,26792,26783,26785,26754,27442,27578,27627,27628,27691,28046,28092,28147,28121,28082,28129,28108,28132,28155,28154,28165,28103,28107,28079,28113,28078,28126,28153,28088,28151,28149,28101,28114,28186,28085,28122,28139,28120,28138,28145,28142,28136,28102,28100,28074,28140,28095,28134,28921,28937,28938,28925,28911,29245,29309,29313,29468,29467,29462,29459,29465,29575,29701,29706,29699,29702,29694,29709,29920,29942,29943,29980,29986,30053,30054,30050,30064,30095,30164,30165,30133,30154,30157,30350,30420,30418,30427,30519,30526,30524,30518,30520,30522,30827,30787,30798,31077,31080,31085,31227,31378,31381,31520,31528,31515,31532,31526,31513,31518,31534,31890,31895,31893,32070,32067,32113,32046,32057,32060,32064,32048,32051,32068,32047,32066,32050,32049,32573,32670,32666,32716,32718,32722,32796,32842,32838,33071,33046,33059,33067,33065,33072,33060,33282,33333,33335,33334,33337,33678,33694,33688,33656,33698,33686,33725,33707,33682,33674,33683,33673,33696,33655,33659,33660,33670,33703,34389,24426,34503,34496,34486,34500,34485,34502,34507,34481,34479,34505,34899,34974,34952,34987,34962,34966,34957,34955,35219,35215,35370,35357,35363,35365,35377,35373,35359,35355,35362,35913,35930,36009,36012,36011,36008,36010,36007,36199,36198,36286,36282,36571,36575,36889,36877,36890,36887,36899,36895,36893,36880,36885,36894,36896,36879,36898,36886,36891,36884,37096,37101,37117,37207,37326,37365,37350,37347,37351,37357,37353,38281,38506,38517,38515,38520,38512,38516,38518,38519,38508,38592,38634,38633,31456,31455,38914,38915,39770,40165,40565,40575,40613,40635,20642,20621,20613,20633,20625,20608,20630,20632,20634,26368,20977,21106,21108,21109,21097,21214,21213,21211,21338,21413,21883,21888,21927,21884,21898,21917,21912,21890,21916,21930,21908,21895,21899,21891,21939,21934,21919,21822,21938,21914,21947,21932,21937,21886,21897,21931,21913,22285,22575,22570,22580,22564,22576,22577,22561,22557,22560,22777,22778,22880,23159,23194,23167,23186,23195,23207,23411,23409,23506,23500,23507,23504,23562,23563,23601,23884,23888,23860,23879,24061,24133,24125,24128,24131,24190,24266,24257,24258,24260,24380,24429,24489,24490,24488,24785,24801,24754,24758,24800,24860,24867,24826,24853,24816,24827,24820,24936,24817,24846,24822,24841,24832,24850,25119,25161,25507,25484,25551,25536,25577,25545,25542,25549,25554,25571,25552,25569,25558,25581,25582,25462,25588,25578,25563,25682,25562,25593,25950,25958,25954,25955,26001,26000,26031,26222,26224,26228,26230,26223,26257,26234,26238,26231,26366,26367,26399,26397,26874,26837,26848,26840,26839,26885,26847,26869,26862,26855,26873,26834,26866,26851,26827,26829,26893,26898,26894,26825,26842,26990,26875,27454,27450,27453,27544,27542,27580,27631,27694,27695,27692,28207,28216,28244,28193,28210,28263,28234,28192,28197,28195,28187,28251,28248,28196,28246,28270,28205,28198,28271,28212,28237,28218,28204,28227,28189,28222,28363,28297,28185,28238,28259,28228,28274,28265,28255,28953,28954,28966,28976,28961,28982,29038,28956,29260,29316,29312,29494,29477,29492,29481,29754,29738,29747,29730,29733,29749,29750,29748,29743,29723,29734,29736,29989,29990,30059,30058,30178,30171,30179,30169,30168,30174,30176,30331,30332,30358,30355,30388,30428,30543,30701,30813,30828,30831,31245,31240,31243,31237,31232,31384,31383,31382,31461,31459,31561,31574,31558,31568,31570,31572,31565,31563,31567,31569,31903,31909,32094,32080,32104,32085,32043,32110,32114,32097,32102,32098,32112,32115,21892,32724,32725,32779,32850,32901,33109,33108,33099,33105,33102,33081,33094,33086,33100,33107,33140,33298,33308,33769,33795,33784,33805,33760,33733,33803,33729,33775,33777,33780,33879,33802,33776,33804,33740,33789,33778,33738,33848,33806,33796,33756,33799,33748,33759,34395,34527,34521,34541,34516,34523,34532,34512,34526,34903,35009,35010,34993,35203,35222,35387,35424,35413,35422,35388,35393,35412,35419,35408,35398,35380,35386,35382,35414,35937,35970,36015,36028,36019,36029,36033,36027,36032,36020,36023,36022,36031,36024,36234,36229,36225,36302,36317,36299,36314,36305,36300,36315,36294,36603,36600,36604,36764,36910,36917,36913,36920,36914,36918,37122,37109,37129,37118,37219,37221,37327,37396,37397,37411,37385,37406,37389,37392,37383,37393,38292,38287,38283,38289,38291,38290,38286,38538,38542,38539,38525,38533,38534,38541,38514,38532,38593,38597,38596,38598,38599,38639,38642,38860,38917,38918,38920,39143,39146,39151,39145,39154,39149,39342,39341,40643,40653,40657,20098,20653,20661,20658,20659,20677,20670,20652,20663,20667,20655,20679,21119,21111,21117,21215,21222,21220,21218,21219,21295,21983,21992,21971,21990,21966,21980,21959,21969,21987,21988,21999,21978,21985,21957,21958,21989,21961,22290,22291,22622,22609,22616,22615,22618,22612,22635,22604,22637,22602,22626,22610,22603,22887,23233,23241,23244,23230,23229,23228,23219,23234,23218,23913,23919,24140,24185,24265,24264,24338,24409,24492,24494,24858,24847,24904,24863,24819,24859,24825,24833,24840,24910,24908,24900,24909,24894,24884,24871,24845,24838,24887,25121,25122,25619,25662,25630,25642,25645,25661,25644,25615,25628,25620,25613,25654,25622,25623,25606,25964,26015,26032,26263,26249,26247,26248,26262,26244,26264,26253,26371,27028,26989,26970,26999,26976,26964,26997,26928,27010,26954,26984,26987,26974,26963,27001,27014,26973,26979,26971,27463,27506,27584,27583,27603,27645,28322,28335,28371,28342,28354,28304,28317,28359,28357,28325,28312,28348,28346,28331,28369,28310,28316,28356,28372,28330,28327,28340,29006,29017,29033,29028,29001,29031,29020,29036,29030,29004,29029,29022,28998,29032,29014,29242,29266,29495,29509,29503,29502,29807,29786,29781,29791,29790,29761,29759,29785,29787,29788,30070,30072,30208,30192,30209,30194,30193,30202,30207,30196,30195,30430,30431,30555,30571,30566,30558,30563,30585,30570,30572,30556,30565,30568,30562,30702,30862,30896,30871,30872,30860,30857,30844,30865,30867,30847,31098,31103,31105,33836,31165,31260,31258,31264,31252,31263,31262,31391,31392,31607,31680,31584,31598,31591,31921,31923,31925,32147,32121,32145,32129,32143,32091,32622,32617,32618,32626,32681,32680,32676,32854,32856,32902,32900,33137,33136,33144,33125,33134,33139,33131,33145,33146,33126,33285,33351,33922,33911,33853,33841,33909,33894,33899,33865,33900,33883,33852,33845,33889,33891,33897,33901,33862,34398,34396,34399,34553,34579,34568,34567,34560,34558,34555,34562,34563,34566,34570,34905,35039,35028,35033,35036,35032,35037,35041,35018,35029,35026,35228,35299,35435,35442,35443,35430,35433,35440,35463,35452,35427,35488,35441,35461,35437,35426,35438,35436,35449,35451,35390,35432,35938,35978,35977,36042,36039,36040,36036,36018,36035,36034,36037,36321,36319,36328,36335,36339,36346,36330,36324,36326,36530,36611,36617,36606,36618,36767,36786,36939,36938,36947,36930,36948,36924,36949,36944,36935,36943,36942,36941,36945,36926,36929,37138,37143,37228,37226,37225,37321,37431,37463,37432,37437,37440,37438,37467,37451,37476,37457,37428,37449,37453,37445,37433,37439,37466,38296,38552,38548,38549,38605,38603,38601,38602,38647,38651,38649,38646,38742,38772,38774,38928,38929,38931,38922,38930,38924,39164,39156,39165,39166,39347,39345,39348,39649,40169,40578,40718,40723,40736,20711,20718,20709,20694,20717,20698,20693,20687,20689,20721,20686,20713,20834,20979,21123,21122,21297,21421,22014,22016,22043,22039,22013,22036,22022,22025,22029,22030,22007,22038,22047,22024,22032,22006,22296,22294,22645,22654,22659,22675,22666,22649,22661,22653,22781,22821,22818,22820,22890,22889,23265,23270,23273,23255,23254,23256,23267,23413,23518,23527,23521,23525,23526,23528,23522,23524,23519,23565,23650,23940,23943,24155,24163,24149,24151,24148,24275,24278,24330,24390,24432,24505,24903,24895,24907,24951,24930,24931,24927,24922,24920,24949,25130,25735,25688,25684,25764,25720,25695,25722,25681,25703,25652,25709,25723,25970,26017,26071,26070,26274,26280,26269,27036,27048,27029,27073,27054,27091,27083,27035,27063,27067,27051,27060,27088,27085,27053,27084,27046,27075,27043,27465,27468,27699,28467,28436,28414,28435,28404,28457,28478,28448,28460,28431,28418,28450,28415,28399,28422,28465,28472,28466,28451,28437,28459,28463,28552,28458,28396,28417,28402,28364,28407,29076,29081,29053,29066,29060,29074,29246,29330,29334,29508,29520,29796,29795,29802,29808,29805,29956,30097,30247,30221,30219,30217,30227,30433,30435,30596,30589,30591,30561,30913,30879,30887,30899,30889,30883,31118,31119,31117,31278,31281,31402,31401,31469,31471,31649,31637,31627,31605,31639,31645,31636,31631,31672,31623,31620,31929,31933,31934,32187,32176,32156,32189,32190,32160,32202,32180,32178,32177,32186,32162,32191,32181,32184,32173,32210,32199,32172,32624,32736,32737,32735,32862,32858,32903,33104,33152,33167,33160,33162,33151,33154,33255,33274,33287,33300,33310,33355,33993,33983,33990,33988,33945,33950,33970,33948,33995,33976,33984,34003,33936,33980,34001,33994,34623,34588,34619,34594,34597,34612,34584,34645,34615,34601,35059,35074,35060,35065,35064,35069,35048,35098,35055,35494,35468,35486,35491,35469,35489,35475,35492,35498,35493,35496,35480,35473,35482,35495,35946,35981,35980,36051,36049,36050,36203,36249,36245,36348,36628,36626,36629,36627,36771,36960,36952,36956,36963,36953,36958,36962,36957,36955,37145,37144,37150,37237,37240,37239,37236,37496,37504,37509,37528,37526,37499,37523,37532,37544,37500,37521,38305,38312,38313,38307,38309,38308,38553,38556,38555,38604,38610,38656,38780,38789,38902,38935,38936,39087,39089,39171,39173,39180,39177,39361,39599,39600,39654,39745,39746,40180,40182,40179,40636,40763,40778,20740,20736,20731,20725,20729,20738,20744,20745,20741,20956,21127,21128,21129,21133,21130,21232,21426,22062,22075,22073,22066,22079,22068,22057,22099,22094,22103,22132,22070,22063,22064,22656,22687,22686,22707,22684,22702,22697,22694,22893,23305,23291,23307,23285,23308,23304,23534,23532,23529,23531,23652,23653,23965,23956,24162,24159,24161,24290,24282,24287,24285,24291,24288,24392,24433,24503,24501,24950,24935,24942,24925,24917,24962,24956,24944,24939,24958,24999,24976,25003,24974,25004,24986,24996,24980,25006,25134,25705,25711,25721,25758,25778,25736,25744,25776,25765,25747,25749,25769,25746,25774,25773,25771,25754,25772,25753,25762,25779,25973,25975,25976,26286,26283,26292,26289,27171,27167,27112,27137,27166,27161,27133,27169,27155,27146,27123,27138,27141,27117,27153,27472,27470,27556,27589,27590,28479,28540,28548,28497,28518,28500,28550,28525,28507,28536,28526,28558,28538,28528,28516,28567,28504,28373,28527,28512,28511,29087,29100,29105,29096,29270,29339,29518,29527,29801,29835,29827,29822,29824,30079,30240,30249,30239,30244,30246,30241,30242,30362,30394,30436,30606,30599,30604,30609,30603,30923,30917,30906,30922,30910,30933,30908,30928,31295,31292,31296,31293,31287,31291,31407,31406,31661,31665,31684,31668,31686,31687,31681,31648,31692,31946,32224,32244,32239,32251,32216,32236,32221,32232,32227,32218,32222,32233,32158,32217,32242,32249,32629,32631,32687,32745,32806,33179,33180,33181,33184,33178,33176,34071,34109,34074,34030,34092,34093,34067,34065,34083,34081,34068,34028,34085,34047,34054,34690,34676,34678,34656,34662,34680,34664,34649,34647,34636,34643,34907,34909,35088,35079,35090,35091,35093,35082,35516,35538,35527,35524,35477,35531,35576,35506,35529,35522,35519,35504,35542,35533,35510,35513,35547,35916,35918,35948,36064,36062,36070,36068,36076,36077,36066,36067,36060,36074,36065,36205,36255,36259,36395,36368,36381,36386,36367,36393,36383,36385,36382,36538,36637,36635,36639,36649,36646,36650,36636,36638,36645,36969,36974,36968,36973,36983,37168,37165,37159,37169,37255,37257,37259,37251,37573,37563,37559,37610,37548,37604,37569,37555,37564,37586,37575,37616,37554,38317,38321,38660,38662,38663,38665,38752,38797,38795,38799,38945,38955,38940,39091,39178,39187,39186,39192,39389,39376,39391,39387,39377,39381,39378,39385,39607,39662,39663,39719,39749,39748,39799,39791,40198,40201,40195,40617,40638,40654,22696,40786,20754,20760,20756,20752,20757,20864,20906,20957,21137,21139,21235,22105,22123,22137,22121,22116,22136,22122,22120,22117,22129,22127,22124,22114,22134,22721,22718,22727,22725,22894,23325,23348,23416,23536,23566,24394,25010,24977,25001,24970,25037,25014,25022,25034,25032,25136,25797,25793,25803,25787,25788,25818,25796,25799,25794,25805,25791,25810,25812,25790,25972,26310,26313,26297,26308,26311,26296,27197,27192,27194,27225,27243,27224,27193,27204,27234,27233,27211,27207,27189,27231,27208,27481,27511,27653,28610,28593,28577,28611,28580,28609,28583,28595,28608,28601,28598,28582,28576,28596,29118,29129,29136,29138,29128,29141,29113,29134,29145,29148,29123,29124,29544,29852,29859,29848,29855,29854,29922,29964,29965,30260,30264,30266,30439,30437,30624,30622,30623,30629,30952,30938,30956,30951,31142,31309,31310,31302,31308,31307,31418,31705,31761,31689,31716,31707,31713,31721,31718,31957,31958,32266,32273,32264,32283,32291,32286,32285,32265,32272,32633,32690,32752,32753,32750,32808,33203,33193,33192,33275,33288,33368,33369,34122,34137,34120,34152,34153,34115,34121,34157,34154,34142,34691,34719,34718,34722,34701,34913,35114,35122,35109,35115,35105,35242,35238,35558,35578,35563,35569,35584,35548,35559,35566,35582,35585,35586,35575,35565,35571,35574,35580,35947,35949,35987,36084,36420,36401,36404,36418,36409,36405,36667,36655,36664,36659,36776,36774,36981,36980,36984,36978,36988,36986,37172,37266,37664,37686,37624,37683,37679,37666,37628,37675,37636,37658,37648,37670,37665,37653,37678,37657,38331,38567,38568,38570,38613,38670,38673,38678,38669,38675,38671,38747,38748,38758,38808,38960,38968,38971,38967,38957,38969,38948,39184,39208,39198,39195,39201,39194,39405,39394,39409,39608,39612,39675,39661,39720,39825,40213,40227,40230,40232,40210,40219,40664,40660,40845,40860,20778,20767,20769,20786,21237,22158,22144,22160,22149,22151,22159,22741,22739,22737,22734,23344,23338,23332,23418,23607,23656,23996,23994,23997,23992,24171,24396,24509,25033,25026,25031,25062,25035,25138,25140,25806,25802,25816,25824,25840,25830,25836,25841,25826,25837,25986,25987,26329,26326,27264,27284,27268,27298,27292,27355,27299,27262,27287,27280,27296,27484,27566,27610,27656,28632,28657,28639,28640,28635,28644,28651,28655,28544,28652,28641,28649,28629,28654,28656,29159,29151,29166,29158,29157,29165,29164,29172,29152,29237,29254,29552,29554,29865,29872,29862,29864,30278,30274,30284,30442,30643,30634,30640,30636,30631,30637,30703,30967,30970,30964,30959,30977,31143,31146,31319,31423,31751,31757,31742,31735,31756,31712,31968,31964,31966,31970,31967,31961,31965,32302,32318,32326,32311,32306,32323,32299,32317,32305,32325,32321,32308,32313,32328,32309,32319,32303,32580,32755,32764,32881,32882,32880,32879,32883,33222,33219,33210,33218,33216,33215,33213,33225,33214,33256,33289,33393,34218,34180,34174,34204,34193,34196,34223,34203,34183,34216,34186,34407,34752,34769,34739,34770,34758,34731,34747,34746,34760,34763,35131,35126,35140,35128,35133,35244,35598,35607,35609,35611,35594,35616,35613,35588,35600,35905,35903,35955,36090,36093,36092,36088,36091,36264,36425,36427,36424,36426,36676,36670,36674,36677,36671,36991,36989,36996,36993,36994,36992,37177,37283,37278,37276,37709,37762,37672,37749,37706,37733,37707,37656,37758,37740,37723,37744,37722,37716,38346,38347,38348,38344,38342,38577,38584,38614,38684,38686,38816,38867,38982,39094,39221,39425,39423,39854,39851,39850,39853,40251,40255,40587,40655,40670,40668,40669,40667,40766,40779,21474,22165,22190,22745,22744,23352,24413,25059,25139,25844,25842,25854,25862,25850,25851,25847,26039,26332,26406,27315,27308,27331,27323,27320,27330,27310,27311,27487,27512,27567,28681,28683,28670,28678,28666,28689,28687,29179,29180,29182,29176,29559,29557,29863,29887,29973,30294,30296,30290,30653,30655,30651,30652,30990,31150,31329,31330,31328,31428,31429,31787,31783,31786,31774,31779,31777,31975,32340,32341,32350,32346,32353,32338,32345,32584,32761,32763,32887,32886,33229,33231,33290,34255,34217,34253,34256,34249,34224,34234,34233,34214,34799,34796,34802,34784,35206,35250,35316,35624,35641,35628,35627,35920,36101,36441,36451,36454,36452,36447,36437,36544,36681,36685,36999,36995,37000,37291,37292,37328,37780,37770,37782,37794,37811,37806,37804,37808,37784,37786,37783,38356,38358,38352,38357,38626,38620,38617,38619,38622,38692,38819,38822,38829,38905,38989,38991,38988,38990,38995,39098,39230,39231,39229,39214,39333,39438,39617,39683,39686,39759,39758,39757,39882,39881,39933,39880,39872,40273,40285,40288,40672,40725,40748,20787,22181,22750,22751,22754,23541,40848,24300,25074,25079,25078,25077,25856,25871,26336,26333,27365,27357,27354,27347,28699,28703,28712,28698,28701,28693,28696,29190,29197,29272,29346,29560,29562,29885,29898,29923,30087,30086,30303,30305,30663,31001,31153,31339,31337,31806,31807,31800,31805,31799,31808,32363,32365,32377,32361,32362,32645,32371,32694,32697,32696,33240,34281,34269,34282,34261,34276,34277,34295,34811,34821,34829,34809,34814,35168,35167,35158,35166,35649,35676,35672,35657,35674,35662,35663,35654,35673,36104,36106,36476,36466,36487,36470,36460,36474,36468,36692,36686,36781,37002,37003,37297,37294,37857,37841,37855,37827,37832,37852,37853,37846,37858,37837,37848,37860,37847,37864,38364,38580,38627,38698,38695,38753,38876,38907,39006,39000,39003,39100,39237,39241,39446,39449,39693,39912,39911,39894,39899,40329,40289,40306,40298,40300,40594,40599,40595,40628,21240,22184,22199,22198,22196,22204,22756,23360,23363,23421,23542,24009,25080,25082,25880,25876,25881,26342,26407,27372,28734,28720,28722,29200,29563,29903,30306,30309,31014,31018,31020,31019,31431,31478,31820,31811,31821,31983,31984,36782,32381,32380,32386,32588,32768,33242,33382,34299,34297,34321,34298,34310,34315,34311,34314,34836,34837,35172,35258,35320,35696,35692,35686,35695,35679,35691,36111,36109,36489,36481,36485,36482,37300,37323,37912,37891,37885,38369,38704,39108,39250,39249,39336,39467,39472,39479,39477,39955,39949,40569,40629,40680,40751,40799,40803,40801,20791,20792,22209,22208,22210,22804,23660,24013,25084,25086,25885,25884,26005,26345,27387,27396,27386,27570,28748,29211,29351,29910,29908,30313,30675,31824,32399,32396,32700,34327,34349,34330,34851,34850,34849,34847,35178,35180,35261,35700,35703,35709,36115,36490,36493,36491,36703,36783,37306,37934,37939,37941,37946,37944,37938,37931,38370,38712,38713,38706,38911,39015,39013,39255,39493,39491,39488,39486,39631,39764,39761,39981,39973,40367,40372,40386,40376,40605,40687,40729,40796,40806,40807,20796,20795,22216,22218,22217,23423,24020,24018,24398,25087,25892,27402,27489,28753,28760,29568,29924,30090,30318,30316,31155,31840,31839,32894,32893,33247,35186,35183,35324,35712,36118,36119,36497,36499,36705,37192,37956,37969,37970,38717,38718,38851,38849,39019,39253,39509,39501,39634,39706,40009,39985,39998,39995,40403,40407,40756,40812,40810,40852,22220,24022,25088,25891,25899,25898,26348,27408,29914,31434,31844,31843,31845,32403,32406,32404,33250,34360,34367,34865,35722,37008,37007,37987,37984,37988,38760,39023,39260,39514,39515,39511,39635,39636,39633,40020,40023,40022,40421,40607,40692,22225,22761,25900,28766,30321,30322,30679,32592,32648,34870,34873,34914,35731,35730,35734,33399,36123,37312,37994,38722,38728,38724,38854,39024,39519,39714,39768,40031,40441,40442,40572,40573,40711,40823,40818,24307,27414,28771,31852,31854,34875,35264,36513,37313,38002,38000,39025,39262,39638,39715,40652,28772,30682,35738,38007,38857,39522,39525,32412,35740,36522,37317,38013,38014,38012,40055,40056,40695,35924,38015,40474,29224,39530,39729,40475,40478,31858,9312,9313,9314,9315,9316,9317,9318,9319,9320,9321,9332,9333,9334,9335,9336,9337,9338,9339,9340,9341,8560,8561,8562,8563,8564,8565,8566,8567,8568,8569,20022,20031,20101,20128,20866,20886,20907,21241,21304,21353,21430,22794,23424,24027,12083,24191,24308,24400,24417,25908,26080,30098,30326,36789,38582,168,710,12541,12542,12445,12446,12291,20189,12293,12294,12295,12540,65339,65341,10045,12353,12354,12355,12356,12357,12358,12359,12360,12361,12362,12363,12364,12365,12366,12367,12368,12369,12370,12371,12372,12373,12374,12375,12376,12377,12378,12379,12380,12381,12382,12383,12384,12385,12386,12387,12388,12389,12390,12391,12392,12393,12394,12395,12396,12397,12398,12399,12400,12401,12402,12403,12404,12405,12406,12407,12408,12409,12410,12411,12412,12413,12414,12415,12416,12417,12418,12419,12420,12421,12422,12423,12424,12425,12426,12427,12428,12429,12430,12431,12432,12433,12434,12435,12449,12450,12451,12452,12453,12454,12455,12456,12457,12458,12459,12460,12461,12462,12463,12464,12465,12466,12467,12468,12469,12470,12471,12472,12473,12474,12475,12476,12477,12478,12479,12480,12481,12482,12483,12484,12485,12486,12487,12488,12489,12490,12491,12492,12493,12494,12495,12496,12497,12498,12499,12500,12501,12502,12503,12504,12505,12506,12507,12508,12509,12510,12511,12512,12513,12514,12515,12516,12517,12518,12519,12520,12521,12522,12523,12524,12525,12526,12527,12528,12529,12530,12531,12532,12533,12534,1040,1041,1042,1043,1044,1045,1025,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1105,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,8679,8632,8633,12751,131276,20058,131210,20994,17553,40880,20872,40881,161287,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,65506,65508,65287,65282,12849,8470,8481,12443,12444,11904,11908,11910,11911,11912,11914,11916,11917,11925,11932,11933,11941,11943,11946,11948,11950,11958,11964,11966,11974,11978,11980,11981,11983,11990,11991,11998,12003,null,null,null,643,592,603,596,629,339,248,331,650,618,20034,20060,20981,21274,21378,19975,19980,20039,20109,22231,64012,23662,24435,19983,20871,19982,20014,20115,20162,20169,20168,20888,21244,21356,21433,22304,22787,22828,23568,24063,26081,27571,27596,27668,29247,20017,20028,20200,20188,20201,20193,20189,20186,21004,21276,21324,22306,22307,22807,22831,23425,23428,23570,23611,23668,23667,24068,24192,24194,24521,25097,25168,27669,27702,27715,27711,27707,29358,29360,29578,31160,32906,38430,20238,20248,20268,20213,20244,20209,20224,20215,20232,20253,20226,20229,20258,20243,20228,20212,20242,20913,21011,21001,21008,21158,21282,21279,21325,21386,21511,22241,22239,22318,22314,22324,22844,22912,22908,22917,22907,22910,22903,22911,23382,23573,23589,23676,23674,23675,23678,24031,24181,24196,24322,24346,24436,24533,24532,24527,25180,25182,25188,25185,25190,25186,25177,25184,25178,25189,26095,26094,26430,26425,26424,26427,26426,26431,26428,26419,27672,27718,27730,27740,27727,27722,27732,27723,27724,28785,29278,29364,29365,29582,29994,30335,31349,32593,33400,33404,33408,33405,33407,34381,35198,37017,37015,37016,37019,37012,38434,38436,38432,38435,20310,20283,20322,20297,20307,20324,20286,20327,20306,20319,20289,20312,20269,20275,20287,20321,20879,20921,21020,21022,21025,21165,21166,21257,21347,21362,21390,21391,21552,21559,21546,21588,21573,21529,21532,21541,21528,21565,21583,21569,21544,21540,21575,22254,22247,22245,22337,22341,22348,22345,22347,22354,22790,22848,22950,22936,22944,22935,22926,22946,22928,22927,22951,22945,23438,23442,23592,23594,23693,23695,23688,23691,23689,23698,23690,23686,23699,23701,24032,24074,24078,24203,24201,24204,24200,24205,24325,24349,24440,24438,24530,24529,24528,24557,24552,24558,24563,24545,24548,24547,24570,24559,24567,24571,24576,24564,25146,25219,25228,25230,25231,25236,25223,25201,25211,25210,25200,25217,25224,25207,25213,25202,25204,25911,26096,26100,26099,26098,26101,26437,26439,26457,26453,26444,26440,26461,26445,26458,26443,27600,27673,27674,27768,27751,27755,27780,27787,27791,27761,27759,27753,27802,27757,27783,27797,27804,27750,27763,27749,27771,27790,28788,28794,29283,29375,29373,29379,29382,29377,29370,29381,29589,29591,29587,29588,29586,30010,30009,30100,30101,30337,31037,32820,32917,32921,32912,32914,32924,33424,33423,33413,33422,33425,33427,33418,33411,33412,35960,36809,36799,37023,37025,37029,37022,37031,37024,38448,38440,38447,38445,20019,20376,20348,20357,20349,20352,20359,20342,20340,20361,20356,20343,20300,20375,20330,20378,20345,20353,20344,20368,20380,20372,20382,20370,20354,20373,20331,20334,20894,20924,20926,21045,21042,21043,21062,21041,21180,21258,21259,21308,21394,21396,21639,21631,21633,21649,21634,21640,21611,21626,21630,21605,21612,21620,21606,21645,21615,21601,21600,21656,21603,21607,21604,22263,22265,22383,22386,22381,22379,22385,22384,22390,22400,22389,22395,22387,22388,22370,22376,22397,22796,22853,22965,22970,22991,22990,22962,22988,22977,22966,22972,22979,22998,22961,22973,22976,22984,22964,22983,23394,23397,23443,23445,23620,23623,23726,23716,23712,23733,23727,23720,23724,23711,23715,23725,23714,23722,23719,23709,23717,23734,23728,23718,24087,24084,24089,24360,24354,24355,24356,24404,24450,24446,24445,24542,24549,24621,24614,24601,24626,24587,24628,24586,24599,24627,24602,24606,24620,24610,24589,24592,24622,24595,24593,24588,24585,24604,25108,25149,25261,25268,25297,25278,25258,25270,25290,25262,25267,25263,25275,25257,25264,25272,25917,26024,26043,26121,26108,26116,26130,26120,26107,26115,26123,26125,26117,26109,26129,26128,26358,26378,26501,26476,26510,26514,26486,26491,26520,26502,26500,26484,26509,26508,26490,26527,26513,26521,26499,26493,26497,26488,26489,26516,27429,27520,27518,27614,27677,27795,27884,27883,27886,27865,27830,27860,27821,27879,27831,27856,27842,27834,27843,27846,27885,27890,27858,27869,27828,27786,27805,27776,27870,27840,27952,27853,27847,27824,27897,27855,27881,27857,28820,28824,28805,28819,28806,28804,28817,28822,28802,28826,28803,29290,29398,29387,29400,29385,29404,29394,29396,29402,29388,29393,29604,29601,29613,29606,29602,29600,29612,29597,29917,29928,30015,30016,30014,30092,30104,30383,30451,30449,30448,30453,30712,30716,30713,30715,30714,30711,31042,31039,31173,31352,31355,31483,31861,31997,32821,32911,32942,32931,32952,32949,32941,33312,33440,33472,33451,33434,33432,33435,33461,33447,33454,33468,33438,33466,33460,33448,33441,33449,33474,33444,33475,33462,33442,34416,34415,34413,34414,35926,36818,36811,36819,36813,36822,36821,36823,37042,37044,37039,37043,37040,38457,38461,38460,38458,38467,20429,20421,20435,20402,20425,20427,20417,20436,20444,20441,20411,20403,20443,20423,20438,20410,20416,20409,20460,21060,21065,21184,21186,21309,21372,21399,21398,21401,21400,21690,21665,21677,21669,21711,21699,33549,21687,21678,21718,21686,21701,21702,21664,21616,21692,21666,21694,21618,21726,21680,22453,22430,22431,22436,22412,22423,22429,22427,22420,22424,22415,22425,22437,22426,22421,22772,22797,22867,23009,23006,23022,23040,23025,23005,23034,23037,23036,23030,23012,23026,23031,23003,23017,23027,23029,23008,23038,23028,23021,23464,23628,23760,23768,23756,23767,23755,23771,23774,23770,23753,23751,23754,23766,23763,23764,23759,23752,23750,23758,23775,23800,24057,24097,24098,24099,24096,24100,24240,24228,24226,24219,24227,24229,24327,24366,24406,24454,24631,24633,24660,24690,24670,24645,24659,24647,24649,24667,24652,24640,24642,24671,24612,24644,24664,24678,24686,25154,25155,25295,25357,25355,25333,25358,25347,25323,25337,25359,25356,25336,25334,25344,25363,25364,25338,25365,25339,25328,25921,25923,26026,26047,26166,26145,26162,26165,26140,26150,26146,26163,26155,26170,26141,26164,26169,26158,26383,26384,26561,26610,26568,26554,26588,26555,26616,26584,26560,26551,26565,26603,26596,26591,26549,26573,26547,26615,26614,26606,26595,26562,26553,26574,26599,26608,26546,26620,26566,26605,26572,26542,26598,26587,26618,26569,26570,26563,26602,26571,27432,27522,27524,27574,27606,27608,27616,27680,27681,27944,27956,27949,27935,27964,27967,27922,27914,27866,27955,27908,27929,27962,27930,27921,27904,27933,27970,27905,27928,27959,27907,27919,27968,27911,27936,27948,27912,27938,27913,27920,28855,28831,28862,28849,28848,28833,28852,28853,28841,29249,29257,29258,29292,29296,29299,29294,29386,29412,29416,29419,29407,29418,29414,29411,29573,29644,29634,29640,29637,29625,29622,29621,29620,29675,29631,29639,29630,29635,29638,29624,29643,29932,29934,29998,30023,30024,30119,30122,30329,30404,30472,30467,30468,30469,30474,30455,30459,30458,30695,30696,30726,30737,30738,30725,30736,30735,30734,30729,30723,30739,31050,31052,31051,31045,31044,31189,31181,31183,31190,31182,31360,31358,31441,31488,31489,31866,31864,31865,31871,31872,31873,32003,32008,32001,32600,32657,32653,32702,32775,32782,32783,32788,32823,32984,32967,32992,32977,32968,32962,32976,32965,32995,32985,32988,32970,32981,32969,32975,32983,32998,32973,33279,33313,33428,33497,33534,33529,33543,33512,33536,33493,33594,33515,33494,33524,33516,33505,33522,33525,33548,33531,33526,33520,33514,33508,33504,33530,33523,33517,34423,34420,34428,34419,34881,34894,34919,34922,34921,35283,35332,35335,36210,36835,36833,36846,36832,37105,37053,37055,37077,37061,37054,37063,37067,37064,37332,37331,38484,38479,38481,38483,38474,38478,20510,20485,20487,20499,20514,20528,20507,20469,20468,20531,20535,20524,20470,20471,20503,20508,20512,20519,20533,20527,20529,20494,20826,20884,20883,20938,20932,20933,20936,20942,21089,21082,21074,21086,21087,21077,21090,21197,21262,21406,21798,21730,21783,21778,21735,21747,21732,21786,21759,21764,21768,21739,21777,21765,21745,21770,21755,21751,21752,21728,21774,21763,21771,22273,22274,22476,22578,22485,22482,22458,22470,22461,22460,22456,22454,22463,22471,22480,22457,22465,22798,22858,23065,23062,23085,23086,23061,23055,23063,23050,23070,23091,23404,23463,23469,23468,23555,23638,23636,23788,23807,23790,23793,23799,23808,23801,24105,24104,24232,24238,24234,24236,24371,24368,24423,24669,24666,24679,24641,24738,24712,24704,24722,24705,24733,24707,24725,24731,24727,24711,24732,24718,25113,25158,25330,25360,25430,25388,25412,25413,25398,25411,25572,25401,25419,25418,25404,25385,25409,25396,25432,25428,25433,25389,25415,25395,25434,25425,25400,25431,25408,25416,25930,25926,26054,26051,26052,26050,26186,26207,26183,26193,26386,26387,26655,26650,26697,26674,26675,26683,26699,26703,26646,26673,26652,26677,26667,26669,26671,26702,26692,26676,26653,26642,26644,26662,26664,26670,26701,26682,26661,26656,27436,27439,27437,27441,27444,27501,32898,27528,27622,27620,27624,27619,27618,27623,27685,28026,28003,28004,28022,27917,28001,28050,27992,28002,28013,28015,28049,28045,28143,28031,28038,27998,28007,28000,28055,28016,28028,27999,28034,28056,27951,28008,28043,28030,28032,28036,27926,28035,28027,28029,28021,28048,28892,28883,28881,28893,28875,32569,28898,28887,28882,28894,28896,28884,28877,28869,28870,28871,28890,28878,28897,29250,29304,29303,29302,29440,29434,29428,29438,29430,29427,29435,29441,29651,29657,29669,29654,29628,29671,29667,29673,29660,29650,29659,29652,29661,29658,29655,29656,29672,29918,29919,29940,29941,29985,30043,30047,30128,30145,30139,30148,30144,30143,30134,30138,30346,30409,30493,30491,30480,30483,30482,30499,30481,30485,30489,30490,30498,30503,30755,30764,30754,30773,30767,30760,30766,30763,30753,30761,30771,30762,30769,31060,31067,31055,31068,31059,31058,31057,31211,31212,31200,31214,31213,31210,31196,31198,31197,31366,31369,31365,31371,31372,31370,31367,31448,31504,31492,31507,31493,31503,31496,31498,31502,31497,31506,31876,31889,31882,31884,31880,31885,31877,32030,32029,32017,32014,32024,32022,32019,32031,32018,32015,32012,32604,32609,32606,32608,32605,32603,32662,32658,32707,32706,32704,32790,32830,32825,33018,33010,33017,33013,33025,33019,33024,33281,33327,33317,33587,33581,33604,33561,33617,33573,33622,33599,33601,33574,33564,33570,33602,33614,33563,33578,33544,33596,33613,33558,33572,33568,33591,33583,33577,33607,33605,33612,33619,33566,33580,33611,33575,33608,34387,34386,34466,34472,34454,34445,34449,34462,34439,34455,34438,34443,34458,34437,34469,34457,34465,34471,34453,34456,34446,34461,34448,34452,34883,34884,34925,34933,34934,34930,34944,34929,34943,34927,34947,34942,34932,34940,35346,35911,35927,35963,36004,36003,36214,36216,36277,36279,36278,36561,36563,36862,36853,36866,36863,36859,36868,36860,36854,37078,37088,37081,37082,37091,37087,37093,37080,37083,37079,37084,37092,37200,37198,37199,37333,37346,37338,38492,38495,38588,39139,39647,39727,20095,20592,20586,20577,20574,20576,20563,20555,20573,20594,20552,20557,20545,20571,20554,20578,20501,20549,20575,20585,20587,20579,20580,20550,20544,20590,20595,20567,20561,20944,21099,21101,21100,21102,21206,21203,21293,21404,21877,21878,21820,21837,21840,21812,21802,21841,21858,21814,21813,21808,21842,21829,21772,21810,21861,21838,21817,21832,21805,21819,21824,21835,22282,22279,22523,22548,22498,22518,22492,22516,22528,22509,22525,22536,22520,22539,22515,22479,22535,22510,22499,22514,22501,22508,22497,22542,22524,22544,22503,22529,22540,22513,22505,22512,22541,22532,22876,23136,23128,23125,23143,23134,23096,23093,23149,23120,23135,23141,23148,23123,23140,23127,23107,23133,23122,23108,23131,23112,23182,23102,23117,23097,23116,23152,23145,23111,23121,23126,23106,23132,23410,23406,23489,23488,23641,23838,23819,23837,23834,23840,23820,23848,23821,23846,23845,23823,23856,23826,23843,23839,23854,24126,24116,24241,24244,24249,24242,24243,24374,24376,24475,24470,24479,24714,24720,24710,24766,24752,24762,24787,24788,24783,24804,24793,24797,24776,24753,24795,24759,24778,24767,24771,24781,24768,25394,25445,25482,25474,25469,25533,25502,25517,25501,25495,25515,25486,25455,25479,25488,25454,25519,25461,25500,25453,25518,25468,25508,25403,25503,25464,25477,25473,25489,25485,25456,25939,26061,26213,26209,26203,26201,26204,26210,26392,26745,26759,26768,26780,26733,26734,26798,26795,26966,26735,26787,26796,26793,26741,26740,26802,26767,26743,26770,26748,26731,26738,26794,26752,26737,26750,26779,26774,26763,26784,26761,26788,26744,26747,26769,26764,26762,26749,27446,27443,27447,27448,27537,27535,27533,27534,27532,27690,28096,28075,28084,28083,28276,28076,28137,28130,28087,28150,28116,28160,28104,28128,28127,28118,28094,28133,28124,28125,28123,28148,28106,28093,28141,28144,28090,28117,28098,28111,28105,28112,28146,28115,28157,28119,28109,28131,28091,28922,28941,28919,28951,28916,28940,28912,28932,28915,28944,28924,28927,28934,28947,28928,28920,28918,28939,28930,28942,29310,29307,29308,29311,29469,29463,29447,29457,29464,29450,29448,29439,29455,29470,29576,29686,29688,29685,29700,29697,29693,29703,29696,29690,29692,29695,29708,29707,29684,29704,30052,30051,30158,30162,30159,30155,30156,30161,30160,30351,30345,30419,30521,30511,30509,30513,30514,30516,30515,30525,30501,30523,30517,30792,30802,30793,30797,30794,30796,30758,30789,30800,31076,31079,31081,31082,31075,31083,31073,31163,31226,31224,31222,31223,31375,31380,31376,31541,31559,31540,31525,31536,31522,31524,31539,31512,31530,31517,31537,31531,31533,31535,31538,31544,31514,31523,31892,31896,31894,31907,32053,32061,32056,32054,32058,32069,32044,32041,32065,32071,32062,32063,32074,32059,32040,32611,32661,32668,32669,32667,32714,32715,32717,32720,32721,32711,32719,32713,32799,32798,32795,32839,32835,32840,33048,33061,33049,33051,33069,33055,33068,33054,33057,33045,33063,33053,33058,33297,33336,33331,33338,33332,33330,33396,33680,33699,33704,33677,33658,33651,33700,33652,33679,33665,33685,33689,33653,33684,33705,33661,33667,33676,33693,33691,33706,33675,33662,33701,33711,33672,33687,33712,33663,33702,33671,33710,33654,33690,34393,34390,34495,34487,34498,34497,34501,34490,34480,34504,34489,34483,34488,34508,34484,34491,34492,34499,34493,34494,34898,34953,34965,34984,34978,34986,34970,34961,34977,34975,34968,34983,34969,34971,34967,34980,34988,34956,34963,34958,35202,35286,35289,35285,35376,35367,35372,35358,35897,35899,35932,35933,35965,36005,36221,36219,36217,36284,36290,36281,36287,36289,36568,36574,36573,36572,36567,36576,36577,36900,36875,36881,36892,36876,36897,37103,37098,37104,37108,37106,37107,37076,37099,37100,37097,37206,37208,37210,37203,37205,37356,37364,37361,37363,37368,37348,37369,37354,37355,37367,37352,37358,38266,38278,38280,38524,38509,38507,38513,38511,38591,38762,38916,39141,39319,20635,20629,20628,20638,20619,20643,20611,20620,20622,20637,20584,20636,20626,20610,20615,20831,20948,21266,21265,21412,21415,21905,21928,21925,21933,21879,22085,21922,21907,21896,21903,21941,21889,21923,21906,21924,21885,21900,21926,21887,21909,21921,21902,22284,22569,22583,22553,22558,22567,22563,22568,22517,22600,22565,22556,22555,22579,22591,22582,22574,22585,22584,22573,22572,22587,22881,23215,23188,23199,23162,23202,23198,23160,23206,23164,23205,23212,23189,23214,23095,23172,23178,23191,23171,23179,23209,23163,23165,23180,23196,23183,23187,23197,23530,23501,23499,23508,23505,23498,23502,23564,23600,23863,23875,23915,23873,23883,23871,23861,23889,23886,23893,23859,23866,23890,23869,23857,23897,23874,23865,23881,23864,23868,23858,23862,23872,23877,24132,24129,24408,24486,24485,24491,24777,24761,24780,24802,24782,24772,24852,24818,24842,24854,24837,24821,24851,24824,24828,24830,24769,24835,24856,24861,24848,24831,24836,24843,25162,25492,25521,25520,25550,25573,25576,25583,25539,25757,25587,25546,25568,25590,25557,25586,25589,25697,25567,25534,25565,25564,25540,25560,25555,25538,25543,25548,25547,25544,25584,25559,25561,25906,25959,25962,25956,25948,25960,25957,25996,26013,26014,26030,26064,26066,26236,26220,26235,26240,26225,26233,26218,26226,26369,26892,26835,26884,26844,26922,26860,26858,26865,26895,26838,26871,26859,26852,26870,26899,26896,26867,26849,26887,26828,26888,26992,26804,26897,26863,26822,26900,26872,26832,26877,26876,26856,26891,26890,26903,26830,26824,26845,26846,26854,26868,26833,26886,26836,26857,26901,26917,26823,27449,27451,27455,27452,27540,27543,27545,27541,27581,27632,27634,27635,27696,28156,28230,28231,28191,28233,28296,28220,28221,28229,28258,28203,28223,28225,28253,28275,28188,28211,28235,28224,28241,28219,28163,28206,28254,28264,28252,28257,28209,28200,28256,28273,28267,28217,28194,28208,28243,28261,28199,28280,28260,28279,28245,28281,28242,28262,28213,28214,28250,28960,28958,28975,28923,28974,28977,28963,28965,28962,28978,28959,28968,28986,28955,29259,29274,29320,29321,29318,29317,29323,29458,29451,29488,29474,29489,29491,29479,29490,29485,29478,29475,29493,29452,29742,29740,29744,29739,29718,29722,29729,29741,29745,29732,29731,29725,29737,29728,29746,29947,29999,30063,30060,30183,30170,30177,30182,30173,30175,30180,30167,30357,30354,30426,30534,30535,30532,30541,30533,30538,30542,30539,30540,30686,30700,30816,30820,30821,30812,30829,30833,30826,30830,30832,30825,30824,30814,30818,31092,31091,31090,31088,31234,31242,31235,31244,31236,31385,31462,31460,31562,31547,31556,31560,31564,31566,31552,31576,31557,31906,31902,31912,31905,32088,32111,32099,32083,32086,32103,32106,32079,32109,32092,32107,32082,32084,32105,32081,32095,32078,32574,32575,32613,32614,32674,32672,32673,32727,32849,32847,32848,33022,32980,33091,33098,33106,33103,33095,33085,33101,33082,33254,33262,33271,33272,33273,33284,33340,33341,33343,33397,33595,33743,33785,33827,33728,33768,33810,33767,33764,33788,33782,33808,33734,33736,33771,33763,33727,33793,33757,33765,33752,33791,33761,33739,33742,33750,33781,33737,33801,33807,33758,33809,33798,33730,33779,33749,33786,33735,33745,33770,33811,33731,33772,33774,33732,33787,33751,33762,33819,33755,33790,34520,34530,34534,34515,34531,34522,34538,34525,34539,34524,34540,34537,34519,34536,34513,34888,34902,34901,35002,35031,35001,35000,35008,35006,34998,35004,34999,35005,34994,35073,35017,35221,35224,35223,35293,35290,35291,35406,35405,35385,35417,35392,35415,35416,35396,35397,35410,35400,35409,35402,35404,35407,35935,35969,35968,36026,36030,36016,36025,36021,36228,36224,36233,36312,36307,36301,36295,36310,36316,36303,36309,36313,36296,36311,36293,36591,36599,36602,36601,36582,36590,36581,36597,36583,36584,36598,36587,36593,36588,36596,36585,36909,36916,36911,37126,37164,37124,37119,37116,37128,37113,37115,37121,37120,37127,37125,37123,37217,37220,37215,37218,37216,37377,37386,37413,37379,37402,37414,37391,37388,37376,37394,37375,37373,37382,37380,37415,37378,37404,37412,37401,37399,37381,37398,38267,38285,38284,38288,38535,38526,38536,38537,38531,38528,38594,38600,38595,38641,38640,38764,38768,38766,38919,39081,39147,40166,40697,20099,20100,20150,20669,20671,20678,20654,20676,20682,20660,20680,20674,20656,20673,20666,20657,20683,20681,20662,20664,20951,21114,21112,21115,21116,21955,21979,21964,21968,21963,21962,21981,21952,21972,21956,21993,21951,21970,21901,21967,21973,21986,21974,21960,22002,21965,21977,21954,22292,22611,22632,22628,22607,22605,22601,22639,22613,22606,22621,22617,22629,22619,22589,22627,22641,22780,23239,23236,23243,23226,23224,23217,23221,23216,23231,23240,23227,23238,23223,23232,23242,23220,23222,23245,23225,23184,23510,23512,23513,23583,23603,23921,23907,23882,23909,23922,23916,23902,23912,23911,23906,24048,24143,24142,24138,24141,24139,24261,24268,24262,24267,24263,24384,24495,24493,24823,24905,24906,24875,24901,24886,24882,24878,24902,24879,24911,24873,24896,25120,37224,25123,25125,25124,25541,25585,25579,25616,25618,25609,25632,25636,25651,25667,25631,25621,25624,25657,25655,25634,25635,25612,25638,25648,25640,25665,25653,25647,25610,25626,25664,25637,25639,25611,25575,25627,25646,25633,25614,25967,26002,26067,26246,26252,26261,26256,26251,26250,26265,26260,26232,26400,26982,26975,26936,26958,26978,26993,26943,26949,26986,26937,26946,26967,26969,27002,26952,26953,26933,26988,26931,26941,26981,26864,27000,26932,26985,26944,26991,26948,26998,26968,26945,26996,26956,26939,26955,26935,26972,26959,26961,26930,26962,26927,27003,26940,27462,27461,27459,27458,27464,27457,27547,64013,27643,27644,27641,27639,27640,28315,28374,28360,28303,28352,28319,28307,28308,28320,28337,28345,28358,28370,28349,28353,28318,28361,28343,28336,28365,28326,28367,28338,28350,28355,28380,28376,28313,28306,28302,28301,28324,28321,28351,28339,28368,28362,28311,28334,28323,28999,29012,29010,29027,29024,28993,29021,29026,29042,29048,29034,29025,28994,29016,28995,29003,29040,29023,29008,29011,28996,29005,29018,29263,29325,29324,29329,29328,29326,29500,29506,29499,29498,29504,29514,29513,29764,29770,29771,29778,29777,29783,29760,29775,29776,29774,29762,29766,29773,29780,29921,29951,29950,29949,29981,30073,30071,27011,30191,30223,30211,30199,30206,30204,30201,30200,30224,30203,30198,30189,30197,30205,30361,30389,30429,30549,30559,30560,30546,30550,30554,30569,30567,30548,30553,30573,30688,30855,30874,30868,30863,30852,30869,30853,30854,30881,30851,30841,30873,30848,30870,30843,31100,31106,31101,31097,31249,31256,31257,31250,31255,31253,31266,31251,31259,31248,31395,31394,31390,31467,31590,31588,31597,31604,31593,31602,31589,31603,31601,31600,31585,31608,31606,31587,31922,31924,31919,32136,32134,32128,32141,32127,32133,32122,32142,32123,32131,32124,32140,32148,32132,32125,32146,32621,32619,32615,32616,32620,32678,32677,32679,32731,32732,32801,33124,33120,33143,33116,33129,33115,33122,33138,26401,33118,33142,33127,33135,33092,33121,33309,33353,33348,33344,33346,33349,34033,33855,33878,33910,33913,33935,33933,33893,33873,33856,33926,33895,33840,33869,33917,33882,33881,33908,33907,33885,34055,33886,33847,33850,33844,33914,33859,33912,33842,33861,33833,33753,33867,33839,33858,33837,33887,33904,33849,33870,33868,33874,33903,33989,33934,33851,33863,33846,33843,33896,33918,33860,33835,33888,33876,33902,33872,34571,34564,34551,34572,34554,34518,34549,34637,34552,34574,34569,34561,34550,34573,34565,35030,35019,35021,35022,35038,35035,35034,35020,35024,35205,35227,35295,35301,35300,35297,35296,35298,35292,35302,35446,35462,35455,35425,35391,35447,35458,35460,35445,35459,35457,35444,35450,35900,35915,35914,35941,35940,35942,35974,35972,35973,36044,36200,36201,36241,36236,36238,36239,36237,36243,36244,36240,36242,36336,36320,36332,36337,36334,36304,36329,36323,36322,36327,36338,36331,36340,36614,36607,36609,36608,36613,36615,36616,36610,36619,36946,36927,36932,36937,36925,37136,37133,37135,37137,37142,37140,37131,37134,37230,37231,37448,37458,37424,37434,37478,37427,37477,37470,37507,37422,37450,37446,37485,37484,37455,37472,37479,37487,37430,37473,37488,37425,37460,37475,37456,37490,37454,37459,37452,37462,37426,38303,38300,38302,38299,38546,38547,38545,38551,38606,38650,38653,38648,38645,38771,38775,38776,38770,38927,38925,38926,39084,39158,39161,39343,39346,39344,39349,39597,39595,39771,40170,40173,40167,40576,40701,20710,20692,20695,20712,20723,20699,20714,20701,20708,20691,20716,20720,20719,20707,20704,20952,21120,21121,21225,21227,21296,21420,22055,22037,22028,22034,22012,22031,22044,22017,22035,22018,22010,22045,22020,22015,22009,22665,22652,22672,22680,22662,22657,22655,22644,22667,22650,22663,22673,22670,22646,22658,22664,22651,22676,22671,22782,22891,23260,23278,23269,23253,23274,23258,23277,23275,23283,23266,23264,23259,23276,23262,23261,23257,23272,23263,23415,23520,23523,23651,23938,23936,23933,23942,23930,23937,23927,23946,23945,23944,23934,23932,23949,23929,23935,24152,24153,24147,24280,24273,24279,24270,24284,24277,24281,24274,24276,24388,24387,24431,24502,24876,24872,24897,24926,24945,24947,24914,24915,24946,24940,24960,24948,24916,24954,24923,24933,24891,24938,24929,24918,25129,25127,25131,25643,25677,25691,25693,25716,25718,25714,25715,25725,25717,25702,25766,25678,25730,25694,25692,25675,25683,25696,25680,25727,25663,25708,25707,25689,25701,25719,25971,26016,26273,26272,26271,26373,26372,26402,27057,27062,27081,27040,27086,27030,27056,27052,27068,27025,27033,27022,27047,27021,27049,27070,27055,27071,27076,27069,27044,27092,27065,27082,27034,27087,27059,27027,27050,27041,27038,27097,27031,27024,27074,27061,27045,27078,27466,27469,27467,27550,27551,27552,27587,27588,27646,28366,28405,28401,28419,28453,28408,28471,28411,28462,28425,28494,28441,28442,28455,28440,28475,28434,28397,28426,28470,28531,28409,28398,28461,28480,28464,28476,28469,28395,28423,28430,28483,28421,28413,28406,28473,28444,28412,28474,28447,28429,28446,28424,28449,29063,29072,29065,29056,29061,29058,29071,29051,29062,29057,29079,29252,29267,29335,29333,29331,29507,29517,29521,29516,29794,29811,29809,29813,29810,29799,29806,29952,29954,29955,30077,30096,30230,30216,30220,30229,30225,30218,30228,30392,30593,30588,30597,30594,30574,30592,30575,30590,30595,30898,30890,30900,30893,30888,30846,30891,30878,30885,30880,30892,30882,30884,31128,31114,31115,31126,31125,31124,31123,31127,31112,31122,31120,31275,31306,31280,31279,31272,31270,31400,31403,31404,31470,31624,31644,31626,31633,31632,31638,31629,31628,31643,31630,31621,31640,21124,31641,31652,31618,31931,31935,31932,31930,32167,32183,32194,32163,32170,32193,32192,32197,32157,32206,32196,32198,32203,32204,32175,32185,32150,32188,32159,32166,32174,32169,32161,32201,32627,32738,32739,32741,32734,32804,32861,32860,33161,33158,33155,33159,33165,33164,33163,33301,33943,33956,33953,33951,33978,33998,33986,33964,33966,33963,33977,33972,33985,33997,33962,33946,33969,34000,33949,33959,33979,33954,33940,33991,33996,33947,33961,33967,33960,34006,33944,33974,33999,33952,34007,34004,34002,34011,33968,33937,34401,34611,34595,34600,34667,34624,34606,34590,34593,34585,34587,34627,34604,34625,34622,34630,34592,34610,34602,34605,34620,34578,34618,34609,34613,34626,34598,34599,34616,34596,34586,34608,34577,35063,35047,35057,35058,35066,35070,35054,35068,35062,35067,35056,35052,35051,35229,35233,35231,35230,35305,35307,35304,35499,35481,35467,35474,35471,35478,35901,35944,35945,36053,36047,36055,36246,36361,36354,36351,36365,36349,36362,36355,36359,36358,36357,36350,36352,36356,36624,36625,36622,36621,37155,37148,37152,37154,37151,37149,37146,37156,37153,37147,37242,37234,37241,37235,37541,37540,37494,37531,37498,37536,37524,37546,37517,37542,37530,37547,37497,37527,37503,37539,37614,37518,37506,37525,37538,37501,37512,37537,37514,37510,37516,37529,37543,37502,37511,37545,37533,37515,37421,38558,38561,38655,38744,38781,38778,38782,38787,38784,38786,38779,38788,38785,38783,38862,38861,38934,39085,39086,39170,39168,39175,39325,39324,39363,39353,39355,39354,39362,39357,39367,39601,39651,39655,39742,39743,39776,39777,39775,40177,40178,40181,40615,20735,20739,20784,20728,20742,20743,20726,20734,20747,20748,20733,20746,21131,21132,21233,21231,22088,22082,22092,22069,22081,22090,22089,22086,22104,22106,22080,22067,22077,22060,22078,22072,22058,22074,22298,22699,22685,22705,22688,22691,22703,22700,22693,22689,22783,23295,23284,23293,23287,23286,23299,23288,23298,23289,23297,23303,23301,23311,23655,23961,23959,23967,23954,23970,23955,23957,23968,23964,23969,23962,23966,24169,24157,24160,24156,32243,24283,24286,24289,24393,24498,24971,24963,24953,25009,25008,24994,24969,24987,24979,25007,25005,24991,24978,25002,24993,24973,24934,25011,25133,25710,25712,25750,25760,25733,25751,25756,25743,25739,25738,25740,25763,25759,25704,25777,25752,25974,25978,25977,25979,26034,26035,26293,26288,26281,26290,26295,26282,26287,27136,27142,27159,27109,27128,27157,27121,27108,27168,27135,27116,27106,27163,27165,27134,27175,27122,27118,27156,27127,27111,27200,27144,27110,27131,27149,27132,27115,27145,27140,27160,27173,27151,27126,27174,27143,27124,27158,27473,27557,27555,27554,27558,27649,27648,27647,27650,28481,28454,28542,28551,28614,28562,28557,28553,28556,28514,28495,28549,28506,28566,28534,28524,28546,28501,28530,28498,28496,28503,28564,28563,28509,28416,28513,28523,28541,28519,28560,28499,28555,28521,28543,28565,28515,28535,28522,28539,29106,29103,29083,29104,29088,29082,29097,29109,29085,29093,29086,29092,29089,29098,29084,29095,29107,29336,29338,29528,29522,29534,29535,29536,29533,29531,29537,29530,29529,29538,29831,29833,29834,29830,29825,29821,29829,29832,29820,29817,29960,29959,30078,30245,30238,30233,30237,30236,30243,30234,30248,30235,30364,30365,30366,30363,30605,30607,30601,30600,30925,30907,30927,30924,30929,30926,30932,30920,30915,30916,30921,31130,31137,31136,31132,31138,31131,27510,31289,31410,31412,31411,31671,31691,31678,31660,31694,31663,31673,31690,31669,31941,31944,31948,31947,32247,32219,32234,32231,32215,32225,32259,32250,32230,32246,32241,32240,32238,32223,32630,32684,32688,32685,32749,32747,32746,32748,32742,32744,32868,32871,33187,33183,33182,33173,33186,33177,33175,33302,33359,33363,33362,33360,33358,33361,34084,34107,34063,34048,34089,34062,34057,34061,34079,34058,34087,34076,34043,34091,34042,34056,34060,34036,34090,34034,34069,34039,34027,34035,34044,34066,34026,34025,34070,34046,34088,34077,34094,34050,34045,34078,34038,34097,34086,34023,34024,34032,34031,34041,34072,34080,34096,34059,34073,34095,34402,34646,34659,34660,34679,34785,34675,34648,34644,34651,34642,34657,34650,34641,34654,34669,34666,34640,34638,34655,34653,34671,34668,34682,34670,34652,34661,34639,34683,34677,34658,34663,34665,34906,35077,35084,35092,35083,35095,35096,35097,35078,35094,35089,35086,35081,35234,35236,35235,35309,35312,35308,35535,35526,35512,35539,35537,35540,35541,35515,35543,35518,35520,35525,35544,35523,35514,35517,35545,35902,35917,35983,36069,36063,36057,36072,36058,36061,36071,36256,36252,36257,36251,36384,36387,36389,36388,36398,36373,36379,36374,36369,36377,36390,36391,36372,36370,36376,36371,36380,36375,36378,36652,36644,36632,36634,36640,36643,36630,36631,36979,36976,36975,36967,36971,37167,37163,37161,37162,37170,37158,37166,37253,37254,37258,37249,37250,37252,37248,37584,37571,37572,37568,37593,37558,37583,37617,37599,37592,37609,37591,37597,37580,37615,37570,37608,37578,37576,37582,37606,37581,37589,37577,37600,37598,37607,37585,37587,37557,37601,37574,37556,38268,38316,38315,38318,38320,38564,38562,38611,38661,38664,38658,38746,38794,38798,38792,38864,38863,38942,38941,38950,38953,38952,38944,38939,38951,39090,39176,39162,39185,39188,39190,39191,39189,39388,39373,39375,39379,39380,39374,39369,39382,39384,39371,39383,39372,39603,39660,39659,39667,39666,39665,39750,39747,39783,39796,39793,39782,39798,39797,39792,39784,39780,39788,40188,40186,40189,40191,40183,40199,40192,40185,40187,40200,40197,40196,40579,40659,40719,40720,20764,20755,20759,20762,20753,20958,21300,21473,22128,22112,22126,22131,22118,22115,22125,22130,22110,22135,22300,22299,22728,22717,22729,22719,22714,22722,22716,22726,23319,23321,23323,23329,23316,23315,23312,23318,23336,23322,23328,23326,23535,23980,23985,23977,23975,23989,23984,23982,23978,23976,23986,23981,23983,23988,24167,24168,24166,24175,24297,24295,24294,24296,24293,24395,24508,24989,25000,24982,25029,25012,25030,25025,25036,25018,25023,25016,24972,25815,25814,25808,25807,25801,25789,25737,25795,25819,25843,25817,25907,25983,25980,26018,26312,26302,26304,26314,26315,26319,26301,26299,26298,26316,26403,27188,27238,27209,27239,27186,27240,27198,27229,27245,27254,27227,27217,27176,27226,27195,27199,27201,27242,27236,27216,27215,27220,27247,27241,27232,27196,27230,27222,27221,27213,27214,27206,27477,27476,27478,27559,27562,27563,27592,27591,27652,27651,27654,28589,28619,28579,28615,28604,28622,28616,28510,28612,28605,28574,28618,28584,28676,28581,28590,28602,28588,28586,28623,28607,28600,28578,28617,28587,28621,28591,28594,28592,29125,29122,29119,29112,29142,29120,29121,29131,29140,29130,29127,29135,29117,29144,29116,29126,29146,29147,29341,29342,29545,29542,29543,29548,29541,29547,29546,29823,29850,29856,29844,29842,29845,29857,29963,30080,30255,30253,30257,30269,30259,30268,30261,30258,30256,30395,30438,30618,30621,30625,30620,30619,30626,30627,30613,30617,30615,30941,30953,30949,30954,30942,30947,30939,30945,30946,30957,30943,30944,31140,31300,31304,31303,31414,31416,31413,31409,31415,31710,31715,31719,31709,31701,31717,31706,31720,31737,31700,31722,31714,31708,31723,31704,31711,31954,31956,31959,31952,31953,32274,32289,32279,32268,32287,32288,32275,32270,32284,32277,32282,32290,32267,32271,32278,32269,32276,32293,32292,32579,32635,32636,32634,32689,32751,32810,32809,32876,33201,33190,33198,33209,33205,33195,33200,33196,33204,33202,33207,33191,33266,33365,33366,33367,34134,34117,34155,34125,34131,34145,34136,34112,34118,34148,34113,34146,34116,34129,34119,34147,34110,34139,34161,34126,34158,34165,34133,34151,34144,34188,34150,34141,34132,34149,34156,34403,34405,34404,34715,34703,34711,34707,34706,34696,34689,34710,34712,34681,34695,34723,34693,34704,34705,34717,34692,34708,34716,34714,34697,35102,35110,35120,35117,35118,35111,35121,35106,35113,35107,35119,35116,35103,35313,35552,35554,35570,35572,35573,35549,35604,35556,35551,35568,35528,35550,35553,35560,35583,35567,35579,35985,35986,35984,36085,36078,36081,36080,36083,36204,36206,36261,36263,36403,36414,36408,36416,36421,36406,36412,36413,36417,36400,36415,36541,36662,36654,36661,36658,36665,36663,36660,36982,36985,36987,36998,37114,37171,37173,37174,37267,37264,37265,37261,37263,37671,37662,37640,37663,37638,37647,37754,37688,37692,37659,37667,37650,37633,37702,37677,37646,37645,37579,37661,37626,37669,37651,37625,37623,37684,37634,37668,37631,37673,37689,37685,37674,37652,37644,37643,37630,37641,37632,37627,37654,38332,38349,38334,38329,38330,38326,38335,38325,38333,38569,38612,38667,38674,38672,38809,38807,38804,38896,38904,38965,38959,38962,39204,39199,39207,39209,39326,39406,39404,39397,39396,39408,39395,39402,39401,39399,39609,39615,39604,39611,39670,39674,39673,39671,39731,39808,39813,39815,39804,39806,39803,39810,39827,39826,39824,39802,39829,39805,39816,40229,40215,40224,40222,40212,40233,40221,40216,40226,40208,40217,40223,40584,40582,40583,40622,40621,40661,40662,40698,40722,40765,20774,20773,20770,20772,20768,20777,21236,22163,22156,22157,22150,22148,22147,22142,22146,22143,22145,22742,22740,22735,22738,23341,23333,23346,23331,23340,23335,23334,23343,23342,23419,23537,23538,23991,24172,24170,24510,24507,25027,25013,25020,25063,25056,25061,25060,25064,25054,25839,25833,25827,25835,25828,25832,25985,25984,26038,26074,26322,27277,27286,27265,27301,27273,27295,27291,27297,27294,27271,27283,27278,27285,27267,27304,27300,27281,27263,27302,27290,27269,27276,27282,27483,27565,27657,28620,28585,28660,28628,28643,28636,28653,28647,28646,28638,28658,28637,28642,28648,29153,29169,29160,29170,29156,29168,29154,29555,29550,29551,29847,29874,29867,29840,29866,29869,29873,29861,29871,29968,29969,29970,29967,30084,30275,30280,30281,30279,30372,30441,30645,30635,30642,30647,30646,30644,30641,30632,30704,30963,30973,30978,30971,30972,30962,30981,30969,30974,30980,31147,31144,31324,31323,31318,31320,31316,31322,31422,31424,31425,31749,31759,31730,31744,31743,31739,31758,31732,31755,31731,31746,31753,31747,31745,31736,31741,31750,31728,31729,31760,31754,31976,32301,32316,32322,32307,38984,32312,32298,32329,32320,32327,32297,32332,32304,32315,32310,32324,32314,32581,32639,32638,32637,32756,32754,32812,33211,33220,33228,33226,33221,33223,33212,33257,33371,33370,33372,34179,34176,34191,34215,34197,34208,34187,34211,34171,34212,34202,34206,34167,34172,34185,34209,34170,34168,34135,34190,34198,34182,34189,34201,34205,34177,34210,34178,34184,34181,34169,34166,34200,34192,34207,34408,34750,34730,34733,34757,34736,34732,34745,34741,34748,34734,34761,34755,34754,34764,34743,34735,34756,34762,34740,34742,34751,34744,34749,34782,34738,35125,35123,35132,35134,35137,35154,35127,35138,35245,35247,35246,35314,35315,35614,35608,35606,35601,35589,35595,35618,35599,35602,35605,35591,35597,35592,35590,35612,35603,35610,35919,35952,35954,35953,35951,35989,35988,36089,36207,36430,36429,36435,36432,36428,36423,36675,36672,36997,36990,37176,37274,37282,37275,37273,37279,37281,37277,37280,37793,37763,37807,37732,37718,37703,37756,37720,37724,37750,37705,37712,37713,37728,37741,37775,37708,37738,37753,37719,37717,37714,37711,37745,37751,37755,37729,37726,37731,37735,37760,37710,37721,38343,38336,38345,38339,38341,38327,38574,38576,38572,38688,38687,38680,38685,38681,38810,38817,38812,38814,38813,38869,38868,38897,38977,38980,38986,38985,38981,38979,39205,39211,39212,39210,39219,39218,39215,39213,39217,39216,39320,39331,39329,39426,39418,39412,39415,39417,39416,39414,39419,39421,39422,39420,39427,39614,39678,39677,39681,39676,39752,39834,39848,39838,39835,39846,39841,39845,39844,39814,39842,39840,39855,40243,40257,40295,40246,40238,40239,40241,40248,40240,40261,40258,40259,40254,40247,40256,40253,32757,40237,40586,40585,40589,40624,40648,40666,40699,40703,40740,40739,40738,40788,40864,20785,20781,20782,22168,22172,22167,22170,22173,22169,22896,23356,23657,23658,24000,24173,24174,25048,25055,25069,25070,25073,25066,25072,25067,25046,25065,25855,25860,25853,25848,25857,25859,25852,26004,26075,26330,26331,26328,27333,27321,27325,27361,27334,27322,27318,27319,27335,27316,27309,27486,27593,27659,28679,28684,28685,28673,28677,28692,28686,28671,28672,28667,28710,28668,28663,28682,29185,29183,29177,29187,29181,29558,29880,29888,29877,29889,29886,29878,29883,29890,29972,29971,30300,30308,30297,30288,30291,30295,30298,30374,30397,30444,30658,30650,30975,30988,30995,30996,30985,30992,30994,30993,31149,31148,31327,31772,31785,31769,31776,31775,31789,31773,31782,31784,31778,31781,31792,32348,32336,32342,32355,32344,32354,32351,32337,32352,32343,32339,32693,32691,32759,32760,32885,33233,33234,33232,33375,33374,34228,34246,34240,34243,34242,34227,34229,34237,34247,34244,34239,34251,34254,34248,34245,34225,34230,34258,34340,34232,34231,34238,34409,34791,34790,34786,34779,34795,34794,34789,34783,34803,34788,34772,34780,34771,34797,34776,34787,34724,34775,34777,34817,34804,34792,34781,35155,35147,35151,35148,35142,35152,35153,35145,35626,35623,35619,35635,35632,35637,35655,35631,35644,35646,35633,35621,35639,35622,35638,35630,35620,35643,35645,35642,35906,35957,35993,35992,35991,36094,36100,36098,36096,36444,36450,36448,36439,36438,36446,36453,36455,36443,36442,36449,36445,36457,36436,36678,36679,36680,36683,37160,37178,37179,37182,37288,37285,37287,37295,37290,37813,37772,37778,37815,37787,37789,37769,37799,37774,37802,37790,37798,37781,37768,37785,37791,37773,37809,37777,37810,37796,37800,37812,37795,37797,38354,38355,38353,38579,38615,38618,24002,38623,38616,38621,38691,38690,38693,38828,38830,38824,38827,38820,38826,38818,38821,38871,38873,38870,38872,38906,38992,38993,38994,39096,39233,39228,39226,39439,39435,39433,39437,39428,39441,39434,39429,39431,39430,39616,39644,39688,39684,39685,39721,39733,39754,39756,39755,39879,39878,39875,39871,39873,39861,39864,39891,39862,39876,39865,39869,40284,40275,40271,40266,40283,40267,40281,40278,40268,40279,40274,40276,40287,40280,40282,40590,40588,40671,40705,40704,40726,40741,40747,40746,40745,40744,40780,40789,20788,20789,21142,21239,21428,22187,22189,22182,22183,22186,22188,22746,22749,22747,22802,23357,23358,23359,24003,24176,24511,25083,25863,25872,25869,25865,25868,25870,25988,26078,26077,26334,27367,27360,27340,27345,27353,27339,27359,27356,27344,27371,27343,27341,27358,27488,27568,27660,28697,28711,28704,28694,28715,28705,28706,28707,28713,28695,28708,28700,28714,29196,29194,29191,29186,29189,29349,29350,29348,29347,29345,29899,29893,29879,29891,29974,30304,30665,30666,30660,30705,31005,31003,31009,31004,30999,31006,31152,31335,31336,31795,31804,31801,31788,31803,31980,31978,32374,32373,32376,32368,32375,32367,32378,32370,32372,32360,32587,32586,32643,32646,32695,32765,32766,32888,33239,33237,33380,33377,33379,34283,34289,34285,34265,34273,34280,34266,34263,34284,34290,34296,34264,34271,34275,34268,34257,34288,34278,34287,34270,34274,34816,34810,34819,34806,34807,34825,34828,34827,34822,34812,34824,34815,34826,34818,35170,35162,35163,35159,35169,35164,35160,35165,35161,35208,35255,35254,35318,35664,35656,35658,35648,35667,35670,35668,35659,35669,35665,35650,35666,35671,35907,35959,35958,35994,36102,36103,36105,36268,36266,36269,36267,36461,36472,36467,36458,36463,36475,36546,36690,36689,36687,36688,36691,36788,37184,37183,37296,37293,37854,37831,37839,37826,37850,37840,37881,37868,37836,37849,37801,37862,37834,37844,37870,37859,37845,37828,37838,37824,37842,37863,38269,38362,38363,38625,38697,38699,38700,38696,38694,38835,38839,38838,38877,38878,38879,39004,39001,39005,38999,39103,39101,39099,39102,39240,39239,39235,39334,39335,39450,39445,39461,39453,39460,39451,39458,39456,39463,39459,39454,39452,39444,39618,39691,39690,39694,39692,39735,39914,39915,39904,39902,39908,39910,39906,39920,39892,39895,39916,39900,39897,39909,39893,39905,39898,40311,40321,40330,40324,40328,40305,40320,40312,40326,40331,40332,40317,40299,40308,40309,40304,40297,40325,40307,40315,40322,40303,40313,40319,40327,40296,40596,40593,40640,40700,40749,40768,40769,40781,40790,40791,40792,21303,22194,22197,22195,22755,23365,24006,24007,24302,24303,24512,24513,25081,25879,25878,25877,25875,26079,26344,26339,26340,27379,27376,27370,27368,27385,27377,27374,27375,28732,28725,28719,28727,28724,28721,28738,28728,28735,28730,28729,28736,28731,28723,28737,29203,29204,29352,29565,29564,29882,30379,30378,30398,30445,30668,30670,30671,30669,30706,31013,31011,31015,31016,31012,31017,31154,31342,31340,31341,31479,31817,31816,31818,31815,31813,31982,32379,32382,32385,32384,32698,32767,32889,33243,33241,33291,33384,33385,34338,34303,34305,34302,34331,34304,34294,34308,34313,34309,34316,34301,34841,34832,34833,34839,34835,34838,35171,35174,35257,35319,35680,35690,35677,35688,35683,35685,35687,35693,36270,36486,36488,36484,36697,36694,36695,36693,36696,36698,37005,37187,37185,37303,37301,37298,37299,37899,37907,37883,37920,37903,37908,37886,37909,37904,37928,37913,37901,37877,37888,37879,37895,37902,37910,37906,37882,37897,37880,37898,37887,37884,37900,37878,37905,37894,38366,38368,38367,38702,38703,38841,38843,38909,38910,39008,39010,39011,39007,39105,39106,39248,39246,39257,39244,39243,39251,39474,39476,39473,39468,39466,39478,39465,39470,39480,39469,39623,39626,39622,39696,39698,39697,39947,39944,39927,39941,39954,39928,40000,39943,39950,39942,39959,39956,39945,40351,40345,40356,40349,40338,40344,40336,40347,40352,40340,40348,40362,40343,40353,40346,40354,40360,40350,40355,40383,40361,40342,40358,40359,40601,40603,40602,40677,40676,40679,40678,40752,40750,40795,40800,40798,40797,40793,40849,20794,20793,21144,21143,22211,22205,22206,23368,23367,24011,24015,24305,25085,25883,27394,27388,27395,27384,27392,28739,28740,28746,28744,28745,28741,28742,29213,29210,29209,29566,29975,30314,30672,31021,31025,31023,31828,31827,31986,32394,32391,32392,32395,32390,32397,32589,32699,32816,33245,34328,34346,34342,34335,34339,34332,34329,34343,34350,34337,34336,34345,34334,34341,34857,34845,34843,34848,34852,34844,34859,34890,35181,35177,35182,35179,35322,35705,35704,35653,35706,35707,36112,36116,36271,36494,36492,36702,36699,36701,37190,37188,37189,37305,37951,37947,37942,37929,37949,37948,37936,37945,37930,37943,37932,37952,37937,38373,38372,38371,38709,38714,38847,38881,39012,39113,39110,39104,39256,39254,39481,39485,39494,39492,39490,39489,39482,39487,39629,39701,39703,39704,39702,39738,39762,39979,39965,39964,39980,39971,39976,39977,39972,39969,40375,40374,40380,40385,40391,40394,40399,40382,40389,40387,40379,40373,40398,40377,40378,40364,40392,40369,40365,40396,40371,40397,40370,40570,40604,40683,40686,40685,40731,40728,40730,40753,40782,40805,40804,40850,20153,22214,22213,22219,22897,23371,23372,24021,24017,24306,25889,25888,25894,25890,27403,27400,27401,27661,28757,28758,28759,28754,29214,29215,29353,29567,29912,29909,29913,29911,30317,30381,31029,31156,31344,31345,31831,31836,31833,31835,31834,31988,31985,32401,32591,32647,33246,33387,34356,34357,34355,34348,34354,34358,34860,34856,34854,34858,34853,35185,35263,35262,35323,35710,35716,35714,35718,35717,35711,36117,36501,36500,36506,36498,36496,36502,36503,36704,36706,37191,37964,37968,37962,37963,37967,37959,37957,37960,37961,37958,38719,38883,39018,39017,39115,39252,39259,39502,39507,39508,39500,39503,39496,39498,39497,39506,39504,39632,39705,39723,39739,39766,39765,40006,40008,39999,40004,39993,39987,40001,39996,39991,39988,39986,39997,39990,40411,40402,40414,40410,40395,40400,40412,40401,40415,40425,40409,40408,40406,40437,40405,40413,40630,40688,40757,40755,40754,40770,40811,40853,40866,20797,21145,22760,22759,22898,23373,24024,34863,24399,25089,25091,25092,25897,25893,26006,26347,27409,27410,27407,27594,28763,28762,29218,29570,29569,29571,30320,30676,31847,31846,32405,33388,34362,34368,34361,34364,34353,34363,34366,34864,34866,34862,34867,35190,35188,35187,35326,35724,35726,35723,35720,35909,36121,36504,36708,36707,37308,37986,37973,37981,37975,37982,38852,38853,38912,39510,39513,39710,39711,39712,40018,40024,40016,40010,40013,40011,40021,40025,40012,40014,40443,40439,40431,40419,40427,40440,40420,40438,40417,40430,40422,40434,40432,40418,40428,40436,40435,40424,40429,40642,40656,40690,40691,40710,40732,40760,40759,40758,40771,40783,40817,40816,40814,40815,22227,22221,23374,23661,25901,26349,26350,27411,28767,28769,28765,28768,29219,29915,29925,30677,31032,31159,31158,31850,32407,32649,33389,34371,34872,34871,34869,34891,35732,35733,36510,36511,36512,36509,37310,37309,37314,37995,37992,37993,38629,38726,38723,38727,38855,38885,39518,39637,39769,40035,40039,40038,40034,40030,40032,40450,40446,40455,40451,40454,40453,40448,40449,40457,40447,40445,40452,40608,40734,40774,40820,40821,40822,22228,25902,26040,27416,27417,27415,27418,28770,29222,29354,30680,30681,31033,31849,31851,31990,32410,32408,32411,32409,33248,33249,34374,34375,34376,35193,35194,35196,35195,35327,35736,35737,36517,36516,36515,37998,37997,37999,38001,38003,38729,39026,39263,40040,40046,40045,40459,40461,40464,40463,40466,40465,40609,40693,40713,40775,40824,40827,40826,40825,22302,28774,31855,34876,36274,36518,37315,38004,38008,38006,38005,39520,40052,40051,40049,40053,40468,40467,40694,40714,40868,28776,28773,31991,34410,34878,34877,34879,35742,35996,36521,36553,38731,39027,39028,39116,39265,39339,39524,39526,39527,39716,40469,40471,40776,25095,27422,29223,34380,36520,38018,38016,38017,39529,39528,39726,40473,29225,34379,35743,38019,40057,40631,30325,39531,40058,40477,28777,28778,40612,40830,40777,40856,30849,37561,35023,22715,24658,31911,23290,9556,9574,9559,9568,9580,9571,9562,9577,9565,9554,9572,9557,9566,9578,9569,9560,9575,9563,9555,9573,9558,9567,9579,9570,9561,9576,9564,9553,9552,9581,9582,9584,9583,65517,132423,37595,132575,147397,34124,17077,29679,20917,13897,149826,166372,37700,137691,33518,146632,30780,26436,25311,149811,166314,131744,158643,135941,20395,140525,20488,159017,162436,144896,150193,140563,20521,131966,24484,131968,131911,28379,132127,20605,20737,13434,20750,39020,14147,33814,149924,132231,20832,144308,20842,134143,139516,131813,140592,132494,143923,137603,23426,34685,132531,146585,20914,20920,40244,20937,20943,20945,15580,20947,150182,20915,20962,21314,20973,33741,26942,145197,24443,21003,21030,21052,21173,21079,21140,21177,21189,31765,34114,21216,34317,158483,21253,166622,21833,28377,147328,133460,147436,21299,21316,134114,27851,136998,26651,29653,24650,16042,14540,136936,29149,17570,21357,21364,165547,21374,21375,136598,136723,30694,21395,166555,21408,21419,21422,29607,153458,16217,29596,21441,21445,27721,20041,22526,21465,15019,134031,21472,147435,142755,21494,134263,21523,28793,21803,26199,27995,21613,158547,134516,21853,21647,21668,18342,136973,134877,15796,134477,166332,140952,21831,19693,21551,29719,21894,21929,22021,137431,147514,17746,148533,26291,135348,22071,26317,144010,26276,26285,22093,22095,30961,22257,38791,21502,22272,22255,22253,166758,13859,135759,22342,147877,27758,28811,22338,14001,158846,22502,136214,22531,136276,148323,22566,150517,22620,22698,13665,22752,22748,135740,22779,23551,22339,172368,148088,37843,13729,22815,26790,14019,28249,136766,23076,21843,136850,34053,22985,134478,158849,159018,137180,23001,137211,137138,159142,28017,137256,136917,23033,159301,23211,23139,14054,149929,23159,14088,23190,29797,23251,159649,140628,15749,137489,14130,136888,24195,21200,23414,25992,23420,162318,16388,18525,131588,23509,24928,137780,154060,132517,23539,23453,19728,23557,138052,23571,29646,23572,138405,158504,23625,18653,23685,23785,23791,23947,138745,138807,23824,23832,23878,138916,23738,24023,33532,14381,149761,139337,139635,33415,14390,15298,24110,27274,24181,24186,148668,134355,21414,20151,24272,21416,137073,24073,24308,164994,24313,24315,14496,24316,26686,37915,24333,131521,194708,15070,18606,135994,24378,157832,140240,24408,140401,24419,38845,159342,24434,37696,166454,24487,23990,15711,152144,139114,159992,140904,37334,131742,166441,24625,26245,137335,14691,15815,13881,22416,141236,31089,15936,24734,24740,24755,149890,149903,162387,29860,20705,23200,24932,33828,24898,194726,159442,24961,20980,132694,24967,23466,147383,141407,25043,166813,170333,25040,14642,141696,141505,24611,24924,25886,25483,131352,25285,137072,25301,142861,25452,149983,14871,25656,25592,136078,137212,25744,28554,142902,38932,147596,153373,25825,25829,38011,14950,25658,14935,25933,28438,150056,150051,25989,25965,25951,143486,26037,149824,19255,26065,16600,137257,26080,26083,24543,144384,26136,143863,143864,26180,143780,143781,26187,134773,26215,152038,26227,26228,138813,143921,165364,143816,152339,30661,141559,39332,26370,148380,150049,15147,27130,145346,26462,26471,26466,147917,168173,26583,17641,26658,28240,37436,26625,144358,159136,26717,144495,27105,27147,166623,26995,26819,144845,26881,26880,15666,14849,144956,15232,26540,26977,166474,17148,26934,27032,15265,132041,33635,20624,27129,144985,139562,27205,145155,27293,15347,26545,27336,168348,15373,27421,133411,24798,27445,27508,141261,28341,146139,132021,137560,14144,21537,146266,27617,147196,27612,27703,140427,149745,158545,27738,33318,27769,146876,17605,146877,147876,149772,149760,146633,14053,15595,134450,39811,143865,140433,32655,26679,159013,159137,159211,28054,27996,28284,28420,149887,147589,159346,34099,159604,20935,27804,28189,33838,166689,28207,146991,29779,147330,31180,28239,23185,143435,28664,14093,28573,146992,28410,136343,147517,17749,37872,28484,28508,15694,28532,168304,15675,28575,147780,28627,147601,147797,147513,147440,147380,147775,20959,147798,147799,147776,156125,28747,28798,28839,28801,28876,28885,28886,28895,16644,15848,29108,29078,148087,28971,28997,23176,29002,29038,23708,148325,29007,37730,148161,28972,148570,150055,150050,29114,166888,28861,29198,37954,29205,22801,37955,29220,37697,153093,29230,29248,149876,26813,29269,29271,15957,143428,26637,28477,29314,29482,29483,149539,165931,18669,165892,29480,29486,29647,29610,134202,158254,29641,29769,147938,136935,150052,26147,14021,149943,149901,150011,29687,29717,26883,150054,29753,132547,16087,29788,141485,29792,167602,29767,29668,29814,33721,29804,14128,29812,37873,27180,29826,18771,150156,147807,150137,166799,23366,166915,137374,29896,137608,29966,29929,29982,167641,137803,23511,167596,37765,30029,30026,30055,30062,151426,16132,150803,30094,29789,30110,30132,30210,30252,30289,30287,30319,30326,156661,30352,33263,14328,157969,157966,30369,30373,30391,30412,159647,33890,151709,151933,138780,30494,30502,30528,25775,152096,30552,144044,30639,166244,166248,136897,30708,30729,136054,150034,26826,30895,30919,30931,38565,31022,153056,30935,31028,30897,161292,36792,34948,166699,155779,140828,31110,35072,26882,31104,153687,31133,162617,31036,31145,28202,160038,16040,31174,168205,31188}; + +// https://encoding.spec.whatwg.org/#big5-decoder +decoder::result big5_decoder::handler(inout string& input, inout int& index, out int ch[2]) +{ + int b = index == (int)input.size() ? EOF : (byte)input[index++]; // read input byte + + // 1. If byte is end-of-queue and Big5 lead is not 0x00, set Big5 lead to 0x00 and return error. + if (b == EOF && m_lead != 0) + { + m_lead = 0; + return result_error; + } + + // 2. If byte is end-of-queue and Big5 lead is 0x00, return finished. + if (b == EOF && m_lead == 0) + return result_finished; + + // 3. + if (m_lead != 0) + { + int lead = m_lead; + int pointer = null; + m_lead = 0; + + // 1. + int offset = b < 0x7F ? 0x40 : 0x62; + + // 2. + if ((b >= 0x40 && b <= 0x7E) || (b >= 0xA1 && b <= 0xFE)) + pointer = (lead - 0x81) * 157 + b - offset; + + // 3. + if (pointer == 1133) { *ch = 0xCA, ch[1] = 0x304; return result_codepoint; } + else if (pointer == 1135) { *ch = 0xCA, ch[1] = 0x30C; return result_codepoint; } + else if (pointer == 1164) { *ch = 0xEA, ch[1] = 0x304; return result_codepoint; } + else if (pointer == 1166) { *ch = 0xEA, ch[1] = 0x30C; return result_codepoint; } + + // 4. + int code_point = pointer != null ? index_code_point(pointer, m_index) : null; + + // 5. + if (code_point != null) + { + *ch = code_point; + return result_codepoint; + } + + // 6. If byte is an ASCII byte, restore byte to ioQueue. + if (b >= 0 && b <= 0x7F) index--; + + // 7. + return result_error; + } + + // 4. If byte is an ASCII byte, return a code point whose value is byte. + if (b >= 0 && b <= 0x7F) + { + *ch = b; + return result_codepoint; + } + + // 5. + if (b >= 0x81 && b <= 0xFE) + { + m_lead = b; + return result_continue; + } + + // 6. + return result_error; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +struct jis_decoder : decoder +{ + static int m_jis0208_index[11280]; + static int m_jis0212_index[8836]; +}; + +// https://encoding.spec.whatwg.org/indexes.json +int jis_decoder::m_jis0208_index[] = {12288,12289,12290,65292,65294,12539,65306,65307,65311,65281,12443,12444,180,65344,168,65342,65507,65343,12541,12542,12445,12446,12291,20189,12293,12294,12295,12540,8213,8208,65295,65340,65374,8741,65372,8230,8229,8216,8217,8220,8221,65288,65289,12308,12309,65339,65341,65371,65373,12296,12297,12298,12299,12300,12301,12302,12303,12304,12305,65291,65293,177,215,247,65309,8800,65308,65310,8806,8807,8734,8756,9794,9792,176,8242,8243,8451,65509,65284,65504,65505,65285,65283,65286,65290,65312,167,9734,9733,9675,9679,9678,9671,9670,9633,9632,9651,9650,9661,9660,8251,12306,8594,8592,8593,8595,12307,null,null,null,null,null,null,null,null,null,null,null,8712,8715,8838,8839,8834,8835,8746,8745,null,null,null,null,null,null,null,null,8743,8744,65506,8658,8660,8704,8707,null,null,null,null,null,null,null,null,null,null,null,8736,8869,8978,8706,8711,8801,8786,8810,8811,8730,8765,8733,8757,8747,8748,null,null,null,null,null,null,null,8491,8240,9839,9837,9834,8224,8225,182,null,null,null,null,9711,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,65296,65297,65298,65299,65300,65301,65302,65303,65304,65305,null,null,null,null,null,null,null,65313,65314,65315,65316,65317,65318,65319,65320,65321,65322,65323,65324,65325,65326,65327,65328,65329,65330,65331,65332,65333,65334,65335,65336,65337,65338,null,null,null,null,null,null,65345,65346,65347,65348,65349,65350,65351,65352,65353,65354,65355,65356,65357,65358,65359,65360,65361,65362,65363,65364,65365,65366,65367,65368,65369,65370,null,null,null,null,12353,12354,12355,12356,12357,12358,12359,12360,12361,12362,12363,12364,12365,12366,12367,12368,12369,12370,12371,12372,12373,12374,12375,12376,12377,12378,12379,12380,12381,12382,12383,12384,12385,12386,12387,12388,12389,12390,12391,12392,12393,12394,12395,12396,12397,12398,12399,12400,12401,12402,12403,12404,12405,12406,12407,12408,12409,12410,12411,12412,12413,12414,12415,12416,12417,12418,12419,12420,12421,12422,12423,12424,12425,12426,12427,12428,12429,12430,12431,12432,12433,12434,12435,null,null,null,null,null,null,null,null,null,null,null,12449,12450,12451,12452,12453,12454,12455,12456,12457,12458,12459,12460,12461,12462,12463,12464,12465,12466,12467,12468,12469,12470,12471,12472,12473,12474,12475,12476,12477,12478,12479,12480,12481,12482,12483,12484,12485,12486,12487,12488,12489,12490,12491,12492,12493,12494,12495,12496,12497,12498,12499,12500,12501,12502,12503,12504,12505,12506,12507,12508,12509,12510,12511,12512,12513,12514,12515,12516,12517,12518,12519,12520,12521,12522,12523,12524,12525,12526,12527,12528,12529,12530,12531,12532,12533,12534,null,null,null,null,null,null,null,null,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,931,932,933,934,935,936,937,null,null,null,null,null,null,null,null,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,963,964,965,966,967,968,969,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,1040,1041,1042,1043,1044,1045,1025,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,1072,1073,1074,1075,1076,1077,1105,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,null,null,null,null,null,null,null,null,null,null,null,null,null,9472,9474,9484,9488,9496,9492,9500,9516,9508,9524,9532,9473,9475,9487,9491,9499,9495,9507,9523,9515,9531,9547,9504,9519,9512,9527,9535,9501,9520,9509,9528,9538,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,9312,9313,9314,9315,9316,9317,9318,9319,9320,9321,9322,9323,9324,9325,9326,9327,9328,9329,9330,9331,8544,8545,8546,8547,8548,8549,8550,8551,8552,8553,null,13129,13076,13090,13133,13080,13095,13059,13110,13137,13143,13069,13094,13091,13099,13130,13115,13212,13213,13214,13198,13199,13252,13217,null,null,null,null,null,null,null,null,13179,12317,12319,8470,13261,8481,12964,12965,12966,12967,12968,12849,12850,12857,13182,13181,13180,8786,8801,8747,8750,8721,8730,8869,8736,8735,8895,8757,8745,8746,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,20124,21782,23043,38463,21696,24859,25384,23030,36898,33909,33564,31312,24746,25569,28197,26093,33894,33446,39925,26771,22311,26017,25201,23451,22992,34427,39156,32098,32190,39822,25110,31903,34999,23433,24245,25353,26263,26696,38343,38797,26447,20197,20234,20301,20381,20553,22258,22839,22996,23041,23561,24799,24847,24944,26131,26885,28858,30031,30064,31227,32173,32239,32963,33806,34915,35586,36949,36986,21307,20117,20133,22495,32946,37057,30959,19968,22769,28322,36920,31282,33576,33419,39983,20801,21360,21693,21729,22240,23035,24341,39154,28139,32996,34093,38498,38512,38560,38907,21515,21491,23431,28879,32701,36802,38632,21359,40284,31418,19985,30867,33276,28198,22040,21764,27421,34074,39995,23013,21417,28006,29916,38287,22082,20113,36939,38642,33615,39180,21473,21942,23344,24433,26144,26355,26628,27704,27891,27945,29787,30408,31310,38964,33521,34907,35424,37613,28082,30123,30410,39365,24742,35585,36234,38322,27022,21421,20870,22290,22576,22852,23476,24310,24616,25513,25588,27839,28436,28814,28948,29017,29141,29503,32257,33398,33489,34199,36960,37467,40219,22633,26044,27738,29989,20985,22830,22885,24448,24540,25276,26106,27178,27431,27572,29579,32705,35158,40236,40206,40644,23713,27798,33659,20740,23627,25014,33222,26742,29281,20057,20474,21368,24681,28201,31311,38899,19979,21270,20206,20309,20285,20385,20339,21152,21487,22025,22799,23233,23478,23521,31185,26247,26524,26550,27468,27827,28779,29634,31117,31166,31292,31623,33457,33499,33540,33655,33775,33747,34662,35506,22057,36008,36838,36942,38686,34442,20420,23784,25105,29273,30011,33253,33469,34558,36032,38597,39187,39381,20171,20250,35299,22238,22602,22730,24315,24555,24618,24724,24674,25040,25106,25296,25913,39745,26214,26800,28023,28784,30028,30342,32117,33445,34809,38283,38542,35997,20977,21182,22806,21683,23475,23830,24936,27010,28079,30861,33995,34903,35442,37799,39608,28012,39336,34521,22435,26623,34510,37390,21123,22151,21508,24275,25313,25785,26684,26680,27579,29554,30906,31339,35226,35282,36203,36611,37101,38307,38548,38761,23398,23731,27005,38989,38990,25499,31520,27179,27263,26806,39949,28511,21106,21917,24688,25324,27963,28167,28369,33883,35088,36676,19988,39993,21494,26907,27194,38788,26666,20828,31427,33970,37340,37772,22107,40232,26658,33541,33841,31909,21000,33477,29926,20094,20355,20896,23506,21002,21208,21223,24059,21914,22570,23014,23436,23448,23515,24178,24185,24739,24863,24931,25022,25563,25954,26577,26707,26874,27454,27475,27735,28450,28567,28485,29872,29976,30435,30475,31487,31649,31777,32233,32566,32752,32925,33382,33694,35251,35532,36011,36996,37969,38291,38289,38306,38501,38867,39208,33304,20024,21547,23736,24012,29609,30284,30524,23721,32747,36107,38593,38929,38996,39000,20225,20238,21361,21916,22120,22522,22855,23305,23492,23696,24076,24190,24524,25582,26426,26071,26082,26399,26827,26820,27231,24112,27589,27671,27773,30079,31048,23395,31232,32000,24509,35215,35352,36020,36215,36556,36637,39138,39438,39740,20096,20605,20736,22931,23452,25135,25216,25836,27450,29344,30097,31047,32681,34811,35516,35696,25516,33738,38816,21513,21507,21931,26708,27224,35440,30759,26485,40653,21364,23458,33050,34384,36870,19992,20037,20167,20241,21450,21560,23470,24339,24613,25937,26429,27714,27762,27875,28792,29699,31350,31406,31496,32026,31998,32102,26087,29275,21435,23621,24040,25298,25312,25369,28192,34394,35377,36317,37624,28417,31142,39770,20136,20139,20140,20379,20384,20689,20807,31478,20849,20982,21332,21281,21375,21483,21932,22659,23777,24375,24394,24623,24656,24685,25375,25945,27211,27841,29378,29421,30703,33016,33029,33288,34126,37111,37857,38911,39255,39514,20208,20957,23597,26241,26989,23616,26354,26997,29577,26704,31873,20677,21220,22343,24062,37670,26020,27427,27453,29748,31105,31165,31563,32202,33465,33740,34943,35167,35641,36817,37329,21535,37504,20061,20534,21477,21306,29399,29590,30697,33510,36527,39366,39368,39378,20855,24858,34398,21936,31354,20598,23507,36935,38533,20018,27355,37351,23633,23624,25496,31391,27795,38772,36705,31402,29066,38536,31874,26647,32368,26705,37740,21234,21531,34219,35347,32676,36557,37089,21350,34952,31041,20418,20670,21009,20804,21843,22317,29674,22411,22865,24418,24452,24693,24950,24935,25001,25522,25658,25964,26223,26690,28179,30054,31293,31995,32076,32153,32331,32619,33550,33610,34509,35336,35427,35686,36605,38938,40335,33464,36814,39912,21127,25119,25731,28608,38553,26689,20625,27424,27770,28500,31348,32080,34880,35363,26376,20214,20537,20518,20581,20860,21048,21091,21927,22287,22533,23244,24314,25010,25080,25331,25458,26908,27177,29309,29356,29486,30740,30831,32121,30476,32937,35211,35609,36066,36562,36963,37749,38522,38997,39443,40568,20803,21407,21427,24187,24358,28187,28304,29572,29694,32067,33335,35328,35578,38480,20046,20491,21476,21628,22266,22993,23396,24049,24235,24359,25144,25925,26543,28246,29392,31946,34996,32929,32993,33776,34382,35463,36328,37431,38599,39015,40723,20116,20114,20237,21320,21577,21566,23087,24460,24481,24735,26791,27278,29786,30849,35486,35492,35703,37264,20062,39881,20132,20348,20399,20505,20502,20809,20844,21151,21177,21246,21402,21475,21521,21518,21897,22353,22434,22909,23380,23389,23439,24037,24039,24055,24184,24195,24218,24247,24344,24658,24908,25239,25304,25511,25915,26114,26179,26356,26477,26657,26775,27083,27743,27946,28009,28207,28317,30002,30343,30828,31295,31968,32005,32024,32094,32177,32789,32771,32943,32945,33108,33167,33322,33618,34892,34913,35611,36002,36092,37066,37237,37489,30783,37628,38308,38477,38917,39321,39640,40251,21083,21163,21495,21512,22741,25335,28640,35946,36703,40633,20811,21051,21578,22269,31296,37239,40288,40658,29508,28425,33136,29969,24573,24794,39592,29403,36796,27492,38915,20170,22256,22372,22718,23130,24680,25031,26127,26118,26681,26801,28151,30165,32058,33390,39746,20123,20304,21449,21766,23919,24038,24046,26619,27801,29811,30722,35408,37782,35039,22352,24231,25387,20661,20652,20877,26368,21705,22622,22971,23472,24425,25165,25505,26685,27507,28168,28797,37319,29312,30741,30758,31085,25998,32048,33756,35009,36617,38555,21092,22312,26448,32618,36001,20916,22338,38442,22586,27018,32948,21682,23822,22524,30869,40442,20316,21066,21643,25662,26152,26388,26613,31364,31574,32034,37679,26716,39853,31545,21273,20874,21047,23519,25334,25774,25830,26413,27578,34217,38609,30352,39894,25420,37638,39851,30399,26194,19977,20632,21442,23665,24808,25746,25955,26719,29158,29642,29987,31639,32386,34453,35715,36059,37240,39184,26028,26283,27531,20181,20180,20282,20351,21050,21496,21490,21987,22235,22763,22987,22985,23039,23376,23629,24066,24107,24535,24605,25351,25903,23388,26031,26045,26088,26525,27490,27515,27663,29509,31049,31169,31992,32025,32043,32930,33026,33267,35222,35422,35433,35430,35468,35566,36039,36060,38604,39164,27503,20107,20284,20365,20816,23383,23546,24904,25345,26178,27425,28363,27835,29246,29885,30164,30913,31034,32780,32819,33258,33940,36766,27728,40575,24335,35672,40235,31482,36600,23437,38635,19971,21489,22519,22833,23241,23460,24713,28287,28422,30142,36074,23455,34048,31712,20594,26612,33437,23649,34122,32286,33294,20889,23556,25448,36198,26012,29038,31038,32023,32773,35613,36554,36974,34503,37034,20511,21242,23610,26451,28796,29237,37196,37320,37675,33509,23490,24369,24825,20027,21462,23432,25163,26417,27530,29417,29664,31278,33131,36259,37202,39318,20754,21463,21610,23551,25480,27193,32172,38656,22234,21454,21608,23447,23601,24030,20462,24833,25342,27954,31168,31179,32066,32333,32722,33261,33311,33936,34886,35186,35728,36468,36655,36913,37195,37228,38598,37276,20160,20303,20805,21313,24467,25102,26580,27713,28171,29539,32294,37325,37507,21460,22809,23487,28113,31069,32302,31899,22654,29087,20986,34899,36848,20426,23803,26149,30636,31459,33308,39423,20934,24490,26092,26991,27529,28147,28310,28516,30462,32020,24033,36981,37255,38918,20966,21021,25152,26257,26329,28186,24246,32210,32626,26360,34223,34295,35576,21161,21465,22899,24207,24464,24661,37604,38500,20663,20767,21213,21280,21319,21484,21736,21830,21809,22039,22888,22974,23100,23477,23558,23567,23569,23578,24196,24202,24288,24432,25215,25220,25307,25484,25463,26119,26124,26157,26230,26494,26786,27167,27189,27836,28040,28169,28248,28988,28966,29031,30151,30465,30813,30977,31077,31216,31456,31505,31911,32057,32918,33750,33931,34121,34909,35059,35359,35388,35412,35443,35937,36062,37284,37478,37758,37912,38556,38808,19978,19976,19998,20055,20887,21104,22478,22580,22732,23330,24120,24773,25854,26465,26454,27972,29366,30067,31331,33976,35698,37304,37664,22065,22516,39166,25325,26893,27542,29165,32340,32887,33394,35302,39135,34645,36785,23611,20280,20449,20405,21767,23072,23517,23529,24515,24910,25391,26032,26187,26862,27035,28024,28145,30003,30137,30495,31070,31206,32051,33251,33455,34218,35242,35386,36523,36763,36914,37341,38663,20154,20161,20995,22645,22764,23563,29978,23613,33102,35338,36805,38499,38765,31525,35535,38920,37218,22259,21416,36887,21561,22402,24101,25512,27700,28810,30561,31883,32736,34928,36930,37204,37648,37656,38543,29790,39620,23815,23913,25968,26530,36264,38619,25454,26441,26905,33733,38935,38592,35070,28548,25722,23544,19990,28716,30045,26159,20932,21046,21218,22995,24449,24615,25104,25919,25972,26143,26228,26866,26646,27491,28165,29298,29983,30427,31934,32854,22768,35069,35199,35488,35475,35531,36893,37266,38738,38745,25993,31246,33030,38587,24109,24796,25114,26021,26132,26512,30707,31309,31821,32318,33034,36012,36196,36321,36447,30889,20999,25305,25509,25666,25240,35373,31363,31680,35500,38634,32118,33292,34633,20185,20808,21315,21344,23459,23554,23574,24029,25126,25159,25776,26643,26676,27849,27973,27927,26579,28508,29006,29053,26059,31359,31661,32218,32330,32680,33146,33307,33337,34214,35438,36046,36341,36984,36983,37549,37521,38275,39854,21069,21892,28472,28982,20840,31109,32341,33203,31950,22092,22609,23720,25514,26366,26365,26970,29401,30095,30094,30990,31062,31199,31895,32032,32068,34311,35380,38459,36961,40736,20711,21109,21452,21474,20489,21930,22766,22863,29245,23435,23652,21277,24803,24819,25436,25475,25407,25531,25805,26089,26361,24035,27085,27133,28437,29157,20105,30185,30456,31379,31967,32207,32156,32865,33609,33624,33900,33980,34299,35013,36208,36865,36973,37783,38684,39442,20687,22679,24974,33235,34101,36104,36896,20419,20596,21063,21363,24687,25417,26463,28204,36275,36895,20439,23646,36042,26063,32154,21330,34966,20854,25539,23384,23403,23562,25613,26449,36956,20182,22810,22826,27760,35409,21822,22549,22949,24816,25171,26561,33333,26965,38464,39364,39464,20307,22534,23550,32784,23729,24111,24453,24608,24907,25140,26367,27888,28382,32974,33151,33492,34955,36024,36864,36910,38538,40667,39899,20195,21488,22823,31532,37261,38988,40441,28381,28711,21331,21828,23429,25176,25246,25299,27810,28655,29730,35351,37944,28609,35582,33592,20967,34552,21482,21481,20294,36948,36784,22890,33073,24061,31466,36799,26842,35895,29432,40008,27197,35504,20025,21336,22022,22374,25285,25506,26086,27470,28129,28251,28845,30701,31471,31658,32187,32829,32966,34507,35477,37723,22243,22727,24382,26029,26262,27264,27573,30007,35527,20516,30693,22320,24347,24677,26234,27744,30196,31258,32622,33268,34584,36933,39347,31689,30044,31481,31569,33988,36880,31209,31378,33590,23265,30528,20013,20210,23449,24544,25277,26172,26609,27880,34411,34935,35387,37198,37619,39376,27159,28710,29482,33511,33879,36015,19969,20806,20939,21899,23541,24086,24115,24193,24340,24373,24427,24500,25074,25361,26274,26397,28526,29266,30010,30522,32884,33081,33144,34678,35519,35548,36229,36339,37530,38263,38914,40165,21189,25431,30452,26389,27784,29645,36035,37806,38515,27941,22684,26894,27084,36861,37786,30171,36890,22618,26626,25524,27131,20291,28460,26584,36795,34086,32180,37716,26943,28528,22378,22775,23340,32044,29226,21514,37347,40372,20141,20302,20572,20597,21059,35998,21576,22564,23450,24093,24213,24237,24311,24351,24716,25269,25402,25552,26799,27712,30855,31118,31243,32224,33351,35330,35558,36420,36883,37048,37165,37336,40718,27877,25688,25826,25973,28404,30340,31515,36969,37841,28346,21746,24505,25764,36685,36845,37444,20856,22635,22825,23637,24215,28155,32399,29980,36028,36578,39003,28857,20253,27583,28593,30000,38651,20814,21520,22581,22615,22956,23648,24466,26007,26460,28193,30331,33759,36077,36884,37117,37709,30757,30778,21162,24230,22303,22900,24594,20498,20826,20908,20941,20992,21776,22612,22616,22871,23445,23798,23947,24764,25237,25645,26481,26691,26812,26847,30423,28120,28271,28059,28783,29128,24403,30168,31095,31561,31572,31570,31958,32113,21040,33891,34153,34276,35342,35588,35910,36367,36867,36879,37913,38518,38957,39472,38360,20685,21205,21516,22530,23566,24999,25758,27934,30643,31461,33012,33796,36947,37509,23776,40199,21311,24471,24499,28060,29305,30563,31167,31716,27602,29420,35501,26627,27233,20984,31361,26932,23626,40182,33515,23493,37193,28702,22136,23663,24775,25958,27788,35930,36929,38931,21585,26311,37389,22856,37027,20869,20045,20970,34201,35598,28760,25466,37707,26978,39348,32260,30071,21335,26976,36575,38627,27741,20108,23612,24336,36841,21250,36049,32905,34425,24319,26085,20083,20837,22914,23615,38894,20219,22922,24525,35469,28641,31152,31074,23527,33905,29483,29105,24180,24565,25467,25754,29123,31896,20035,24316,20043,22492,22178,24745,28611,32013,33021,33075,33215,36786,35223,34468,24052,25226,25773,35207,26487,27874,27966,29750,30772,23110,32629,33453,39340,20467,24259,25309,25490,25943,26479,30403,29260,32972,32954,36649,37197,20493,22521,23186,26757,26995,29028,29437,36023,22770,36064,38506,36889,34687,31204,30695,33833,20271,21093,21338,25293,26575,27850,30333,31636,31893,33334,34180,36843,26333,28448,29190,32283,33707,39361,40614,20989,31665,30834,31672,32903,31560,27368,24161,32908,30033,30048,20843,37474,28300,30330,37271,39658,20240,32624,25244,31567,38309,40169,22138,22617,34532,38588,20276,21028,21322,21453,21467,24070,25644,26001,26495,27710,27726,29256,29359,29677,30036,32321,33324,34281,36009,31684,37318,29033,38930,39151,25405,26217,30058,30436,30928,34115,34542,21290,21329,21542,22915,24199,24444,24754,25161,25209,25259,26000,27604,27852,30130,30382,30865,31192,32203,32631,32933,34987,35513,36027,36991,38750,39131,27147,31800,20633,23614,24494,26503,27608,29749,30473,32654,40763,26570,31255,21305,30091,39661,24422,33181,33777,32920,24380,24517,30050,31558,36924,26727,23019,23195,32016,30334,35628,20469,24426,27161,27703,28418,29922,31080,34920,35413,35961,24287,25551,30149,31186,33495,37672,37618,33948,34541,39981,21697,24428,25996,27996,28693,36007,36051,38971,25935,29942,19981,20184,22496,22827,23142,23500,20904,24067,24220,24598,25206,25975,26023,26222,28014,29238,31526,33104,33178,33433,35676,36000,36070,36212,38428,38468,20398,25771,27494,33310,33889,34154,37096,23553,26963,39080,33914,34135,20239,21103,24489,24133,26381,31119,33145,35079,35206,28149,24343,25173,27832,20175,29289,39826,20998,21563,22132,22707,24996,25198,28954,22894,31881,31966,32027,38640,25991,32862,19993,20341,20853,22592,24163,24179,24330,26564,20006,34109,38281,38491,31859,38913,20731,22721,30294,30887,21029,30629,34065,31622,20559,22793,29255,31687,32232,36794,36820,36941,20415,21193,23081,24321,38829,20445,33303,37610,22275,25429,27497,29995,35036,36628,31298,21215,22675,24917,25098,26286,27597,31807,33769,20515,20472,21253,21574,22577,22857,23453,23792,23791,23849,24214,25265,25447,25918,26041,26379,27861,27873,28921,30770,32299,32990,33459,33804,34028,34562,35090,35370,35914,37030,37586,39165,40179,40300,20047,20129,20621,21078,22346,22952,24125,24536,24537,25151,26292,26395,26576,26834,20882,32033,32938,33192,35584,35980,36031,37502,38450,21536,38956,21271,20693,21340,22696,25778,26420,29287,30566,31302,37350,21187,27809,27526,22528,24140,22868,26412,32763,20961,30406,25705,30952,39764,40635,22475,22969,26151,26522,27598,21737,27097,24149,33180,26517,39850,26622,40018,26717,20134,20451,21448,25273,26411,27819,36804,20397,32365,40639,19975,24930,28288,28459,34067,21619,26410,39749,24051,31637,23724,23494,34588,28234,34001,31252,33032,22937,31885,27665,30496,21209,22818,28961,29279,30683,38695,40289,26891,23167,23064,20901,21517,21629,26126,30431,36855,37528,40180,23018,29277,28357,20813,26825,32191,32236,38754,40634,25720,27169,33538,22916,23391,27611,29467,30450,32178,32791,33945,20786,26408,40665,30446,26466,21247,39173,23588,25147,31870,36016,21839,24758,32011,38272,21249,20063,20918,22812,29242,32822,37326,24357,30690,21380,24441,32004,34220,35379,36493,38742,26611,34222,37971,24841,24840,27833,30290,35565,36664,21807,20305,20778,21191,21451,23461,24189,24736,24962,25558,26377,26586,28263,28044,29494,29495,30001,31056,35029,35480,36938,37009,37109,38596,34701,22805,20104,20313,19982,35465,36671,38928,20653,24188,22934,23481,24248,25562,25594,25793,26332,26954,27096,27915,28342,29076,29992,31407,32650,32768,33865,33993,35201,35617,36362,36965,38525,39178,24958,25233,27442,27779,28020,32716,32764,28096,32645,34746,35064,26469,33713,38972,38647,27931,32097,33853,37226,20081,21365,23888,27396,28651,34253,34349,35239,21033,21519,23653,26446,26792,29702,29827,30178,35023,35041,37324,38626,38520,24459,29575,31435,33870,25504,30053,21129,27969,28316,29705,30041,30827,31890,38534,31452,40845,20406,24942,26053,34396,20102,20142,20698,20001,20940,23534,26009,26753,28092,29471,30274,30637,31260,31975,33391,35538,36988,37327,38517,38936,21147,32209,20523,21400,26519,28107,29136,29747,33256,36650,38563,40023,40607,29792,22593,28057,32047,39006,20196,20278,20363,20919,21169,23994,24604,29618,31036,33491,37428,38583,38646,38666,40599,40802,26278,27508,21015,21155,28872,35010,24265,24651,24976,28451,29001,31806,32244,32879,34030,36899,37676,21570,39791,27347,28809,36034,36335,38706,21172,23105,24266,24324,26391,27004,27028,28010,28431,29282,29436,31725,32769,32894,34635,37070,20845,40595,31108,32907,37682,35542,20525,21644,35441,27498,36036,33031,24785,26528,40434,20121,20120,39952,35435,34241,34152,26880,28286,30871,33109,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,24332,19984,19989,20010,20017,20022,20028,20031,20034,20054,20056,20098,20101,35947,20106,33298,24333,20110,20126,20127,20128,20130,20144,20147,20150,20174,20173,20164,20166,20162,20183,20190,20205,20191,20215,20233,20314,20272,20315,20317,20311,20295,20342,20360,20367,20376,20347,20329,20336,20369,20335,20358,20374,20760,20436,20447,20430,20440,20443,20433,20442,20432,20452,20453,20506,20520,20500,20522,20517,20485,20252,20470,20513,20521,20524,20478,20463,20497,20486,20547,20551,26371,20565,20560,20552,20570,20566,20588,20600,20608,20634,20613,20660,20658,20681,20682,20659,20674,20694,20702,20709,20717,20707,20718,20729,20725,20745,20737,20738,20758,20757,20756,20762,20769,20794,20791,20796,20795,20799,20800,20818,20812,20820,20834,31480,20841,20842,20846,20864,20866,22232,20876,20873,20879,20881,20883,20885,20886,20900,20902,20898,20905,20906,20907,20915,20913,20914,20912,20917,20925,20933,20937,20955,20960,34389,20969,20973,20976,20981,20990,20996,21003,21012,21006,21031,21034,21038,21043,21049,21071,21060,21067,21068,21086,21076,21098,21108,21097,21107,21119,21117,21133,21140,21138,21105,21128,21137,36776,36775,21164,21165,21180,21173,21185,21197,21207,21214,21219,21222,39149,21216,21235,21237,21240,21241,21254,21256,30008,21261,21264,21263,21269,21274,21283,21295,21297,21299,21304,21312,21318,21317,19991,21321,21325,20950,21342,21353,21358,22808,21371,21367,21378,21398,21408,21414,21413,21422,21424,21430,21443,31762,38617,21471,26364,29166,21486,21480,21485,21498,21505,21565,21568,21548,21549,21564,21550,21558,21545,21533,21582,21647,21621,21646,21599,21617,21623,21616,21650,21627,21632,21622,21636,21648,21638,21703,21666,21688,21669,21676,21700,21704,21672,21675,21698,21668,21694,21692,21720,21733,21734,21775,21780,21757,21742,21741,21754,21730,21817,21824,21859,21836,21806,21852,21829,21846,21847,21816,21811,21853,21913,21888,21679,21898,21919,21883,21886,21912,21918,21934,21884,21891,21929,21895,21928,21978,21957,21983,21956,21980,21988,21972,22036,22007,22038,22014,22013,22043,22009,22094,22096,29151,22068,22070,22066,22072,22123,22116,22063,22124,22122,22150,22144,22154,22176,22164,22159,22181,22190,22198,22196,22210,22204,22209,22211,22208,22216,22222,22225,22227,22231,22254,22265,22272,22271,22276,22281,22280,22283,22285,22291,22296,22294,21959,22300,22310,22327,22328,22350,22331,22336,22351,22377,22464,22408,22369,22399,22409,22419,22432,22451,22436,22442,22448,22467,22470,22484,22482,22483,22538,22486,22499,22539,22553,22557,22642,22561,22626,22603,22640,27584,22610,22589,22649,22661,22713,22687,22699,22714,22750,22715,22712,22702,22725,22739,22737,22743,22745,22744,22757,22748,22756,22751,22767,22778,22777,22779,22780,22781,22786,22794,22800,22811,26790,22821,22828,22829,22834,22840,22846,31442,22869,22864,22862,22874,22872,22882,22880,22887,22892,22889,22904,22913,22941,20318,20395,22947,22962,22982,23016,23004,22925,23001,23002,23077,23071,23057,23068,23049,23066,23104,23148,23113,23093,23094,23138,23146,23194,23228,23230,23243,23234,23229,23267,23255,23270,23273,23254,23290,23291,23308,23307,23318,23346,23248,23338,23350,23358,23363,23365,23360,23377,23381,23386,23387,23397,23401,23408,23411,23413,23416,25992,23418,23424,23427,23462,23480,23491,23495,23497,23508,23504,23524,23526,23522,23518,23525,23531,23536,23542,23539,23557,23559,23560,23565,23571,23584,23586,23592,23608,23609,23617,23622,23630,23635,23632,23631,23409,23660,23662,20066,23670,23673,23692,23697,23700,22939,23723,23739,23734,23740,23735,23749,23742,23751,23769,23785,23805,23802,23789,23948,23786,23819,23829,23831,23900,23839,23835,23825,23828,23842,23834,23833,23832,23884,23890,23886,23883,23916,23923,23926,23943,23940,23938,23970,23965,23980,23982,23997,23952,23991,23996,24009,24013,24019,24018,24022,24027,24043,24050,24053,24075,24090,24089,24081,24091,24118,24119,24132,24131,24128,24142,24151,24148,24159,24162,24164,24135,24181,24182,24186,40636,24191,24224,24257,24258,24264,24272,24271,24278,24291,24285,24282,24283,24290,24289,24296,24297,24300,24305,24307,24304,24308,24312,24318,24323,24329,24413,24412,24331,24337,24342,24361,24365,24376,24385,24392,24396,24398,24367,24401,24406,24407,24409,24417,24429,24435,24439,24451,24450,24447,24458,24456,24465,24455,24478,24473,24472,24480,24488,24493,24508,24534,24571,24548,24568,24561,24541,24755,24575,24609,24672,24601,24592,24617,24590,24625,24603,24597,24619,24614,24591,24634,24666,24641,24682,24695,24671,24650,24646,24653,24675,24643,24676,24642,24684,24683,24665,24705,24717,24807,24707,24730,24708,24731,24726,24727,24722,24743,24715,24801,24760,24800,24787,24756,24560,24765,24774,24757,24792,24909,24853,24838,24822,24823,24832,24820,24826,24835,24865,24827,24817,24845,24846,24903,24894,24872,24871,24906,24895,24892,24876,24884,24893,24898,24900,24947,24951,24920,24921,24922,24939,24948,24943,24933,24945,24927,24925,24915,24949,24985,24982,24967,25004,24980,24986,24970,24977,25003,25006,25036,25034,25033,25079,25032,25027,25030,25018,25035,32633,25037,25062,25059,25078,25082,25076,25087,25085,25084,25086,25088,25096,25097,25101,25100,25108,25115,25118,25121,25130,25134,25136,25138,25139,25153,25166,25182,25187,25179,25184,25192,25212,25218,25225,25214,25234,25235,25238,25300,25219,25236,25303,25297,25275,25295,25343,25286,25812,25288,25308,25292,25290,25282,25287,25243,25289,25356,25326,25329,25383,25346,25352,25327,25333,25424,25406,25421,25628,25423,25494,25486,25472,25515,25462,25507,25487,25481,25503,25525,25451,25449,25534,25577,25536,25542,25571,25545,25554,25590,25540,25622,25652,25606,25619,25638,25654,25885,25623,25640,25615,25703,25711,25718,25678,25898,25749,25747,25765,25769,25736,25788,25818,25810,25797,25799,25787,25816,25794,25841,25831,33289,25824,25825,25260,25827,25839,25900,25846,25844,25842,25850,25856,25853,25880,25884,25861,25892,25891,25899,25908,25909,25911,25910,25912,30027,25928,25942,25941,25933,25944,25950,25949,25970,25976,25986,25987,35722,26011,26015,26027,26039,26051,26054,26049,26052,26060,26066,26075,26073,26080,26081,26097,26482,26122,26115,26107,26483,26165,26166,26164,26140,26191,26180,26185,26177,26206,26205,26212,26215,26216,26207,26210,26224,26243,26248,26254,26249,26244,26264,26269,26305,26297,26313,26302,26300,26308,26296,26326,26330,26336,26175,26342,26345,26352,26357,26359,26383,26390,26398,26406,26407,38712,26414,26431,26422,26433,26424,26423,26438,26462,26464,26457,26467,26468,26505,26480,26537,26492,26474,26508,26507,26534,26529,26501,26551,26607,26548,26604,26547,26601,26552,26596,26590,26589,26594,26606,26553,26574,26566,26599,27292,26654,26694,26665,26688,26701,26674,26702,26803,26667,26713,26723,26743,26751,26783,26767,26797,26772,26781,26779,26755,27310,26809,26740,26805,26784,26810,26895,26765,26750,26881,26826,26888,26840,26914,26918,26849,26892,26829,26836,26855,26837,26934,26898,26884,26839,26851,26917,26873,26848,26863,26920,26922,26906,26915,26913,26822,27001,26999,26972,27000,26987,26964,27006,26990,26937,26996,26941,26969,26928,26977,26974,26973,27009,26986,27058,27054,27088,27071,27073,27091,27070,27086,23528,27082,27101,27067,27075,27047,27182,27025,27040,27036,27029,27060,27102,27112,27138,27163,27135,27402,27129,27122,27111,27141,27057,27166,27117,27156,27115,27146,27154,27329,27171,27155,27204,27148,27250,27190,27256,27207,27234,27225,27238,27208,27192,27170,27280,27277,27296,27268,27298,27299,27287,34327,27323,27331,27330,27320,27315,27308,27358,27345,27359,27306,27354,27370,27387,27397,34326,27386,27410,27414,39729,27423,27448,27447,30428,27449,39150,27463,27459,27465,27472,27481,27476,27483,27487,27489,27512,27513,27519,27520,27524,27523,27533,27544,27541,27550,27556,27562,27563,27567,27570,27569,27571,27575,27580,27590,27595,27603,27615,27628,27627,27635,27631,40638,27656,27667,27668,27675,27684,27683,27742,27733,27746,27754,27778,27789,27802,27777,27803,27774,27752,27763,27794,27792,27844,27889,27859,27837,27863,27845,27869,27822,27825,27838,27834,27867,27887,27865,27882,27935,34893,27958,27947,27965,27960,27929,27957,27955,27922,27916,28003,28051,28004,27994,28025,27993,28046,28053,28644,28037,28153,28181,28170,28085,28103,28134,28088,28102,28140,28126,28108,28136,28114,28101,28154,28121,28132,28117,28138,28142,28205,28270,28206,28185,28274,28255,28222,28195,28267,28203,28278,28237,28191,28227,28218,28238,28196,28415,28189,28216,28290,28330,28312,28361,28343,28371,28349,28335,28356,28338,28372,28373,28303,28325,28354,28319,28481,28433,28748,28396,28408,28414,28479,28402,28465,28399,28466,28364,28478,28435,28407,28550,28538,28536,28545,28544,28527,28507,28659,28525,28546,28540,28504,28558,28561,28610,28518,28595,28579,28577,28580,28601,28614,28586,28639,28629,28652,28628,28632,28657,28654,28635,28681,28683,28666,28689,28673,28687,28670,28699,28698,28532,28701,28696,28703,28720,28734,28722,28753,28771,28825,28818,28847,28913,28844,28856,28851,28846,28895,28875,28893,28889,28937,28925,28956,28953,29029,29013,29064,29030,29026,29004,29014,29036,29071,29179,29060,29077,29096,29100,29143,29113,29118,29138,29129,29140,29134,29152,29164,29159,29173,29180,29177,29183,29197,29200,29211,29224,29229,29228,29232,29234,29243,29244,29247,29248,29254,29259,29272,29300,29310,29314,29313,29319,29330,29334,29346,29351,29369,29362,29379,29382,29380,29390,29394,29410,29408,29409,29433,29431,20495,29463,29450,29468,29462,29469,29492,29487,29481,29477,29502,29518,29519,40664,29527,29546,29544,29552,29560,29557,29563,29562,29640,29619,29646,29627,29632,29669,29678,29662,29858,29701,29807,29733,29688,29746,29754,29781,29759,29791,29785,29761,29788,29801,29808,29795,29802,29814,29822,29835,29854,29863,29898,29903,29908,29681,29920,29923,29927,29929,29934,29938,29936,29937,29944,29943,29956,29955,29957,29964,29966,29965,29973,29971,29982,29990,29996,30012,30020,30029,30026,30025,30043,30022,30042,30057,30052,30055,30059,30061,30072,30070,30086,30087,30068,30090,30089,30082,30100,30106,30109,30117,30115,30146,30131,30147,30133,30141,30136,30140,30129,30157,30154,30162,30169,30179,30174,30206,30207,30204,30209,30192,30202,30194,30195,30219,30221,30217,30239,30247,30240,30241,30242,30244,30260,30256,30267,30279,30280,30278,30300,30296,30305,30306,30312,30313,30314,30311,30316,30320,30322,30326,30328,30332,30336,30339,30344,30347,30350,30358,30355,30361,30362,30384,30388,30392,30393,30394,30402,30413,30422,30418,30430,30433,30437,30439,30442,34351,30459,30472,30471,30468,30505,30500,30494,30501,30502,30491,30519,30520,30535,30554,30568,30571,30555,30565,30591,30590,30585,30606,30603,30609,30624,30622,30640,30646,30649,30655,30652,30653,30651,30663,30669,30679,30682,30684,30691,30702,30716,30732,30738,31014,30752,31018,30789,30862,30836,30854,30844,30874,30860,30883,30901,30890,30895,30929,30918,30923,30932,30910,30908,30917,30922,30956,30951,30938,30973,30964,30983,30994,30993,31001,31020,31019,31040,31072,31063,31071,31066,31061,31059,31098,31103,31114,31133,31143,40779,31146,31150,31155,31161,31162,31177,31189,31207,31212,31201,31203,31240,31245,31256,31257,31264,31263,31104,31281,31291,31294,31287,31299,31319,31305,31329,31330,31337,40861,31344,31353,31357,31368,31383,31381,31384,31382,31401,31432,31408,31414,31429,31428,31423,36995,31431,31434,31437,31439,31445,31443,31449,31450,31453,31457,31458,31462,31469,31472,31490,31503,31498,31494,31539,31512,31513,31518,31541,31528,31542,31568,31610,31492,31565,31499,31564,31557,31605,31589,31604,31591,31600,31601,31596,31598,31645,31640,31647,31629,31644,31642,31627,31634,31631,31581,31641,31691,31681,31692,31695,31668,31686,31709,31721,31761,31764,31718,31717,31840,31744,31751,31763,31731,31735,31767,31757,31734,31779,31783,31786,31775,31799,31787,31805,31820,31811,31828,31823,31808,31824,31832,31839,31844,31830,31845,31852,31861,31875,31888,31908,31917,31906,31915,31905,31912,31923,31922,31921,31918,31929,31933,31936,31941,31938,31960,31954,31964,31970,39739,31983,31986,31988,31990,31994,32006,32002,32028,32021,32010,32069,32075,32046,32050,32063,32053,32070,32115,32086,32078,32114,32104,32110,32079,32099,32147,32137,32091,32143,32125,32155,32186,32174,32163,32181,32199,32189,32171,32317,32162,32175,32220,32184,32159,32176,32216,32221,32228,32222,32251,32242,32225,32261,32266,32291,32289,32274,32305,32287,32265,32267,32290,32326,32358,32315,32309,32313,32323,32311,32306,32314,32359,32349,32342,32350,32345,32346,32377,32362,32361,32380,32379,32387,32213,32381,36782,32383,32392,32393,32396,32402,32400,32403,32404,32406,32398,32411,32412,32568,32570,32581,32588,32589,32590,32592,32593,32597,32596,32600,32607,32608,32616,32617,32615,32632,32642,32646,32643,32648,32647,32652,32660,32670,32669,32666,32675,32687,32690,32697,32686,32694,32696,35697,32709,32710,32714,32725,32724,32737,32742,32745,32755,32761,39132,32774,32772,32779,32786,32792,32793,32796,32801,32808,32831,32827,32842,32838,32850,32856,32858,32863,32866,32872,32883,32882,32880,32886,32889,32893,32895,32900,32902,32901,32923,32915,32922,32941,20880,32940,32987,32997,32985,32989,32964,32986,32982,33033,33007,33009,33051,33065,33059,33071,33099,38539,33094,33086,33107,33105,33020,33137,33134,33125,33126,33140,33155,33160,33162,33152,33154,33184,33173,33188,33187,33119,33171,33193,33200,33205,33214,33208,33213,33216,33218,33210,33225,33229,33233,33241,33240,33224,33242,33247,33248,33255,33274,33275,33278,33281,33282,33285,33287,33290,33293,33296,33302,33321,33323,33336,33331,33344,33369,33368,33373,33370,33375,33380,33378,33384,33386,33387,33326,33393,33399,33400,33406,33421,33426,33451,33439,33467,33452,33505,33507,33503,33490,33524,33523,33530,33683,33539,33531,33529,33502,33542,33500,33545,33497,33589,33588,33558,33586,33585,33600,33593,33616,33605,33583,33579,33559,33560,33669,33690,33706,33695,33698,33686,33571,33678,33671,33674,33660,33717,33651,33653,33696,33673,33704,33780,33811,33771,33742,33789,33795,33752,33803,33729,33783,33799,33760,33778,33805,33826,33824,33725,33848,34054,33787,33901,33834,33852,34138,33924,33911,33899,33965,33902,33922,33897,33862,33836,33903,33913,33845,33994,33890,33977,33983,33951,34009,33997,33979,34010,34000,33985,33990,34006,33953,34081,34047,34036,34071,34072,34092,34079,34069,34068,34044,34112,34147,34136,34120,34113,34306,34123,34133,34176,34212,34184,34193,34186,34216,34157,34196,34203,34282,34183,34204,34167,34174,34192,34249,34234,34255,34233,34256,34261,34269,34277,34268,34297,34314,34323,34315,34302,34298,34310,34338,34330,34352,34367,34381,20053,34388,34399,34407,34417,34451,34467,34473,34474,34443,34444,34486,34479,34500,34502,34480,34505,34851,34475,34516,34526,34537,34540,34527,34523,34543,34578,34566,34568,34560,34563,34555,34577,34569,34573,34553,34570,34612,34623,34615,34619,34597,34601,34586,34656,34655,34680,34636,34638,34676,34647,34664,34670,34649,34643,34659,34666,34821,34722,34719,34690,34735,34763,34749,34752,34768,38614,34731,34756,34739,34759,34758,34747,34799,34802,34784,34831,34829,34814,34806,34807,34830,34770,34833,34838,34837,34850,34849,34865,34870,34873,34855,34875,34884,34882,34898,34905,34910,34914,34923,34945,34942,34974,34933,34941,34997,34930,34946,34967,34962,34990,34969,34978,34957,34980,34992,35007,34993,35011,35012,35028,35032,35033,35037,35065,35074,35068,35060,35048,35058,35076,35084,35082,35091,35139,35102,35109,35114,35115,35137,35140,35131,35126,35128,35148,35101,35168,35166,35174,35172,35181,35178,35183,35188,35191,35198,35203,35208,35210,35219,35224,35233,35241,35238,35244,35247,35250,35258,35261,35263,35264,35290,35292,35293,35303,35316,35320,35331,35350,35344,35340,35355,35357,35365,35382,35393,35419,35410,35398,35400,35452,35437,35436,35426,35461,35458,35460,35496,35489,35473,35493,35494,35482,35491,35524,35533,35522,35546,35563,35571,35559,35556,35569,35604,35552,35554,35575,35550,35547,35596,35591,35610,35553,35606,35600,35607,35616,35635,38827,35622,35627,35646,35624,35649,35660,35663,35662,35657,35670,35675,35674,35691,35679,35692,35695,35700,35709,35712,35724,35726,35730,35731,35734,35737,35738,35898,35905,35903,35912,35916,35918,35920,35925,35938,35948,35960,35962,35970,35977,35973,35978,35981,35982,35988,35964,35992,25117,36013,36010,36029,36018,36019,36014,36022,36040,36033,36068,36067,36058,36093,36090,36091,36100,36101,36106,36103,36111,36109,36112,40782,36115,36045,36116,36118,36199,36205,36209,36211,36225,36249,36290,36286,36282,36303,36314,36310,36300,36315,36299,36330,36331,36319,36323,36348,36360,36361,36351,36381,36382,36368,36383,36418,36405,36400,36404,36426,36423,36425,36428,36432,36424,36441,36452,36448,36394,36451,36437,36470,36466,36476,36481,36487,36485,36484,36491,36490,36499,36497,36500,36505,36522,36513,36524,36528,36550,36529,36542,36549,36552,36555,36571,36579,36604,36603,36587,36606,36618,36613,36629,36626,36633,36627,36636,36639,36635,36620,36646,36659,36667,36665,36677,36674,36670,36684,36681,36678,36686,36695,36700,36706,36707,36708,36764,36767,36771,36781,36783,36791,36826,36837,36834,36842,36847,36999,36852,36869,36857,36858,36881,36885,36897,36877,36894,36886,36875,36903,36918,36917,36921,36856,36943,36944,36945,36946,36878,36937,36926,36950,36952,36958,36968,36975,36982,38568,36978,36994,36989,36993,36992,37002,37001,37007,37032,37039,37041,37045,37090,37092,25160,37083,37122,37138,37145,37170,37168,37194,37206,37208,37219,37221,37225,37235,37234,37259,37257,37250,37282,37291,37295,37290,37301,37300,37306,37312,37313,37321,37323,37328,37334,37343,37345,37339,37372,37365,37366,37406,37375,37396,37420,37397,37393,37470,37463,37445,37449,37476,37448,37525,37439,37451,37456,37532,37526,37523,37531,37466,37583,37561,37559,37609,37647,37626,37700,37678,37657,37666,37658,37667,37690,37685,37691,37724,37728,37756,37742,37718,37808,37804,37805,37780,37817,37846,37847,37864,37861,37848,37827,37853,37840,37832,37860,37914,37908,37907,37891,37895,37904,37942,37931,37941,37921,37946,37953,37970,37956,37979,37984,37986,37982,37994,37417,38000,38005,38007,38013,37978,38012,38014,38017,38015,38274,38279,38282,38292,38294,38296,38297,38304,38312,38311,38317,38332,38331,38329,38334,38346,28662,38339,38349,38348,38357,38356,38358,38364,38369,38373,38370,38433,38440,38446,38447,38466,38476,38479,38475,38519,38492,38494,38493,38495,38502,38514,38508,38541,38552,38549,38551,38570,38567,38577,38578,38576,38580,38582,38584,38585,38606,38603,38601,38605,35149,38620,38669,38613,38649,38660,38662,38664,38675,38670,38673,38671,38678,38681,38692,38698,38704,38713,38717,38718,38724,38726,38728,38722,38729,38748,38752,38756,38758,38760,21202,38763,38769,38777,38789,38780,38785,38778,38790,38795,38799,38800,38812,38824,38822,38819,38835,38836,38851,38854,38856,38859,38876,38893,40783,38898,31455,38902,38901,38927,38924,38968,38948,38945,38967,38973,38982,38991,38987,39019,39023,39024,39025,39028,39027,39082,39087,39089,39094,39108,39107,39110,39145,39147,39171,39177,39186,39188,39192,39201,39197,39198,39204,39200,39212,39214,39229,39230,39234,39241,39237,39248,39243,39249,39250,39244,39253,39319,39320,39333,39341,39342,39356,39391,39387,39389,39384,39377,39405,39406,39409,39410,39419,39416,39425,39439,39429,39394,39449,39467,39479,39493,39490,39488,39491,39486,39509,39501,39515,39511,39519,39522,39525,39524,39529,39531,39530,39597,39600,39612,39616,39631,39633,39635,39636,39646,39647,39650,39651,39654,39663,39659,39662,39668,39665,39671,39675,39686,39704,39706,39711,39714,39715,39717,39719,39720,39721,39722,39726,39727,39730,39748,39747,39759,39757,39758,39761,39768,39796,39827,39811,39825,39830,39831,39839,39840,39848,39860,39872,39882,39865,39878,39887,39889,39890,39907,39906,39908,39892,39905,39994,39922,39921,39920,39957,39956,39945,39955,39948,39942,39944,39954,39946,39940,39982,39963,39973,39972,39969,39984,40007,39986,40006,39998,40026,40032,40039,40054,40056,40167,40172,40176,40201,40200,40171,40195,40198,40234,40230,40367,40227,40223,40260,40213,40210,40257,40255,40254,40262,40264,40285,40286,40292,40273,40272,40281,40306,40329,40327,40363,40303,40314,40346,40356,40361,40370,40388,40385,40379,40376,40378,40390,40399,40386,40409,40403,40440,40422,40429,40431,40445,40474,40475,40478,40565,40569,40573,40577,40584,40587,40588,40594,40597,40593,40605,40613,40617,40632,40618,40621,38753,40652,40654,40655,40656,40660,40668,40670,40669,40672,40677,40680,40687,40692,40694,40695,40697,40699,40700,40701,40711,40712,30391,40725,40737,40748,40766,40778,40786,40788,40803,40799,40800,40801,40806,40807,40812,40810,40823,40818,40822,40853,40860,40864,22575,27079,36953,29796,20956,29081,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,32394,35100,37704,37512,34012,20425,28859,26161,26824,37625,26363,24389,20008,20193,20220,20224,20227,20281,20310,20370,20362,20378,20372,20429,20544,20514,20479,20510,20550,20592,20546,20628,20724,20696,20810,20836,20893,20926,20972,21013,21148,21158,21184,21211,21248,21255,21284,21362,21395,21426,21469,64014,21660,21642,21673,21759,21894,22361,22373,22444,22472,22471,64015,64016,22686,22706,22795,22867,22875,22877,22883,22948,22970,23382,23488,29999,23512,23532,23582,23718,23738,23797,23847,23891,64017,23874,23917,23992,23993,24016,24353,24372,24423,24503,24542,24669,24709,24714,24798,24789,24864,24818,24849,24887,24880,24984,25107,25254,25589,25696,25757,25806,25934,26112,26133,26171,26121,26158,26142,26148,26213,26199,26201,64018,26227,26265,26272,26290,26303,26362,26382,63785,26470,26555,26706,26560,26625,26692,26831,64019,26984,64020,27032,27106,27184,27243,27206,27251,27262,27362,27364,27606,27711,27740,27782,27759,27866,27908,28039,28015,28054,28076,28111,28152,28146,28156,28217,28252,28199,28220,28351,28552,28597,28661,28677,28679,28712,28805,28843,28943,28932,29020,28998,28999,64021,29121,29182,29361,29374,29476,64022,29559,29629,29641,29654,29667,29650,29703,29685,29734,29738,29737,29742,29794,29833,29855,29953,30063,30338,30364,30366,30363,30374,64023,30534,21167,30753,30798,30820,30842,31024,64024,64025,64026,31124,64027,31131,31441,31463,64028,31467,31646,64029,32072,32092,32183,32160,32214,32338,32583,32673,64030,33537,33634,33663,33735,33782,33864,33972,34131,34137,34155,64031,34224,64032,64033,34823,35061,35346,35383,35449,35495,35518,35551,64034,35574,35667,35711,36080,36084,36114,36214,64035,36559,64036,64037,36967,37086,64038,37141,37159,37338,37335,37342,37357,37358,37348,37349,37382,37392,37386,37434,37440,37436,37454,37465,37457,37433,37479,37543,37495,37496,37607,37591,37593,37584,64039,37589,37600,37587,37669,37665,37627,64040,37662,37631,37661,37634,37744,37719,37796,37830,37854,37880,37937,37957,37960,38290,63964,64041,38557,38575,38707,38715,38723,38733,38735,38737,38741,38999,39013,64042,64043,39207,64044,39326,39502,39641,39644,39797,39794,39823,39857,39867,39936,40304,40299,64045,40473,40657,null,null,8560,8561,8562,8563,8564,8565,8566,8567,8568,8569,65506,65508,65287,65282,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,8560,8561,8562,8563,8564,8565,8566,8567,8568,8569,8544,8545,8546,8547,8548,8549,8550,8551,8552,8553,65506,65508,65287,65282,12849,8470,8481,8757,32394,35100,37704,37512,34012,20425,28859,26161,26824,37625,26363,24389,20008,20193,20220,20224,20227,20281,20310,20370,20362,20378,20372,20429,20544,20514,20479,20510,20550,20592,20546,20628,20724,20696,20810,20836,20893,20926,20972,21013,21148,21158,21184,21211,21248,21255,21284,21362,21395,21426,21469,64014,21660,21642,21673,21759,21894,22361,22373,22444,22472,22471,64015,64016,22686,22706,22795,22867,22875,22877,22883,22948,22970,23382,23488,29999,23512,23532,23582,23718,23738,23797,23847,23891,64017,23874,23917,23992,23993,24016,24353,24372,24423,24503,24542,24669,24709,24714,24798,24789,24864,24818,24849,24887,24880,24984,25107,25254,25589,25696,25757,25806,25934,26112,26133,26171,26121,26158,26142,26148,26213,26199,26201,64018,26227,26265,26272,26290,26303,26362,26382,63785,26470,26555,26706,26560,26625,26692,26831,64019,26984,64020,27032,27106,27184,27243,27206,27251,27262,27362,27364,27606,27711,27740,27782,27759,27866,27908,28039,28015,28054,28076,28111,28152,28146,28156,28217,28252,28199,28220,28351,28552,28597,28661,28677,28679,28712,28805,28843,28943,28932,29020,28998,28999,64021,29121,29182,29361,29374,29476,64022,29559,29629,29641,29654,29667,29650,29703,29685,29734,29738,29737,29742,29794,29833,29855,29953,30063,30338,30364,30366,30363,30374,64023,30534,21167,30753,30798,30820,30842,31024,64024,64025,64026,31124,64027,31131,31441,31463,64028,31467,31646,64029,32072,32092,32183,32160,32214,32338,32583,32673,64030,33537,33634,33663,33735,33782,33864,33972,34131,34137,34155,64031,34224,64032,64033,34823,35061,35346,35383,35449,35495,35518,35551,64034,35574,35667,35711,36080,36084,36114,36214,64035,36559,64036,64037,36967,37086,64038,37141,37159,37338,37335,37342,37357,37358,37348,37349,37382,37392,37386,37434,37440,37436,37454,37465,37457,37433,37479,37543,37495,37496,37607,37591,37593,37584,64039,37589,37600,37587,37669,37665,37627,64040,37662,37631,37661,37634,37744,37719,37796,37830,37854,37880,37937,37957,37960,38290,63964,64041,38557,38575,38707,38715,38723,38733,38735,38737,38741,38999,39013,64042,64043,39207,64044,39326,39502,39641,39644,39797,39794,39823,39857,39867,39936,40304,40299,64045,40473,40657,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null}; +int jis_decoder::m_jis0212_index[] = {null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,728,711,184,729,733,175,731,730,65374,900,901,null,null,null,null,null,null,null,null,161,166,191,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,186,170,169,174,8482,164,8470,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,902,904,905,906,938,null,908,null,910,939,null,911,null,null,null,null,940,941,942,943,970,912,972,962,973,971,944,974,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,1026,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1038,1039,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,1106,1107,1108,1109,1110,1111,1112,1113,1114,1115,1116,1118,1119,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,198,272,null,294,null,306,null,321,319,null,330,216,338,null,358,222,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,230,273,240,295,305,307,312,322,320,329,331,248,339,223,359,254,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,193,192,196,194,258,461,256,260,197,195,262,264,268,199,266,270,201,200,203,202,282,278,274,280,null,284,286,290,288,292,205,204,207,206,463,304,298,302,296,308,310,313,317,315,323,327,325,209,211,210,214,212,465,336,332,213,340,344,342,346,348,352,350,356,354,218,217,220,219,364,467,368,362,370,366,360,471,475,473,469,372,221,376,374,377,381,379,null,null,null,null,null,null,null,225,224,228,226,259,462,257,261,229,227,263,265,269,231,267,271,233,232,235,234,283,279,275,281,501,285,287,null,289,293,237,236,239,238,464,null,299,303,297,309,311,314,318,316,324,328,326,241,243,242,246,244,466,337,333,245,341,345,343,347,349,353,351,357,355,250,249,252,251,365,468,369,363,371,367,361,472,476,474,470,373,253,255,375,378,382,380,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,19970,19972,19973,19980,19986,19999,20003,20004,20008,20011,20014,20015,20016,20021,20032,20033,20036,20039,20049,20058,20060,20067,20072,20073,20084,20085,20089,20095,20109,20118,20119,20125,20143,20153,20163,20176,20186,20187,20192,20193,20194,20200,20207,20209,20211,20213,20221,20222,20223,20224,20226,20227,20232,20235,20236,20242,20245,20246,20247,20249,20270,20273,20320,20275,20277,20279,20281,20283,20286,20288,20290,20296,20297,20299,20300,20306,20308,20310,20312,20319,20323,20330,20332,20334,20337,20343,20344,20345,20346,20349,20350,20353,20354,20356,20357,20361,20362,20364,20366,20368,20370,20371,20372,20375,20377,20378,20382,20383,20402,20407,20409,20411,20412,20413,20414,20416,20417,20421,20422,20424,20425,20427,20428,20429,20431,20434,20444,20448,20450,20464,20466,20476,20477,20479,20480,20481,20484,20487,20490,20492,20494,20496,20499,20503,20504,20507,20508,20509,20510,20514,20519,20526,20528,20530,20531,20533,20544,20545,20546,20549,20550,20554,20556,20558,20561,20562,20563,20567,20569,20575,20576,20578,20579,20582,20583,20586,20589,20592,20593,20539,20609,20611,20612,20614,20618,20622,20623,20624,20626,20627,20628,20630,20635,20636,20638,20639,20640,20641,20642,20650,20655,20656,20665,20666,20669,20672,20675,20676,20679,20684,20686,20688,20691,20692,20696,20700,20701,20703,20706,20708,20710,20712,20713,20719,20721,20726,20730,20734,20739,20742,20743,20744,20747,20748,20749,20750,20722,20752,20759,20761,20763,20764,20765,20766,20771,20775,20776,20780,20781,20783,20785,20787,20788,20789,20792,20793,20802,20810,20815,20819,20821,20823,20824,20831,20836,20838,20862,20867,20868,20875,20878,20888,20893,20897,20899,20909,20920,20922,20924,20926,20927,20930,20936,20943,20945,20946,20947,20949,20952,20958,20962,20965,20974,20978,20979,20980,20983,20993,20994,20997,21010,21011,21013,21014,21016,21026,21032,21041,21042,21045,21052,21061,21065,21077,21079,21080,21082,21084,21087,21088,21089,21094,21102,21111,21112,21113,21120,21122,21125,21130,21132,21139,21141,21142,21143,21144,21146,21148,21156,21157,21158,21159,21167,21168,21174,21175,21176,21178,21179,21181,21184,21188,21190,21192,21196,21199,21201,21204,21206,21211,21212,21217,21221,21224,21225,21226,21228,21232,21233,21236,21238,21239,21248,21251,21258,21259,21260,21265,21267,21272,21275,21276,21278,21279,21285,21287,21288,21289,21291,21292,21293,21296,21298,21301,21308,21309,21310,21314,21324,21323,21337,21339,21345,21347,21349,21356,21357,21362,21369,21374,21379,21383,21384,21390,21395,21396,21401,21405,21409,21412,21418,21419,21423,21426,21428,21429,21431,21432,21434,21437,21440,21445,21455,21458,21459,21461,21466,21469,21470,21472,21478,21479,21493,21506,21523,21530,21537,21543,21544,21546,21551,21553,21556,21557,21571,21572,21575,21581,21583,21598,21602,21604,21606,21607,21609,21611,21613,21614,21620,21631,21633,21635,21637,21640,21641,21645,21649,21653,21654,21660,21663,21665,21670,21671,21673,21674,21677,21678,21681,21687,21689,21690,21691,21695,21702,21706,21709,21710,21728,21738,21740,21743,21750,21756,21758,21759,21760,21761,21765,21768,21769,21772,21773,21774,21781,21802,21803,21810,21813,21814,21819,21820,21821,21825,21831,21833,21834,21837,21840,21841,21848,21850,21851,21854,21856,21857,21860,21862,21887,21889,21890,21894,21896,21902,21903,21905,21906,21907,21908,21911,21923,21924,21933,21938,21951,21953,21955,21958,21961,21963,21964,21966,21969,21970,21971,21975,21976,21979,21982,21986,21993,22006,22015,22021,22024,22026,22029,22030,22031,22032,22033,22034,22041,22060,22064,22067,22069,22071,22073,22075,22076,22077,22079,22080,22081,22083,22084,22086,22089,22091,22093,22095,22100,22110,22112,22113,22114,22115,22118,22121,22125,22127,22129,22130,22133,22148,22149,22152,22155,22156,22165,22169,22170,22173,22174,22175,22182,22183,22184,22185,22187,22188,22189,22193,22195,22199,22206,22213,22217,22218,22219,22223,22224,22220,22221,22233,22236,22237,22239,22241,22244,22245,22246,22247,22248,22257,22251,22253,22262,22263,22273,22274,22279,22282,22284,22289,22293,22298,22299,22301,22304,22306,22307,22308,22309,22313,22314,22316,22318,22319,22323,22324,22333,22334,22335,22341,22342,22348,22349,22354,22370,22373,22375,22376,22379,22381,22382,22383,22384,22385,22387,22388,22389,22391,22393,22394,22395,22396,22398,22401,22403,22412,22420,22423,22425,22426,22428,22429,22430,22431,22433,22421,22439,22440,22441,22444,22456,22461,22471,22472,22476,22479,22485,22493,22494,22500,22502,22503,22505,22509,22512,22517,22518,22520,22525,22526,22527,22531,22532,22536,22537,22497,22540,22541,22555,22558,22559,22560,22566,22567,22573,22578,22585,22591,22601,22604,22605,22607,22608,22613,22623,22625,22628,22631,22632,22648,22652,22655,22656,22657,22663,22664,22665,22666,22668,22669,22671,22672,22676,22678,22685,22688,22689,22690,22694,22697,22705,22706,22724,22716,22722,22728,22733,22734,22736,22738,22740,22742,22746,22749,22753,22754,22761,22771,22789,22790,22795,22796,22802,22803,22804,34369,22813,22817,22819,22820,22824,22831,22832,22835,22837,22838,22847,22851,22854,22866,22867,22873,22875,22877,22878,22879,22881,22883,22891,22893,22895,22898,22901,22902,22905,22907,22908,22923,22924,22926,22930,22933,22935,22943,22948,22951,22957,22958,22959,22960,22963,22967,22970,22972,22977,22979,22980,22984,22986,22989,22994,23005,23006,23007,23011,23012,23015,23022,23023,23025,23026,23028,23031,23040,23044,23052,23053,23054,23058,23059,23070,23075,23076,23079,23080,23082,23085,23088,23108,23109,23111,23112,23116,23120,23125,23134,23139,23141,23143,23149,23159,23162,23163,23166,23179,23184,23187,23190,23193,23196,23198,23199,23200,23202,23207,23212,23217,23218,23219,23221,23224,23226,23227,23231,23236,23238,23240,23247,23258,23260,23264,23269,23274,23278,23285,23286,23293,23296,23297,23304,23319,23348,23321,23323,23325,23329,23333,23341,23352,23361,23371,23372,23378,23382,23390,23400,23406,23407,23420,23421,23422,23423,23425,23428,23430,23434,23438,23440,23441,23443,23444,23446,23464,23465,23468,23469,23471,23473,23474,23479,23482,23484,23488,23489,23501,23503,23510,23511,23512,23513,23514,23520,23535,23537,23540,23549,23564,23575,23582,23583,23587,23590,23593,23595,23596,23598,23600,23602,23605,23606,23641,23642,23644,23650,23651,23655,23656,23657,23661,23664,23668,23669,23674,23675,23676,23677,23687,23688,23690,23695,23698,23709,23711,23712,23714,23715,23718,23722,23730,23732,23733,23738,23753,23755,23762,23773,23767,23790,23793,23794,23796,23809,23814,23821,23826,23851,23843,23844,23846,23847,23857,23860,23865,23869,23871,23874,23875,23878,23880,23893,23889,23897,23882,23903,23904,23905,23906,23908,23914,23917,23920,23929,23930,23934,23935,23937,23939,23944,23946,23954,23955,23956,23957,23961,23963,23967,23968,23975,23979,23984,23988,23992,23993,24003,24007,24011,24016,24014,24024,24025,24032,24036,24041,24056,24057,24064,24071,24077,24082,24084,24085,24088,24095,24096,24110,24104,24114,24117,24126,24139,24144,24137,24145,24150,24152,24155,24156,24158,24168,24170,24171,24172,24173,24174,24176,24192,24203,24206,24226,24228,24229,24232,24234,24236,24241,24243,24253,24254,24255,24262,24268,24267,24270,24273,24274,24276,24277,24284,24286,24293,24299,24322,24326,24327,24328,24334,24345,24348,24349,24353,24354,24355,24356,24360,24363,24364,24366,24368,24372,24374,24379,24381,24383,24384,24388,24389,24391,24397,24400,24404,24408,24411,24416,24419,24420,24423,24431,24434,24436,24437,24440,24442,24445,24446,24457,24461,24463,24470,24476,24477,24482,24487,24491,24484,24492,24495,24496,24497,24504,24516,24519,24520,24521,24523,24528,24529,24530,24531,24532,24542,24545,24546,24552,24553,24554,24556,24557,24558,24559,24562,24563,24566,24570,24572,24583,24586,24589,24595,24596,24599,24600,24602,24607,24612,24621,24627,24629,24640,24647,24648,24649,24652,24657,24660,24662,24663,24669,24673,24679,24689,24702,24703,24706,24710,24712,24714,24718,24721,24723,24725,24728,24733,24734,24738,24740,24741,24744,24752,24753,24759,24763,24766,24770,24772,24776,24777,24778,24779,24782,24783,24788,24789,24793,24795,24797,24798,24802,24805,24818,24821,24824,24828,24829,24834,24839,24842,24844,24848,24849,24850,24851,24852,24854,24855,24857,24860,24862,24866,24874,24875,24880,24881,24885,24886,24887,24889,24897,24901,24902,24905,24926,24928,24940,24946,24952,24955,24956,24959,24960,24961,24963,24964,24971,24973,24978,24979,24983,24984,24988,24989,24991,24992,24997,25000,25002,25005,25016,25017,25020,25024,25025,25026,25038,25039,25045,25052,25053,25054,25055,25057,25058,25063,25065,25061,25068,25069,25071,25089,25091,25092,25095,25107,25109,25116,25120,25122,25123,25127,25129,25131,25145,25149,25154,25155,25156,25158,25164,25168,25169,25170,25172,25174,25178,25180,25188,25197,25199,25203,25210,25213,25229,25230,25231,25232,25254,25256,25267,25270,25271,25274,25278,25279,25284,25294,25301,25302,25306,25322,25330,25332,25340,25341,25347,25348,25354,25355,25357,25360,25363,25366,25368,25385,25386,25389,25397,25398,25401,25404,25409,25410,25411,25412,25414,25418,25419,25422,25426,25427,25428,25432,25435,25445,25446,25452,25453,25457,25460,25461,25464,25468,25469,25471,25474,25476,25479,25482,25488,25492,25493,25497,25498,25502,25508,25510,25517,25518,25519,25533,25537,25541,25544,25550,25553,25555,25556,25557,25564,25568,25573,25578,25580,25586,25587,25589,25592,25593,25609,25610,25616,25618,25620,25624,25630,25632,25634,25636,25637,25641,25642,25647,25648,25653,25661,25663,25675,25679,25681,25682,25683,25684,25690,25691,25692,25693,25695,25696,25697,25699,25709,25715,25716,25723,25725,25733,25735,25743,25744,25745,25752,25753,25755,25757,25759,25761,25763,25766,25768,25772,25779,25789,25790,25791,25796,25801,25802,25803,25804,25806,25808,25809,25813,25815,25828,25829,25833,25834,25837,25840,25845,25847,25851,25855,25857,25860,25864,25865,25866,25871,25875,25876,25878,25881,25883,25886,25887,25890,25894,25897,25902,25905,25914,25916,25917,25923,25927,25929,25936,25938,25940,25951,25952,25959,25963,25978,25981,25985,25989,25994,26002,26005,26008,26013,26016,26019,26022,26030,26034,26035,26036,26047,26050,26056,26057,26062,26064,26068,26070,26072,26079,26096,26098,26100,26101,26105,26110,26111,26112,26116,26120,26121,26125,26129,26130,26133,26134,26141,26142,26145,26146,26147,26148,26150,26153,26154,26155,26156,26158,26160,26161,26163,26169,26167,26176,26181,26182,26186,26188,26193,26190,26199,26200,26201,26203,26204,26208,26209,26363,26218,26219,26220,26238,26227,26229,26239,26231,26232,26233,26235,26240,26236,26251,26252,26253,26256,26258,26265,26266,26267,26268,26271,26272,26276,26285,26289,26290,26293,26299,26303,26304,26306,26307,26312,26316,26318,26319,26324,26331,26335,26344,26347,26348,26350,26362,26373,26375,26382,26387,26393,26396,26400,26402,26419,26430,26437,26439,26440,26444,26452,26453,26461,26470,26476,26478,26484,26486,26491,26497,26500,26510,26511,26513,26515,26518,26520,26521,26523,26544,26545,26546,26549,26555,26556,26557,26617,26560,26562,26563,26565,26568,26569,26578,26583,26585,26588,26593,26598,26608,26610,26614,26615,26706,26644,26649,26653,26655,26664,26663,26668,26669,26671,26672,26673,26675,26683,26687,26692,26693,26698,26700,26709,26711,26712,26715,26731,26734,26735,26736,26737,26738,26741,26745,26746,26747,26748,26754,26756,26758,26760,26774,26776,26778,26780,26785,26787,26789,26793,26794,26798,26802,26811,26821,26824,26828,26831,26832,26833,26835,26838,26841,26844,26845,26853,26856,26858,26859,26860,26861,26864,26865,26869,26870,26875,26876,26877,26886,26889,26890,26896,26897,26899,26902,26903,26929,26931,26933,26936,26939,26946,26949,26953,26958,26967,26971,26979,26980,26981,26982,26984,26985,26988,26992,26993,26994,27002,27003,27007,27008,27021,27026,27030,27032,27041,27045,27046,27048,27051,27053,27055,27063,27064,27066,27068,27077,27080,27089,27094,27095,27106,27109,27118,27119,27121,27123,27125,27134,27136,27137,27139,27151,27153,27157,27162,27165,27168,27172,27176,27184,27186,27188,27191,27195,27198,27199,27205,27206,27209,27210,27214,27216,27217,27218,27221,27222,27227,27236,27239,27242,27249,27251,27262,27265,27267,27270,27271,27273,27275,27281,27291,27293,27294,27295,27301,27307,27311,27312,27313,27316,27325,27326,27327,27334,27337,27336,27340,27344,27348,27349,27350,27356,27357,27364,27367,27372,27376,27377,27378,27388,27389,27394,27395,27398,27399,27401,27407,27408,27409,27415,27419,27422,27428,27432,27435,27436,27439,27445,27446,27451,27455,27462,27466,27469,27474,27478,27480,27485,27488,27495,27499,27502,27504,27509,27517,27518,27522,27525,27543,27547,27551,27552,27554,27555,27560,27561,27564,27565,27566,27568,27576,27577,27581,27582,27587,27588,27593,27596,27606,27610,27617,27619,27622,27623,27630,27633,27639,27641,27647,27650,27652,27653,27657,27661,27662,27664,27666,27673,27679,27686,27687,27688,27692,27694,27699,27701,27702,27706,27707,27711,27722,27723,27725,27727,27730,27732,27737,27739,27740,27755,27757,27759,27764,27766,27768,27769,27771,27781,27782,27783,27785,27796,27797,27799,27800,27804,27807,27824,27826,27828,27842,27846,27853,27855,27856,27857,27858,27860,27862,27866,27868,27872,27879,27881,27883,27884,27886,27890,27892,27908,27911,27914,27918,27919,27921,27923,27930,27942,27943,27944,27751,27950,27951,27953,27961,27964,27967,27991,27998,27999,28001,28005,28007,28015,28016,28028,28034,28039,28049,28050,28052,28054,28055,28056,28074,28076,28084,28087,28089,28093,28095,28100,28104,28106,28110,28111,28118,28123,28125,28127,28128,28130,28133,28137,28143,28144,28148,28150,28156,28160,28164,28190,28194,28199,28210,28214,28217,28219,28220,28228,28229,28232,28233,28235,28239,28241,28242,28243,28244,28247,28252,28253,28254,28258,28259,28264,28275,28283,28285,28301,28307,28313,28320,28327,28333,28334,28337,28339,28347,28351,28352,28353,28355,28359,28360,28362,28365,28366,28367,28395,28397,28398,28409,28411,28413,28420,28424,28426,28428,28429,28438,28440,28442,28443,28454,28457,28458,28463,28464,28467,28470,28475,28476,28461,28495,28497,28498,28499,28503,28505,28506,28509,28510,28513,28514,28520,28524,28541,28542,28547,28551,28552,28555,28556,28557,28560,28562,28563,28564,28566,28570,28575,28576,28581,28582,28583,28584,28590,28591,28592,28597,28598,28604,28613,28615,28616,28618,28634,28638,28648,28649,28656,28661,28665,28668,28669,28672,28677,28678,28679,28685,28695,28704,28707,28719,28724,28727,28729,28732,28739,28740,28744,28745,28746,28747,28756,28757,28765,28766,28750,28772,28773,28780,28782,28789,28790,28798,28801,28805,28806,28820,28821,28822,28823,28824,28827,28836,28843,28848,28849,28852,28855,28874,28881,28883,28884,28885,28886,28888,28892,28900,28922,28931,28932,28933,28934,28935,28939,28940,28943,28958,28960,28971,28973,28975,28976,28977,28984,28993,28997,28998,28999,29002,29003,29008,29010,29015,29018,29020,29022,29024,29032,29049,29056,29061,29063,29068,29074,29082,29083,29088,29090,29103,29104,29106,29107,29114,29119,29120,29121,29124,29131,29132,29139,29142,29145,29146,29148,29176,29182,29184,29191,29192,29193,29203,29207,29210,29213,29215,29220,29227,29231,29236,29240,29241,29249,29250,29251,29253,29262,29263,29264,29267,29269,29270,29274,29276,29278,29280,29283,29288,29291,29294,29295,29297,29303,29304,29307,29308,29311,29316,29321,29325,29326,29331,29339,29352,29357,29358,29361,29364,29374,29377,29383,29385,29388,29397,29398,29400,29407,29413,29427,29428,29434,29435,29438,29442,29444,29445,29447,29451,29453,29458,29459,29464,29465,29470,29474,29476,29479,29480,29484,29489,29490,29493,29498,29499,29501,29507,29517,29520,29522,29526,29528,29533,29534,29535,29536,29542,29543,29545,29547,29548,29550,29551,29553,29559,29561,29564,29568,29569,29571,29573,29574,29582,29584,29587,29589,29591,29592,29596,29598,29599,29600,29602,29605,29606,29610,29611,29613,29621,29623,29625,29628,29629,29631,29637,29638,29641,29643,29644,29647,29650,29651,29654,29657,29661,29665,29667,29670,29671,29673,29684,29685,29687,29689,29690,29691,29693,29695,29696,29697,29700,29703,29706,29713,29722,29723,29732,29734,29736,29737,29738,29739,29740,29741,29742,29743,29744,29745,29753,29760,29763,29764,29766,29767,29771,29773,29777,29778,29783,29789,29794,29798,29799,29800,29803,29805,29806,29809,29810,29824,29825,29829,29830,29831,29833,29839,29840,29841,29842,29848,29849,29850,29852,29855,29856,29857,29859,29862,29864,29865,29866,29867,29870,29871,29873,29874,29877,29881,29883,29887,29896,29897,29900,29904,29907,29912,29914,29915,29918,29919,29924,29928,29930,29931,29935,29940,29946,29947,29948,29951,29958,29970,29974,29975,29984,29985,29988,29991,29993,29994,29999,30006,30009,30013,30014,30015,30016,30019,30023,30024,30030,30032,30034,30039,30046,30047,30049,30063,30065,30073,30074,30075,30076,30077,30078,30081,30085,30096,30098,30099,30101,30105,30108,30114,30116,30132,30138,30143,30144,30145,30148,30150,30156,30158,30159,30167,30172,30175,30176,30177,30180,30183,30188,30190,30191,30193,30201,30208,30210,30211,30212,30215,30216,30218,30220,30223,30226,30227,30229,30230,30233,30235,30236,30237,30238,30243,30245,30246,30249,30253,30258,30259,30261,30264,30265,30266,30268,30282,30272,30273,30275,30276,30277,30281,30283,30293,30297,30303,30308,30309,30317,30318,30319,30321,30324,30337,30341,30348,30349,30357,30363,30364,30365,30367,30368,30370,30371,30372,30373,30374,30375,30376,30378,30381,30397,30401,30405,30409,30411,30412,30414,30420,30425,30432,30438,30440,30444,30448,30449,30454,30457,30460,30464,30470,30474,30478,30482,30484,30485,30487,30489,30490,30492,30498,30504,30509,30510,30511,30516,30517,30518,30521,30525,30526,30530,30533,30534,30538,30541,30542,30543,30546,30550,30551,30556,30558,30559,30560,30562,30564,30567,30570,30572,30576,30578,30579,30580,30586,30589,30592,30596,30604,30605,30612,30613,30614,30618,30623,30626,30631,30634,30638,30639,30641,30645,30654,30659,30665,30673,30674,30677,30681,30686,30687,30688,30692,30694,30698,30700,30704,30705,30708,30712,30715,30725,30726,30729,30733,30734,30737,30749,30753,30754,30755,30765,30766,30768,30773,30775,30787,30788,30791,30792,30796,30798,30802,30812,30814,30816,30817,30819,30820,30824,30826,30830,30842,30846,30858,30863,30868,30872,30881,30877,30878,30879,30884,30888,30892,30893,30896,30897,30898,30899,30907,30909,30911,30919,30920,30921,30924,30926,30930,30931,30933,30934,30948,30939,30943,30944,30945,30950,30954,30962,30963,30976,30966,30967,30970,30971,30975,30982,30988,30992,31002,31004,31006,31007,31008,31013,31015,31017,31021,31025,31028,31029,31035,31037,31039,31044,31045,31046,31050,31051,31055,31057,31060,31064,31067,31068,31079,31081,31083,31090,31097,31099,31100,31102,31115,31116,31121,31123,31124,31125,31126,31128,31131,31132,31137,31144,31145,31147,31151,31153,31156,31160,31163,31170,31172,31175,31176,31178,31183,31188,31190,31194,31197,31198,31200,31202,31205,31210,31211,31213,31217,31224,31228,31234,31235,31239,31241,31242,31244,31249,31253,31259,31262,31265,31271,31275,31277,31279,31280,31284,31285,31288,31289,31290,31300,31301,31303,31304,31308,31317,31318,31321,31324,31325,31327,31328,31333,31335,31338,31341,31349,31352,31358,31360,31362,31365,31366,31370,31371,31376,31377,31380,31390,31392,31395,31404,31411,31413,31417,31419,31420,31430,31433,31436,31438,31441,31451,31464,31465,31467,31468,31473,31476,31483,31485,31486,31495,31508,31519,31523,31527,31529,31530,31531,31533,31534,31535,31536,31537,31540,31549,31551,31552,31553,31559,31566,31573,31584,31588,31590,31593,31594,31597,31599,31602,31603,31607,31620,31625,31630,31632,31633,31638,31643,31646,31648,31653,31660,31663,31664,31666,31669,31670,31674,31675,31676,31677,31682,31685,31688,31690,31700,31702,31703,31705,31706,31707,31720,31722,31730,31732,31733,31736,31737,31738,31740,31742,31745,31746,31747,31748,31750,31753,31755,31756,31758,31759,31769,31771,31776,31781,31782,31784,31788,31793,31795,31796,31798,31801,31802,31814,31818,31829,31825,31826,31827,31833,31834,31835,31836,31837,31838,31841,31843,31847,31849,31853,31854,31856,31858,31865,31868,31869,31878,31879,31887,31892,31902,31904,31910,31920,31926,31927,31930,31931,31932,31935,31940,31943,31944,31945,31949,31951,31955,31956,31957,31959,31961,31962,31965,31974,31977,31979,31989,32003,32007,32008,32009,32015,32017,32018,32019,32022,32029,32030,32035,32038,32042,32045,32049,32060,32061,32062,32064,32065,32071,32072,32077,32081,32083,32087,32089,32090,32092,32093,32101,32103,32106,32112,32120,32122,32123,32127,32129,32130,32131,32133,32134,32136,32139,32140,32141,32145,32150,32151,32157,32158,32166,32167,32170,32179,32182,32183,32185,32194,32195,32196,32197,32198,32204,32205,32206,32215,32217,32256,32226,32229,32230,32234,32235,32237,32241,32245,32246,32249,32250,32264,32272,32273,32277,32279,32284,32285,32288,32295,32296,32300,32301,32303,32307,32310,32319,32324,32325,32327,32334,32336,32338,32344,32351,32353,32354,32357,32363,32366,32367,32371,32376,32382,32385,32390,32391,32394,32397,32401,32405,32408,32410,32413,32414,32572,32571,32573,32574,32575,32579,32580,32583,32591,32594,32595,32603,32604,32605,32609,32611,32612,32613,32614,32621,32625,32637,32638,32639,32640,32651,32653,32655,32656,32657,32662,32663,32668,32673,32674,32678,32682,32685,32692,32700,32703,32704,32707,32712,32718,32719,32731,32735,32739,32741,32744,32748,32750,32751,32754,32762,32765,32766,32767,32775,32776,32778,32781,32782,32783,32785,32787,32788,32790,32797,32798,32799,32800,32804,32806,32812,32814,32816,32820,32821,32823,32825,32826,32828,32830,32832,32836,32864,32868,32870,32877,32881,32885,32897,32904,32910,32924,32926,32934,32935,32939,32952,32953,32968,32973,32975,32978,32980,32981,32983,32984,32992,33005,33006,33008,33010,33011,33014,33017,33018,33022,33027,33035,33046,33047,33048,33052,33054,33056,33060,33063,33068,33072,33077,33082,33084,33093,33095,33098,33100,33106,33111,33120,33121,33127,33128,33129,33133,33135,33143,33153,33168,33156,33157,33158,33163,33166,33174,33176,33179,33182,33186,33198,33202,33204,33211,33227,33219,33221,33226,33230,33231,33237,33239,33243,33245,33246,33249,33252,33259,33260,33264,33265,33266,33269,33270,33272,33273,33277,33279,33280,33283,33295,33299,33300,33305,33306,33309,33313,33314,33320,33330,33332,33338,33347,33348,33349,33350,33355,33358,33359,33361,33366,33372,33376,33379,33383,33389,33396,33403,33405,33407,33408,33409,33411,33412,33415,33417,33418,33422,33425,33428,33430,33432,33434,33435,33440,33441,33443,33444,33447,33448,33449,33450,33454,33456,33458,33460,33463,33466,33468,33470,33471,33478,33488,33493,33498,33504,33506,33508,33512,33514,33517,33519,33526,33527,33533,33534,33536,33537,33543,33544,33546,33547,33620,33563,33565,33566,33567,33569,33570,33580,33581,33582,33584,33587,33591,33594,33596,33597,33602,33603,33604,33607,33613,33614,33617,33621,33622,33623,33648,33656,33661,33663,33664,33666,33668,33670,33677,33682,33684,33685,33688,33689,33691,33692,33693,33702,33703,33705,33708,33726,33727,33728,33735,33737,33743,33744,33745,33748,33757,33619,33768,33770,33782,33784,33785,33788,33793,33798,33802,33807,33809,33813,33817,33709,33839,33849,33861,33863,33864,33866,33869,33871,33873,33874,33878,33880,33881,33882,33884,33888,33892,33893,33895,33898,33904,33907,33908,33910,33912,33916,33917,33921,33925,33938,33939,33941,33950,33958,33960,33961,33962,33967,33969,33972,33978,33981,33982,33984,33986,33991,33992,33996,33999,34003,34012,34023,34026,34031,34032,34033,34034,34039,34098,34042,34043,34045,34050,34051,34055,34060,34062,34064,34076,34078,34082,34083,34084,34085,34087,34090,34091,34095,34099,34100,34102,34111,34118,34127,34128,34129,34130,34131,34134,34137,34140,34141,34142,34143,34144,34145,34146,34148,34155,34159,34169,34170,34171,34173,34175,34177,34181,34182,34185,34187,34188,34191,34195,34200,34205,34207,34208,34210,34213,34215,34228,34230,34231,34232,34236,34237,34238,34239,34242,34247,34250,34251,34254,34221,34264,34266,34271,34272,34278,34280,34285,34291,34294,34300,34303,34304,34308,34309,34317,34318,34320,34321,34322,34328,34329,34331,34334,34337,34343,34345,34358,34360,34362,34364,34365,34368,34370,34374,34386,34387,34390,34391,34392,34393,34397,34400,34401,34402,34403,34404,34409,34412,34415,34421,34422,34423,34426,34445,34449,34454,34456,34458,34460,34465,34470,34471,34472,34477,34481,34483,34484,34485,34487,34488,34489,34495,34496,34497,34499,34501,34513,34514,34517,34519,34522,34524,34528,34531,34533,34535,34440,34554,34556,34557,34564,34565,34567,34571,34574,34575,34576,34579,34580,34585,34590,34591,34593,34595,34600,34606,34607,34609,34610,34617,34618,34620,34621,34622,34624,34627,34629,34637,34648,34653,34657,34660,34661,34671,34673,34674,34683,34691,34692,34693,34694,34695,34696,34697,34699,34700,34704,34707,34709,34711,34712,34713,34718,34720,34723,34727,34732,34733,34734,34737,34741,34750,34751,34753,34760,34761,34762,34766,34773,34774,34777,34778,34780,34783,34786,34787,34788,34794,34795,34797,34801,34803,34808,34810,34815,34817,34819,34822,34825,34826,34827,34832,34841,34834,34835,34836,34840,34842,34843,34844,34846,34847,34856,34861,34862,34864,34866,34869,34874,34876,34881,34883,34885,34888,34889,34890,34891,34894,34897,34901,34902,34904,34906,34908,34911,34912,34916,34921,34929,34937,34939,34944,34968,34970,34971,34972,34975,34976,34984,34986,35002,35005,35006,35008,35018,35019,35020,35021,35022,35025,35026,35027,35035,35038,35047,35055,35056,35057,35061,35063,35073,35078,35085,35086,35087,35093,35094,35096,35097,35098,35100,35104,35110,35111,35112,35120,35121,35122,35125,35129,35130,35134,35136,35138,35141,35142,35145,35151,35154,35159,35162,35163,35164,35169,35170,35171,35179,35182,35184,35187,35189,35194,35195,35196,35197,35209,35213,35216,35220,35221,35227,35228,35231,35232,35237,35248,35252,35253,35254,35255,35260,35284,35285,35286,35287,35288,35301,35305,35307,35309,35313,35315,35318,35321,35325,35327,35332,35333,35335,35343,35345,35346,35348,35349,35358,35360,35362,35364,35366,35371,35372,35375,35381,35383,35389,35390,35392,35395,35397,35399,35401,35405,35406,35411,35414,35415,35416,35420,35421,35425,35429,35431,35445,35446,35447,35449,35450,35451,35454,35455,35456,35459,35462,35467,35471,35472,35474,35478,35479,35481,35487,35495,35497,35502,35503,35507,35510,35511,35515,35518,35523,35526,35528,35529,35530,35537,35539,35540,35541,35543,35549,35551,35564,35568,35572,35573,35574,35580,35583,35589,35590,35595,35601,35612,35614,35615,35594,35629,35632,35639,35644,35650,35651,35652,35653,35654,35656,35666,35667,35668,35673,35661,35678,35683,35693,35702,35704,35705,35708,35710,35713,35716,35717,35723,35725,35727,35732,35733,35740,35742,35743,35896,35897,35901,35902,35909,35911,35913,35915,35919,35921,35923,35924,35927,35928,35931,35933,35929,35939,35940,35942,35944,35945,35949,35955,35957,35958,35963,35966,35974,35975,35979,35984,35986,35987,35993,35995,35996,36004,36025,36026,36037,36038,36041,36043,36047,36054,36053,36057,36061,36065,36072,36076,36079,36080,36082,36085,36087,36088,36094,36095,36097,36099,36105,36114,36119,36123,36197,36201,36204,36206,36223,36226,36228,36232,36237,36240,36241,36245,36254,36255,36256,36262,36267,36268,36271,36274,36277,36279,36281,36283,36288,36293,36294,36295,36296,36298,36302,36305,36308,36309,36311,36313,36324,36325,36327,36332,36336,36284,36337,36338,36340,36349,36353,36356,36357,36358,36363,36369,36372,36374,36384,36385,36386,36387,36390,36391,36401,36403,36406,36407,36408,36409,36413,36416,36417,36427,36429,36430,36431,36436,36443,36444,36445,36446,36449,36450,36457,36460,36461,36463,36464,36465,36473,36474,36475,36482,36483,36489,36496,36498,36501,36506,36507,36509,36510,36514,36519,36521,36525,36526,36531,36533,36538,36539,36544,36545,36547,36548,36551,36559,36561,36564,36572,36584,36590,36592,36593,36599,36601,36602,36589,36608,36610,36615,36616,36623,36624,36630,36631,36632,36638,36640,36641,36643,36645,36647,36648,36652,36653,36654,36660,36661,36662,36663,36666,36672,36673,36675,36679,36687,36689,36690,36691,36692,36693,36696,36701,36702,36709,36765,36768,36769,36772,36773,36774,36789,36790,36792,36798,36800,36801,36806,36810,36811,36813,36816,36818,36819,36821,36832,36835,36836,36840,36846,36849,36853,36854,36859,36862,36866,36868,36872,36876,36888,36891,36904,36905,36911,36906,36908,36909,36915,36916,36919,36927,36931,36932,36940,36955,36957,36962,36966,36967,36972,36976,36980,36985,36997,37000,37003,37004,37006,37008,37013,37015,37016,37017,37019,37024,37025,37026,37029,37040,37042,37043,37044,37046,37053,37068,37054,37059,37060,37061,37063,37064,37077,37079,37080,37081,37084,37085,37087,37093,37074,37110,37099,37103,37104,37108,37118,37119,37120,37124,37125,37126,37128,37133,37136,37140,37142,37143,37144,37146,37148,37150,37152,37157,37154,37155,37159,37161,37166,37167,37169,37172,37174,37175,37177,37178,37180,37181,37187,37191,37192,37199,37203,37207,37209,37210,37211,37217,37220,37223,37229,37236,37241,37242,37243,37249,37251,37253,37254,37258,37262,37265,37267,37268,37269,37272,37278,37281,37286,37288,37292,37293,37294,37296,37297,37298,37299,37302,37307,37308,37309,37311,37314,37315,37317,37331,37332,37335,37337,37338,37342,37348,37349,37353,37354,37356,37357,37358,37359,37360,37361,37367,37369,37371,37373,37376,37377,37380,37381,37382,37383,37385,37386,37388,37392,37394,37395,37398,37400,37404,37405,37411,37412,37413,37414,37416,37422,37423,37424,37427,37429,37430,37432,37433,37434,37436,37438,37440,37442,37443,37446,37447,37450,37453,37454,37455,37457,37464,37465,37468,37469,37472,37473,37477,37479,37480,37481,37486,37487,37488,37493,37494,37495,37496,37497,37499,37500,37501,37503,37512,37513,37514,37517,37518,37522,37527,37529,37535,37536,37540,37541,37543,37544,37547,37551,37554,37558,37560,37562,37563,37564,37565,37567,37568,37569,37570,37571,37573,37574,37575,37576,37579,37580,37581,37582,37584,37587,37589,37591,37592,37593,37596,37597,37599,37600,37601,37603,37605,37607,37608,37612,37614,37616,37625,37627,37631,37632,37634,37640,37645,37649,37652,37653,37660,37661,37662,37663,37665,37668,37669,37671,37673,37674,37683,37684,37686,37687,37703,37704,37705,37712,37713,37714,37717,37719,37720,37722,37726,37732,37733,37735,37737,37738,37741,37743,37744,37745,37747,37748,37750,37754,37757,37759,37760,37761,37762,37768,37770,37771,37773,37775,37778,37781,37784,37787,37790,37793,37795,37796,37798,37800,37803,37812,37813,37814,37818,37801,37825,37828,37829,37830,37831,37833,37834,37835,37836,37837,37843,37849,37852,37854,37855,37858,37862,37863,37881,37879,37880,37882,37883,37885,37889,37890,37892,37896,37897,37901,37902,37903,37909,37910,37911,37919,37934,37935,37937,37938,37939,37940,37947,37951,37949,37955,37957,37960,37962,37964,37973,37977,37980,37983,37985,37987,37992,37995,37997,37998,37999,38001,38002,38020,38019,38264,38265,38270,38276,38280,38284,38285,38286,38301,38302,38303,38305,38310,38313,38315,38316,38324,38326,38330,38333,38335,38342,38344,38345,38347,38352,38353,38354,38355,38361,38362,38365,38366,38367,38368,38372,38374,38429,38430,38434,38436,38437,38438,38444,38449,38451,38455,38456,38457,38458,38460,38461,38465,38482,38484,38486,38487,38488,38497,38510,38516,38523,38524,38526,38527,38529,38530,38531,38532,38537,38545,38550,38554,38557,38559,38564,38565,38566,38569,38574,38575,38579,38586,38602,38610,23986,38616,38618,38621,38622,38623,38633,38639,38641,38650,38658,38659,38661,38665,38682,38683,38685,38689,38690,38691,38696,38705,38707,38721,38723,38730,38734,38735,38741,38743,38744,38746,38747,38755,38759,38762,38766,38771,38774,38775,38776,38779,38781,38783,38784,38793,38805,38806,38807,38809,38810,38814,38815,38818,38828,38830,38833,38834,38837,38838,38840,38841,38842,38844,38846,38847,38849,38852,38853,38855,38857,38858,38860,38861,38862,38864,38865,38868,38871,38872,38873,38877,38878,38880,38875,38881,38884,38895,38897,38900,38903,38904,38906,38919,38922,38937,38925,38926,38932,38934,38940,38942,38944,38947,38950,38955,38958,38959,38960,38962,38963,38965,38949,38974,38980,38983,38986,38993,38994,38995,38998,38999,39001,39002,39010,39011,39013,39014,39018,39020,39083,39085,39086,39088,39092,39095,39096,39098,39099,39103,39106,39109,39112,39116,39137,39139,39141,39142,39143,39146,39155,39158,39170,39175,39176,39185,39189,39190,39191,39194,39195,39196,39199,39202,39206,39207,39211,39217,39218,39219,39220,39221,39225,39226,39227,39228,39232,39233,39238,39239,39240,39245,39246,39252,39256,39257,39259,39260,39262,39263,39264,39323,39325,39327,39334,39344,39345,39346,39349,39353,39354,39357,39359,39363,39369,39379,39380,39385,39386,39388,39390,39399,39402,39403,39404,39408,39412,39413,39417,39421,39422,39426,39427,39428,39435,39436,39440,39441,39446,39454,39456,39458,39459,39460,39463,39469,39470,39475,39477,39478,39480,39495,39489,39492,39498,39499,39500,39502,39505,39508,39510,39517,39594,39596,39598,39599,39602,39604,39605,39606,39609,39611,39614,39615,39617,39619,39622,39624,39630,39632,39634,39637,39638,39639,39643,39644,39648,39652,39653,39655,39657,39660,39666,39667,39669,39673,39674,39677,39679,39680,39681,39682,39683,39684,39685,39688,39689,39691,39692,39693,39694,39696,39698,39702,39705,39707,39708,39712,39718,39723,39725,39731,39732,39733,39735,39737,39738,39741,39752,39755,39756,39765,39766,39767,39771,39774,39777,39779,39781,39782,39784,39786,39787,39788,39789,39790,39795,39797,39799,39800,39801,39807,39808,39812,39813,39814,39815,39817,39818,39819,39821,39823,39824,39828,39834,39837,39838,39846,39847,39849,39852,39856,39857,39858,39863,39864,39867,39868,39870,39871,39873,39879,39880,39886,39888,39895,39896,39901,39903,39909,39911,39914,39915,39919,39923,39927,39928,39929,39930,39933,39935,39936,39938,39947,39951,39953,39958,39960,39961,39962,39964,39966,39970,39971,39974,39975,39976,39977,39978,39985,39989,39990,39991,39997,40001,40003,40004,40005,40009,40010,40014,40015,40016,40019,40020,40022,40024,40027,40029,40030,40031,40035,40041,40042,40028,40043,40040,40046,40048,40050,40053,40055,40059,40166,40178,40183,40185,40203,40194,40209,40215,40216,40220,40221,40222,40239,40240,40242,40243,40244,40250,40252,40261,40253,40258,40259,40263,40266,40275,40276,40287,40291,40290,40293,40297,40298,40299,40304,40310,40311,40315,40316,40318,40323,40324,40326,40330,40333,40334,40338,40339,40341,40342,40343,40344,40353,40362,40364,40366,40369,40373,40377,40380,40383,40387,40391,40393,40394,40404,40405,40406,40407,40410,40414,40415,40416,40421,40423,40425,40427,40430,40432,40435,40436,40446,40458,40450,40455,40462,40464,40465,40466,40469,40470,40473,40476,40477,40570,40571,40572,40576,40578,40579,40580,40581,40583,40590,40591,40598,40600,40603,40606,40612,40616,40620,40622,40623,40624,40627,40628,40629,40646,40648,40651,40661,40671,40676,40679,40684,40685,40686,40688,40689,40690,40693,40696,40703,40706,40707,40713,40719,40720,40721,40722,40724,40726,40727,40729,40730,40731,40735,40738,40742,40746,40747,40751,40753,40754,40756,40759,40761,40762,40764,40765,40767,40769,40771,40772,40773,40774,40775,40787,40789,40790,40791,40792,40794,40797,40798,40808,40809,40813,40814,40815,40816,40817,40819,40821,40826,40829,40847,40848,40849,40850,40852,40854,40855,40862,40865,40866,40867,40869,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null}; + + +struct euc_jp_decoder final : jis_decoder +{ + int m_lead = 0; + bool m_jis0212 = false; + + result handler(string& input, int& index, int ch[2]) override; +}; + +// https://encoding.spec.whatwg.org/#euc-jp-decoder +decoder::result euc_jp_decoder::handler(inout string& input, inout int& index, out int ch[2]) +{ + int b = index == (int)input.size() ? EOF : (byte)input[index++]; // read input byte + + // 1. If byte is end-of-queue and EUC-JP lead is not 0x00, set EUC-JP lead to 0x00, and return error. + if (b == EOF && m_lead != 0) + { + m_lead = 0; + return result_error; + } + + // 2. If byte is end-of-queue and EUC-JP lead is 0x00, return finished. + if (b == EOF && m_lead == 0) + return result_finished; + + // 3. + if (m_lead == 0x8E && b >= 0xA1 && b <= 0xDF) + { + m_lead = 0; + *ch = 0xFF61 - 0xA1 + b; + return result_codepoint; + } + + // 4. + if (m_lead == 0x8F && b >= 0xA1 && b <= 0xFE) + { + m_jis0212 = true; + m_lead = b; + return result_continue; + } + + // 5. If EUC-JP lead is not 0x00, let lead be EUC-JP lead, set EUC-JP lead to 0x00, and then: + if (m_lead != 0) + { + int lead = m_lead; + m_lead = 0; + + // 1. + int code_point = null; + + // 2. + if ((lead >= 0xA1 && lead <= 0xFE) && (b >= 0xA1 && b <= 0xFE)) + { + int pointer = (lead - 0xA1) * 94 + b - 0xA1; + code_point = m_jis0212 ? + index_code_point(pointer, m_jis0212_index) : + index_code_point(pointer, m_jis0208_index); + } + + // 3. + m_jis0212 = false; + + // 4. + if (code_point != null) + { + *ch = code_point; + return result_codepoint; + } + + // 5. If byte is an ASCII byte, restore byte to ioQueue. + if (b >= 0 && b <= 0x7F) index--; + + // 6. + return result_error; + } + + // 6. If byte is an ASCII byte, return a code point whose value is byte. + if (b >= 0 && b <= 0x7F) + { + *ch = b; + return result_codepoint; + } + + // 7. + if (b == 0x8E || b == 0x8F || (b >= 0xA1 && b <= 0xFE)) + { + m_lead = b; + return result_continue; + } + + // 8. + return result_error; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#undef NULL + +struct iso_2022_jp_decoder final : jis_decoder +{ + enum state + { + NULL, + ASCII, + ROMAN, + KATAKANA, + LEAD_BYTE, + TRAIL_BYTE, + ESCAPE_START, + ESCAPE + }; + + int m_lead = 0; + state m_state = ASCII; + state m_output_state = ASCII; + bool m_output = false; + + result handler(string& input, int& index, int ch[2]) override; +}; + +// https://encoding.spec.whatwg.org/#iso-2022-jp-decoder +decoder::result iso_2022_jp_decoder::handler(inout string& input, inout int& index, out int ch[2]) +{ + int b = index == (int)input.size() ? EOF : (byte)input[index++]; // read input byte + + switch (m_state) + { + case ASCII: + if (b == 0x1B) + { + m_state = ESCAPE_START; + return result_continue; + } + else if (b >= 0 && b <= 0x7F && b != 0x0E && b != 0x0F && b != 0x1B) + { + m_output = false; + *ch = b; + return result_codepoint; + } + else if (b == EOF) + { + return result_finished; + } + else + { + m_output = false; + return result_error; + } + break; + + case ROMAN: + if (b == 0x1B) + { + m_state = ESCAPE_START; + return result_continue; + } + else if (b == 0x5C) + { + m_output = false; + *ch = 0xA5; + return result_codepoint; + } + else if (b == 0x7E) + { + m_output = false; + *ch = 0x203E; + return result_codepoint; + } + else if (b >= 0 && b <= 0x7F && b != 0x0E && b != 0x0F && b != 0x1B && b != 0x5C && b != 0x7E) + { + m_output = false; + *ch = b; + return result_codepoint; + } + else if (b == EOF) + { + return result_finished; + } + else + { + m_output = false; + return result_error; + } + break; + + case KATAKANA: + if (b == 0x1B) + { + m_state = ESCAPE_START; + return result_continue; + } + else if (b >= 0x21 && b <= 0x5F) + { + m_output = false; + *ch = 0xFF61 - 0x21 + b; + return result_codepoint; + } + else if (b == EOF) + { + return result_finished; + } + else + { + m_output = false; + return result_error; + } + break; + + case LEAD_BYTE: + if (b == 0x1B) + { + m_state = ESCAPE_START; + return result_continue; + } + else if (b >= 0x21 && b <= 0x7E) + { + m_output = false; + m_lead = b; + m_state = TRAIL_BYTE; + return result_continue; + } + else if (b == EOF) + { + return result_finished; + } + else + { + m_output = false; + return result_error; + } + break; + + case TRAIL_BYTE: + if (b == 0x1B) + { + m_state = ESCAPE_START; + return result_error; + } + else if (b >= 0x21 && b <= 0x7E) + { + m_state = LEAD_BYTE; + int pointer = (m_lead - 0x21) * 94 + b - 0x21; + int code_point = index_code_point(pointer, m_jis0208_index); + if (code_point == null) return result_error; + *ch = code_point; + return result_codepoint; + } + else if (b == EOF) // same as last else + { + m_state = LEAD_BYTE; + return result_error; + } + else + { + m_state = LEAD_BYTE; + return result_error; + } + break; + + case ESCAPE_START: + // 1. + if (b == 0x24 || b == 0x28) + { + m_lead = b; + m_state = ESCAPE; + return result_continue; + } + // 2. + if (b != EOF) index--; + // 3. + m_output = false; + m_state = m_output_state; + return result_error; + + case ESCAPE: + { + // 1. + int lead = m_lead; + m_lead = 0; + // 2,3,4,5,6. + state state = NULL; + if (lead == 0x28 && b == 0x42) state = ASCII; + else if (lead == 0x28 && b == 0x4A) state = ROMAN; + else if (lead == 0x28 && b == 0x49) state = KATAKANA; + else if (lead == 0x24 && (b == 0x40 || b == 0x42)) state = LEAD_BYTE; + // 7. + if (m_state) + { + m_state = m_output_state = state; + bool output = m_output; + m_output = true; + return output == false ? result_continue : result_error; + } + // 8. If byte is end-of-queue, then restore lead to ioQueue; otherwise, restore « lead, byte » to ioQueue. + if (b == EOF) + input.insert(index, 1, (char)lead); + else + input.insert(index, {(char)lead, (char)b}); + // 9. + m_output = false; + m_state = m_output_state; + return result_error; + } + break; + + default: // silence warning C4715 (not all control paths return a value) + assert(0); + return result_error; + + } // switch +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +struct shift_jis_decoder final : jis_decoder +{ + int m_lead = 0; + + result handler(string& input, int& index, int ch[2]) override; +}; + +// https://encoding.spec.whatwg.org/#shift_jis-decoder +decoder::result shift_jis_decoder::handler(inout string& input, inout int& index, out int ch[2]) +{ + int b = index == (int)input.size() ? EOF : (byte)input[index++]; // read input byte + + // 1. If byte is end-of-queue and Shift_JIS lead is not 0x00, set Shift_JIS lead to 0x00 and return error. + if (b == EOF && m_lead != 0) + { + m_lead = 0; + return result_error; + } + + // 2. If byte is end-of-queue and Shift_JIS lead is 0x00, return finished. + if (b == EOF && m_lead == 0) + return result_finished; + + // 3. + if (m_lead != 0) + { + int lead = m_lead; + int pointer = null; + m_lead = 0; + + // 1,2. + int offset = b < 0x7F ? 0x40 : 0x41; + int lead_offset = lead < 0xA0 ? 0x81 : 0xC1; + + // 3. + if ((b >= 0x40 && b <= 0x7E) || (b >= 0x80 && b <= 0xFC)) + pointer = (lead - lead_offset) * 188 + b - offset; + + // 4. + if (pointer >= 8836 && pointer <= 10715) + { + *ch = 0xE000 - 8836 + pointer; + return result_codepoint; + } + + // 5. + int code_point = pointer != null ? index_code_point(pointer, m_jis0208_index) : null; + + // 6. + if (code_point != null) + { + *ch = code_point; + return result_codepoint; + } + + // 7. If byte is an ASCII byte, restore byte to ioQueue. + if (b >= 0 && b <= 0x7F) index--; + + // 8. + return result_error; + } + + // 4. If byte is an ASCII byte or 0x80, return a code point whose value is byte. + if (b >= 0 && b <= 0x80) + { + *ch = b; + return result_codepoint; + } + + // 5. + if (b >= 0xA1 && b <= 0xDF) + { + *ch = 0xFF61 - 0xA1 + b; + return result_codepoint; + } + + // 6. + if ((b >= 0x81 && b <= 0x9F) || (b >= 0xE0 && b <= 0xFC)) + { + m_lead = b; + return result_continue; + } + + // 7. + return result_error; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +struct euc_kr_decoder final : decoder +{ + int m_lead = 0; + + result handler(string& input, int& index, int ch[2]) override; + + static int m_index[23940]; +}; + +// https://encoding.spec.whatwg.org/indexes.json +int euc_kr_decoder::m_index[] = {44034,44035,44037,44038,44043,44044,44045,44046,44047,44056,44062,44063,44065,44066,44067,44069,44070,44071,44072,44073,44074,44075,44078,44082,44083,44084,null,null,null,null,null,null,44085,44086,44087,44090,44091,44093,44094,44095,44097,44098,44099,44100,44101,44102,44103,44104,44105,44106,44108,44110,44111,44112,44113,44114,44115,44117,null,null,null,null,null,null,44118,44119,44121,44122,44123,44125,44126,44127,44128,44129,44130,44131,44132,44133,44134,44135,44136,44137,44138,44139,44140,44141,44142,44143,44146,44147,44149,44150,44153,44155,44156,44157,44158,44159,44162,44167,44168,44173,44174,44175,44177,44178,44179,44181,44182,44183,44184,44185,44186,44187,44190,44194,44195,44196,44197,44198,44199,44203,44205,44206,44209,44210,44211,44212,44213,44214,44215,44218,44222,44223,44224,44226,44227,44229,44230,44231,44233,44234,44235,44237,44238,44239,44240,44241,44242,44243,44244,44246,44248,44249,44250,44251,44252,44253,44254,44255,44258,44259,44261,44262,44265,44267,44269,44270,44274,44276,44279,44280,44281,44282,44283,44286,44287,44289,44290,44291,44293,44295,44296,44297,44298,44299,44302,44304,44306,44307,44308,44309,44310,44311,44313,44314,44315,44317,44318,44319,44321,44322,44323,44324,44325,44326,44327,44328,44330,44331,44334,44335,44336,44337,44338,44339,null,null,null,null,null,null,44342,44343,44345,44346,44347,44349,44350,44351,44352,44353,44354,44355,44358,44360,44362,44363,44364,44365,44366,44367,44369,44370,44371,44373,44374,44375,null,null,null,null,null,null,44377,44378,44379,44380,44381,44382,44383,44384,44386,44388,44389,44390,44391,44392,44393,44394,44395,44398,44399,44401,44402,44407,44408,44409,44410,44414,44416,44419,44420,44421,44422,44423,44426,44427,44429,44430,44431,44433,44434,44435,44436,44437,44438,44439,44440,44441,44442,44443,44446,44447,44448,44449,44450,44451,44453,44454,44455,44456,44457,44458,44459,44460,44461,44462,44463,44464,44465,44466,44467,44468,44469,44470,44472,44473,44474,44475,44476,44477,44478,44479,44482,44483,44485,44486,44487,44489,44490,44491,44492,44493,44494,44495,44498,44500,44501,44502,44503,44504,44505,44506,44507,44509,44510,44511,44513,44514,44515,44517,44518,44519,44520,44521,44522,44523,44524,44525,44526,44527,44528,44529,44530,44531,44532,44533,44534,44535,44538,44539,44541,44542,44546,44547,44548,44549,44550,44551,44554,44556,44558,44559,44560,44561,44562,44563,44565,44566,44567,44568,44569,44570,44571,44572,null,null,null,null,null,null,44573,44574,44575,44576,44577,44578,44579,44580,44581,44582,44583,44584,44585,44586,44587,44588,44589,44590,44591,44594,44595,44597,44598,44601,44603,44604,null,null,null,null,null,null,44605,44606,44607,44610,44612,44615,44616,44617,44619,44623,44625,44626,44627,44629,44631,44632,44633,44634,44635,44638,44642,44643,44644,44646,44647,44650,44651,44653,44654,44655,44657,44658,44659,44660,44661,44662,44663,44666,44670,44671,44672,44673,44674,44675,44678,44679,44680,44681,44682,44683,44685,44686,44687,44688,44689,44690,44691,44692,44693,44694,44695,44696,44697,44698,44699,44700,44701,44702,44703,44704,44705,44706,44707,44708,44709,44710,44711,44712,44713,44714,44715,44716,44717,44718,44719,44720,44721,44722,44723,44724,44725,44726,44727,44728,44729,44730,44731,44735,44737,44738,44739,44741,44742,44743,44744,44745,44746,44747,44750,44754,44755,44756,44757,44758,44759,44762,44763,44765,44766,44767,44768,44769,44770,44771,44772,44773,44774,44775,44777,44778,44780,44782,44783,44784,44785,44786,44787,44789,44790,44791,44793,44794,44795,44797,44798,44799,44800,44801,44802,44803,44804,44805,null,null,null,null,null,null,44806,44809,44810,44811,44812,44814,44815,44817,44818,44819,44820,44821,44822,44823,44824,44825,44826,44827,44828,44829,44830,44831,44832,44833,44834,44835,null,null,null,null,null,null,44836,44837,44838,44839,44840,44841,44842,44843,44846,44847,44849,44851,44853,44854,44855,44856,44857,44858,44859,44862,44864,44868,44869,44870,44871,44874,44875,44876,44877,44878,44879,44881,44882,44883,44884,44885,44886,44887,44888,44889,44890,44891,44894,44895,44896,44897,44898,44899,44902,44903,44904,44905,44906,44907,44908,44909,44910,44911,44912,44913,44914,44915,44916,44917,44918,44919,44920,44922,44923,44924,44925,44926,44927,44929,44930,44931,44933,44934,44935,44937,44938,44939,44940,44941,44942,44943,44946,44947,44948,44950,44951,44952,44953,44954,44955,44957,44958,44959,44960,44961,44962,44963,44964,44965,44966,44967,44968,44969,44970,44971,44972,44973,44974,44975,44976,44977,44978,44979,44980,44981,44982,44983,44986,44987,44989,44990,44991,44993,44994,44995,44996,44997,44998,45002,45004,45007,45008,45009,45010,45011,45013,45014,45015,45016,45017,45018,45019,45021,45022,45023,45024,45025,null,null,null,null,null,null,45026,45027,45028,45029,45030,45031,45034,45035,45036,45037,45038,45039,45042,45043,45045,45046,45047,45049,45050,45051,45052,45053,45054,45055,45058,45059,null,null,null,null,null,null,45061,45062,45063,45064,45065,45066,45067,45069,45070,45071,45073,45074,45075,45077,45078,45079,45080,45081,45082,45083,45086,45087,45088,45089,45090,45091,45092,45093,45094,45095,45097,45098,45099,45100,45101,45102,45103,45104,45105,45106,45107,45108,45109,45110,45111,45112,45113,45114,45115,45116,45117,45118,45119,45120,45121,45122,45123,45126,45127,45129,45131,45133,45135,45136,45137,45138,45142,45144,45146,45147,45148,45150,45151,45152,45153,45154,45155,45156,45157,45158,45159,45160,45161,45162,45163,45164,45165,45166,45167,45168,45169,45170,45171,45172,45173,45174,45175,45176,45177,45178,45179,45182,45183,45185,45186,45187,45189,45190,45191,45192,45193,45194,45195,45198,45200,45202,45203,45204,45205,45206,45207,45211,45213,45214,45219,45220,45221,45222,45223,45226,45232,45234,45238,45239,45241,45242,45243,45245,45246,45247,45248,45249,45250,45251,45254,45258,45259,45260,45261,45262,45263,45266,null,null,null,null,null,null,45267,45269,45270,45271,45273,45274,45275,45276,45277,45278,45279,45281,45282,45283,45284,45286,45287,45288,45289,45290,45291,45292,45293,45294,45295,45296,null,null,null,null,null,null,45297,45298,45299,45300,45301,45302,45303,45304,45305,45306,45307,45308,45309,45310,45311,45312,45313,45314,45315,45316,45317,45318,45319,45322,45325,45326,45327,45329,45332,45333,45334,45335,45338,45342,45343,45344,45345,45346,45350,45351,45353,45354,45355,45357,45358,45359,45360,45361,45362,45363,45366,45370,45371,45372,45373,45374,45375,45378,45379,45381,45382,45383,45385,45386,45387,45388,45389,45390,45391,45394,45395,45398,45399,45401,45402,45403,45405,45406,45407,45409,45410,45411,45412,45413,45414,45415,45416,45417,45418,45419,45420,45421,45422,45423,45424,45425,45426,45427,45428,45429,45430,45431,45434,45435,45437,45438,45439,45441,45443,45444,45445,45446,45447,45450,45452,45454,45455,45456,45457,45461,45462,45463,45465,45466,45467,45469,45470,45471,45472,45473,45474,45475,45476,45477,45478,45479,45481,45482,45483,45484,45485,45486,45487,45488,45489,45490,45491,45492,45493,45494,45495,45496,null,null,null,null,null,null,45497,45498,45499,45500,45501,45502,45503,45504,45505,45506,45507,45508,45509,45510,45511,45512,45513,45514,45515,45517,45518,45519,45521,45522,45523,45525,null,null,null,null,null,null,45526,45527,45528,45529,45530,45531,45534,45536,45537,45538,45539,45540,45541,45542,45543,45546,45547,45549,45550,45551,45553,45554,45555,45556,45557,45558,45559,45560,45562,45564,45566,45567,45568,45569,45570,45571,45574,45575,45577,45578,45581,45582,45583,45584,45585,45586,45587,45590,45592,45594,45595,45596,45597,45598,45599,45601,45602,45603,45604,45605,45606,45607,45608,45609,45610,45611,45612,45613,45614,45615,45616,45617,45618,45619,45621,45622,45623,45624,45625,45626,45627,45629,45630,45631,45632,45633,45634,45635,45636,45637,45638,45639,45640,45641,45642,45643,45644,45645,45646,45647,45648,45649,45650,45651,45652,45653,45654,45655,45657,45658,45659,45661,45662,45663,45665,45666,45667,45668,45669,45670,45671,45674,45675,45676,45677,45678,45679,45680,45681,45682,45683,45686,45687,45688,45689,45690,45691,45693,45694,45695,45696,45697,45698,45699,45702,45703,45704,45706,45707,45708,45709,45710,null,null,null,null,null,null,45711,45714,45715,45717,45718,45719,45723,45724,45725,45726,45727,45730,45732,45735,45736,45737,45739,45741,45742,45743,45745,45746,45747,45749,45750,45751,null,null,null,null,null,null,45752,45753,45754,45755,45756,45757,45758,45759,45760,45761,45762,45763,45764,45765,45766,45767,45770,45771,45773,45774,45775,45777,45779,45780,45781,45782,45783,45786,45788,45790,45791,45792,45793,45795,45799,45801,45802,45808,45809,45810,45814,45820,45821,45822,45826,45827,45829,45830,45831,45833,45834,45835,45836,45837,45838,45839,45842,45846,45847,45848,45849,45850,45851,45853,45854,45855,45856,45857,45858,45859,45860,45861,45862,45863,45864,45865,45866,45867,45868,45869,45870,45871,45872,45873,45874,45875,45876,45877,45878,45879,45880,45881,45882,45883,45884,45885,45886,45887,45888,45889,45890,45891,45892,45893,45894,45895,45896,45897,45898,45899,45900,45901,45902,45903,45904,45905,45906,45907,45911,45913,45914,45917,45920,45921,45922,45923,45926,45928,45930,45932,45933,45935,45938,45939,45941,45942,45943,45945,45946,45947,45948,45949,45950,45951,45954,45958,45959,45960,45961,45962,45963,45965,null,null,null,null,null,null,45966,45967,45969,45970,45971,45973,45974,45975,45976,45977,45978,45979,45980,45981,45982,45983,45986,45987,45988,45989,45990,45991,45993,45994,45995,45997,null,null,null,null,null,null,45998,45999,46000,46001,46002,46003,46004,46005,46006,46007,46008,46009,46010,46011,46012,46013,46014,46015,46016,46017,46018,46019,46022,46023,46025,46026,46029,46031,46033,46034,46035,46038,46040,46042,46044,46046,46047,46049,46050,46051,46053,46054,46055,46057,46058,46059,46060,46061,46062,46063,46064,46065,46066,46067,46068,46069,46070,46071,46072,46073,46074,46075,46077,46078,46079,46080,46081,46082,46083,46084,46085,46086,46087,46088,46089,46090,46091,46092,46093,46094,46095,46097,46098,46099,46100,46101,46102,46103,46105,46106,46107,46109,46110,46111,46113,46114,46115,46116,46117,46118,46119,46122,46124,46125,46126,46127,46128,46129,46130,46131,46133,46134,46135,46136,46137,46138,46139,46140,46141,46142,46143,46144,46145,46146,46147,46148,46149,46150,46151,46152,46153,46154,46155,46156,46157,46158,46159,46162,46163,46165,46166,46167,46169,46170,46171,46172,46173,46174,46175,46178,46180,46182,null,null,null,null,null,null,46183,46184,46185,46186,46187,46189,46190,46191,46192,46193,46194,46195,46196,46197,46198,46199,46200,46201,46202,46203,46204,46205,46206,46207,46209,46210,null,null,null,null,null,null,46211,46212,46213,46214,46215,46217,46218,46219,46220,46221,46222,46223,46224,46225,46226,46227,46228,46229,46230,46231,46232,46233,46234,46235,46236,46238,46239,46240,46241,46242,46243,46245,46246,46247,46249,46250,46251,46253,46254,46255,46256,46257,46258,46259,46260,46262,46264,46266,46267,46268,46269,46270,46271,46273,46274,46275,46277,46278,46279,46281,46282,46283,46284,46285,46286,46287,46289,46290,46291,46292,46294,46295,46296,46297,46298,46299,46302,46303,46305,46306,46309,46311,46312,46313,46314,46315,46318,46320,46322,46323,46324,46325,46326,46327,46329,46330,46331,46332,46333,46334,46335,46336,46337,46338,46339,46340,46341,46342,46343,46344,46345,46346,46347,46348,46349,46350,46351,46352,46353,46354,46355,46358,46359,46361,46362,46365,46366,46367,46368,46369,46370,46371,46374,46379,46380,46381,46382,46383,46386,46387,46389,46390,46391,46393,46394,46395,46396,46397,46398,46399,46402,46406,null,null,null,null,null,null,46407,46408,46409,46410,46414,46415,46417,46418,46419,46421,46422,46423,46424,46425,46426,46427,46430,46434,46435,46436,46437,46438,46439,46440,46441,46442,null,null,null,null,null,null,46443,46444,46445,46446,46447,46448,46449,46450,46451,46452,46453,46454,46455,46456,46457,46458,46459,46460,46461,46462,46463,46464,46465,46466,46467,46468,46469,46470,46471,46472,46473,46474,46475,46476,46477,46478,46479,46480,46481,46482,46483,46484,46485,46486,46487,46488,46489,46490,46491,46492,46493,46494,46495,46498,46499,46501,46502,46503,46505,46508,46509,46510,46511,46514,46518,46519,46520,46521,46522,46526,46527,46529,46530,46531,46533,46534,46535,46536,46537,46538,46539,46542,46546,46547,46548,46549,46550,46551,46553,46554,46555,46556,46557,46558,46559,46560,46561,46562,46563,46564,46565,46566,46567,46568,46569,46570,46571,46573,46574,46575,46576,46577,46578,46579,46580,46581,46582,46583,46584,46585,46586,46587,46588,46589,46590,46591,46592,46593,46594,46595,46596,46597,46598,46599,46600,46601,46602,46603,46604,46605,46606,46607,46610,46611,46613,46614,46615,46617,46618,46619,46620,46621,null,null,null,null,null,null,46622,46623,46624,46625,46626,46627,46628,46630,46631,46632,46633,46634,46635,46637,46638,46639,46640,46641,46642,46643,46645,46646,46647,46648,46649,46650,null,null,null,null,null,null,46651,46652,46653,46654,46655,46656,46657,46658,46659,46660,46661,46662,46663,46665,46666,46667,46668,46669,46670,46671,46672,46673,46674,46675,46676,46677,46678,46679,46680,46681,46682,46683,46684,46685,46686,46687,46688,46689,46690,46691,46693,46694,46695,46697,46698,46699,46700,46701,46702,46703,46704,46705,46706,46707,46708,46709,46710,46711,46712,46713,46714,46715,46716,46717,46718,46719,46720,46721,46722,46723,46724,46725,46726,46727,46728,46729,46730,46731,46732,46733,46734,46735,46736,46737,46738,46739,46740,46741,46742,46743,46744,46745,46746,46747,46750,46751,46753,46754,46755,46757,46758,46759,46760,46761,46762,46765,46766,46767,46768,46770,46771,46772,46773,46774,46775,46776,46777,46778,46779,46780,46781,46782,46783,46784,46785,46786,46787,46788,46789,46790,46791,46792,46793,46794,46795,46796,46797,46798,46799,46800,46801,46802,46803,46805,46806,46807,46808,46809,46810,46811,46812,46813,null,null,null,null,null,null,46814,46815,46816,46817,46818,46819,46820,46821,46822,46823,46824,46825,46826,46827,46828,46829,46830,46831,46833,46834,46835,46837,46838,46839,46841,46842,null,null,null,null,null,null,46843,46844,46845,46846,46847,46850,46851,46852,46854,46855,46856,46857,46858,46859,46860,46861,46862,46863,46864,46865,46866,46867,46868,46869,46870,46871,46872,46873,46874,46875,46876,46877,46878,46879,46880,46881,46882,46883,46884,46885,46886,46887,46890,46891,46893,46894,46897,46898,46899,46900,46901,46902,46903,46906,46908,46909,46910,46911,46912,46913,46914,46915,46917,46918,46919,46921,46922,46923,46925,46926,46927,46928,46929,46930,46931,46934,46935,46936,46937,46938,46939,46940,46941,46942,46943,46945,46946,46947,46949,46950,46951,46953,46954,46955,46956,46957,46958,46959,46962,46964,46966,46967,46968,46969,46970,46971,46974,46975,46977,46978,46979,46981,46982,46983,46984,46985,46986,46987,46990,46995,46996,46997,47002,47003,47005,47006,47007,47009,47010,47011,47012,47013,47014,47015,47018,47022,47023,47024,47025,47026,47027,47030,47031,47033,47034,47035,47036,47037,47038,47039,47040,47041,null,null,null,null,null,null,47042,47043,47044,47045,47046,47048,47050,47051,47052,47053,47054,47055,47056,47057,47058,47059,47060,47061,47062,47063,47064,47065,47066,47067,47068,47069,null,null,null,null,null,null,47070,47071,47072,47073,47074,47075,47076,47077,47078,47079,47080,47081,47082,47083,47086,47087,47089,47090,47091,47093,47094,47095,47096,47097,47098,47099,47102,47106,47107,47108,47109,47110,47114,47115,47117,47118,47119,47121,47122,47123,47124,47125,47126,47127,47130,47132,47134,47135,47136,47137,47138,47139,47142,47143,47145,47146,47147,47149,47150,47151,47152,47153,47154,47155,47158,47162,47163,47164,47165,47166,47167,47169,47170,47171,47173,47174,47175,47176,47177,47178,47179,47180,47181,47182,47183,47184,47186,47188,47189,47190,47191,47192,47193,47194,47195,47198,47199,47201,47202,47203,47205,47206,47207,47208,47209,47210,47211,47214,47216,47218,47219,47220,47221,47222,47223,47225,47226,47227,47229,47230,47231,47232,47233,47234,47235,47236,47237,47238,47239,47240,47241,47242,47243,47244,47246,47247,47248,47249,47250,47251,47252,47253,47254,47255,47256,47257,47258,47259,47260,47261,47262,47263,null,null,null,null,null,null,47264,47265,47266,47267,47268,47269,47270,47271,47273,47274,47275,47276,47277,47278,47279,47281,47282,47283,47285,47286,47287,47289,47290,47291,47292,47293,null,null,null,null,null,null,47294,47295,47298,47300,47302,47303,47304,47305,47306,47307,47309,47310,47311,47313,47314,47315,47317,47318,47319,47320,47321,47322,47323,47324,47326,47328,47330,47331,47332,47333,47334,47335,47338,47339,47341,47342,47343,47345,47346,47347,47348,47349,47350,47351,47354,47356,47358,47359,47360,47361,47362,47363,47365,47366,47367,47368,47369,47370,47371,47372,47373,47374,47375,47376,47377,47378,47379,47380,47381,47382,47383,47385,47386,47387,47388,47389,47390,47391,47393,47394,47395,47396,47397,47398,47399,47400,47401,47402,47403,47404,47405,47406,47407,47408,47409,47410,47411,47412,47413,47414,47415,47416,47417,47418,47419,47422,47423,47425,47426,47427,47429,47430,47431,47432,47433,47434,47435,47437,47438,47440,47442,47443,47444,47445,47446,47447,47450,47451,47453,47454,47455,47457,47458,47459,47460,47461,47462,47463,47466,47468,47470,47471,47472,47473,47474,47475,47478,47479,47481,47482,47483,47485,null,null,null,null,null,null,47486,47487,47488,47489,47490,47491,47494,47496,47499,47500,47503,47504,47505,47506,47507,47508,47509,47510,47511,47512,47513,47514,47515,47516,47517,47518,null,null,null,null,null,null,47519,47520,47521,47522,47523,47524,47525,47526,47527,47528,47529,47530,47531,47534,47535,47537,47538,47539,47541,47542,47543,47544,47545,47546,47547,47550,47552,47554,47555,47556,47557,47558,47559,47562,47563,47565,47571,47572,47573,47574,47575,47578,47580,47583,47584,47586,47590,47591,47593,47594,47595,47597,47598,47599,47600,47601,47602,47603,47606,47611,47612,47613,47614,47615,47618,47619,47620,47621,47622,47623,47625,47626,47627,47628,47629,47630,47631,47632,47633,47634,47635,47636,47638,47639,47640,47641,47642,47643,47644,47645,47646,47647,47648,47649,47650,47651,47652,47653,47654,47655,47656,47657,47658,47659,47660,47661,47662,47663,47664,47665,47666,47667,47668,47669,47670,47671,47674,47675,47677,47678,47679,47681,47683,47684,47685,47686,47687,47690,47692,47695,47696,47697,47698,47702,47703,47705,47706,47707,47709,47710,47711,47712,47713,47714,47715,47718,47722,47723,47724,47725,47726,47727,null,null,null,null,null,null,47730,47731,47733,47734,47735,47737,47738,47739,47740,47741,47742,47743,47744,47745,47746,47750,47752,47753,47754,47755,47757,47758,47759,47760,47761,47762,null,null,null,null,null,null,47763,47764,47765,47766,47767,47768,47769,47770,47771,47772,47773,47774,47775,47776,47777,47778,47779,47780,47781,47782,47783,47786,47789,47790,47791,47793,47795,47796,47797,47798,47799,47802,47804,47806,47807,47808,47809,47810,47811,47813,47814,47815,47817,47818,47819,47820,47821,47822,47823,47824,47825,47826,47827,47828,47829,47830,47831,47834,47835,47836,47837,47838,47839,47840,47841,47842,47843,47844,47845,47846,47847,47848,47849,47850,47851,47852,47853,47854,47855,47856,47857,47858,47859,47860,47861,47862,47863,47864,47865,47866,47867,47869,47870,47871,47873,47874,47875,47877,47878,47879,47880,47881,47882,47883,47884,47886,47888,47890,47891,47892,47893,47894,47895,47897,47898,47899,47901,47902,47903,47905,47906,47907,47908,47909,47910,47911,47912,47914,47916,47917,47918,47919,47920,47921,47922,47923,47927,47929,47930,47935,47936,47937,47938,47939,47942,47944,47946,47947,47948,47950,47953,47954,null,null,null,null,null,null,47955,47957,47958,47959,47961,47962,47963,47964,47965,47966,47967,47968,47970,47972,47973,47974,47975,47976,47977,47978,47979,47981,47982,47983,47984,47985,null,null,null,null,null,null,47986,47987,47988,47989,47990,47991,47992,47993,47994,47995,47996,47997,47998,47999,48000,48001,48002,48003,48004,48005,48006,48007,48009,48010,48011,48013,48014,48015,48017,48018,48019,48020,48021,48022,48023,48024,48025,48026,48027,48028,48029,48030,48031,48032,48033,48034,48035,48037,48038,48039,48041,48042,48043,48045,48046,48047,48048,48049,48050,48051,48053,48054,48056,48057,48058,48059,48060,48061,48062,48063,48065,48066,48067,48069,48070,48071,48073,48074,48075,48076,48077,48078,48079,48081,48082,48084,48085,48086,48087,48088,48089,48090,48091,48092,48093,48094,48095,48096,48097,48098,48099,48100,48101,48102,48103,48104,48105,48106,48107,48108,48109,48110,48111,48112,48113,48114,48115,48116,48117,48118,48119,48122,48123,48125,48126,48129,48131,48132,48133,48134,48135,48138,48142,48144,48146,48147,48153,48154,48160,48161,48162,48163,48166,48168,48170,48171,48172,48174,48175,48178,48179,48181,null,null,null,null,null,null,48182,48183,48185,48186,48187,48188,48189,48190,48191,48194,48198,48199,48200,48202,48203,48206,48207,48209,48210,48211,48212,48213,48214,48215,48216,48217,null,null,null,null,null,null,48218,48219,48220,48222,48223,48224,48225,48226,48227,48228,48229,48230,48231,48232,48233,48234,48235,48236,48237,48238,48239,48240,48241,48242,48243,48244,48245,48246,48247,48248,48249,48250,48251,48252,48253,48254,48255,48256,48257,48258,48259,48262,48263,48265,48266,48269,48271,48272,48273,48274,48275,48278,48280,48283,48284,48285,48286,48287,48290,48291,48293,48294,48297,48298,48299,48300,48301,48302,48303,48306,48310,48311,48312,48313,48314,48315,48318,48319,48321,48322,48323,48325,48326,48327,48328,48329,48330,48331,48332,48334,48338,48339,48340,48342,48343,48345,48346,48347,48349,48350,48351,48352,48353,48354,48355,48356,48357,48358,48359,48360,48361,48362,48363,48364,48365,48366,48367,48368,48369,48370,48371,48375,48377,48378,48379,48381,48382,48383,48384,48385,48386,48387,48390,48392,48394,48395,48396,48397,48398,48399,48401,48402,48403,48405,48406,48407,48408,48409,48410,48411,48412,48413,null,null,null,null,null,null,48414,48415,48416,48417,48418,48419,48421,48422,48423,48424,48425,48426,48427,48429,48430,48431,48432,48433,48434,48435,48436,48437,48438,48439,48440,48441,null,null,null,null,null,null,48442,48443,48444,48445,48446,48447,48449,48450,48451,48452,48453,48454,48455,48458,48459,48461,48462,48463,48465,48466,48467,48468,48469,48470,48471,48474,48475,48476,48477,48478,48479,48480,48481,48482,48483,48485,48486,48487,48489,48490,48491,48492,48493,48494,48495,48496,48497,48498,48499,48500,48501,48502,48503,48504,48505,48506,48507,48508,48509,48510,48511,48514,48515,48517,48518,48523,48524,48525,48526,48527,48530,48532,48534,48535,48536,48539,48541,48542,48543,48544,48545,48546,48547,48549,48550,48551,48552,48553,48554,48555,48556,48557,48558,48559,48561,48562,48563,48564,48565,48566,48567,48569,48570,48571,48572,48573,48574,48575,48576,48577,48578,48579,48580,48581,48582,48583,48584,48585,48586,48587,48588,48589,48590,48591,48592,48593,48594,48595,48598,48599,48601,48602,48603,48605,48606,48607,48608,48609,48610,48611,48612,48613,48614,48615,48616,48618,48619,48620,48621,48622,48623,48625,null,null,null,null,null,null,48626,48627,48629,48630,48631,48633,48634,48635,48636,48637,48638,48639,48641,48642,48644,48646,48647,48648,48649,48650,48651,48654,48655,48657,48658,48659,null,null,null,null,null,null,48661,48662,48663,48664,48665,48666,48667,48670,48672,48673,48674,48675,48676,48677,48678,48679,48680,48681,48682,48683,48684,48685,48686,48687,48688,48689,48690,48691,48692,48693,48694,48695,48696,48697,48698,48699,48700,48701,48702,48703,48704,48705,48706,48707,48710,48711,48713,48714,48715,48717,48719,48720,48721,48722,48723,48726,48728,48732,48733,48734,48735,48738,48739,48741,48742,48743,48745,48747,48748,48749,48750,48751,48754,48758,48759,48760,48761,48762,48766,48767,48769,48770,48771,48773,48774,48775,48776,48777,48778,48779,48782,48786,48787,48788,48789,48790,48791,48794,48795,48796,48797,48798,48799,48800,48801,48802,48803,48804,48805,48806,48807,48809,48810,48811,48812,48813,48814,48815,48816,48817,48818,48819,48820,48821,48822,48823,48824,48825,48826,48827,48828,48829,48830,48831,48832,48833,48834,48835,48836,48837,48838,48839,48840,48841,48842,48843,48844,48845,48846,48847,48850,48851,null,null,null,null,null,null,48853,48854,48857,48858,48859,48860,48861,48862,48863,48865,48866,48870,48871,48872,48873,48874,48875,48877,48878,48879,48880,48881,48882,48883,48884,48885,null,null,null,null,null,null,48886,48887,48888,48889,48890,48891,48892,48893,48894,48895,48896,48898,48899,48900,48901,48902,48903,48906,48907,48908,48909,48910,48911,48912,48913,48914,48915,48916,48917,48918,48919,48922,48926,48927,48928,48929,48930,48931,48932,48933,48934,48935,48936,48937,48938,48939,48940,48941,48942,48943,48944,48945,48946,48947,48948,48949,48950,48951,48952,48953,48954,48955,48956,48957,48958,48959,48962,48963,48965,48966,48967,48969,48970,48971,48972,48973,48974,48975,48978,48979,48980,48982,48983,48984,48985,48986,48987,48988,48989,48990,48991,48992,48993,48994,48995,48996,48997,48998,48999,49000,49001,49002,49003,49004,49005,49006,49007,49008,49009,49010,49011,49012,49013,49014,49015,49016,49017,49018,49019,49020,49021,49022,49023,49024,49025,49026,49027,49028,49029,49030,49031,49032,49033,49034,49035,49036,49037,49038,49039,49040,49041,49042,49043,49045,49046,49047,49048,49049,49050,49051,49052,49053,null,null,null,null,null,null,49054,49055,49056,49057,49058,49059,49060,49061,49062,49063,49064,49065,49066,49067,49068,49069,49070,49071,49073,49074,49075,49076,49077,49078,49079,49080,null,null,null,null,null,null,49081,49082,49083,49084,49085,49086,49087,49088,49089,49090,49091,49092,49094,49095,49096,49097,49098,49099,49102,49103,49105,49106,49107,49109,49110,49111,49112,49113,49114,49115,49117,49118,49120,49122,49123,49124,49125,49126,49127,49128,49129,49130,49131,49132,49133,49134,49135,49136,49137,49138,49139,49140,49141,49142,49143,49144,49145,49146,49147,49148,49149,49150,49151,49152,49153,49154,49155,49156,49157,49158,49159,49160,49161,49162,49163,49164,49165,49166,49167,49168,49169,49170,49171,49172,49173,49174,49175,49176,49177,49178,49179,49180,49181,49182,49183,49184,49185,49186,49187,49188,49189,49190,49191,49192,49193,49194,49195,49196,49197,49198,49199,49200,49201,49202,49203,49204,49205,49206,49207,49208,49209,49210,49211,49213,49214,49215,49216,49217,49218,49219,49220,49221,49222,49223,49224,49225,49226,49227,49228,49229,49230,49231,49232,49234,49235,49236,49237,49238,49239,49241,49242,49243,null,null,null,null,null,null,49245,49246,49247,49249,49250,49251,49252,49253,49254,49255,49258,49259,49260,49261,49262,49263,49264,49265,49266,49267,49268,49269,49270,49271,49272,49273,null,null,null,null,null,null,49274,49275,49276,49277,49278,49279,49280,49281,49282,49283,49284,49285,49286,49287,49288,49289,49290,49291,49292,49293,49294,49295,49298,49299,49301,49302,49303,49305,49306,49307,49308,49309,49310,49311,49314,49316,49318,49319,49320,49321,49322,49323,49326,49329,49330,49335,49336,49337,49338,49339,49342,49346,49347,49348,49350,49351,49354,49355,49357,49358,49359,49361,49362,49363,49364,49365,49366,49367,49370,49374,49375,49376,49377,49378,49379,49382,49383,49385,49386,49387,49389,49390,49391,49392,49393,49394,49395,49398,49400,49402,49403,49404,49405,49406,49407,49409,49410,49411,49413,49414,49415,49417,49418,49419,49420,49421,49422,49423,49425,49426,49427,49428,49430,49431,49432,49433,49434,49435,49441,49442,49445,49448,49449,49450,49451,49454,49458,49459,49460,49461,49463,49466,49467,49469,49470,49471,49473,49474,49475,49476,49477,49478,49479,49482,49486,49487,49488,49489,49490,49491,49494,49495,null,null,null,null,null,null,49497,49498,49499,49501,49502,49503,49504,49505,49506,49507,49510,49514,49515,49516,49517,49518,49519,49521,49522,49523,49525,49526,49527,49529,49530,49531,null,null,null,null,null,null,49532,49533,49534,49535,49536,49537,49538,49539,49540,49542,49543,49544,49545,49546,49547,49551,49553,49554,49555,49557,49559,49560,49561,49562,49563,49566,49568,49570,49571,49572,49574,49575,49578,49579,49581,49582,49583,49585,49586,49587,49588,49589,49590,49591,49592,49593,49594,49595,49596,49598,49599,49600,49601,49602,49603,49605,49606,49607,49609,49610,49611,49613,49614,49615,49616,49617,49618,49619,49621,49622,49625,49626,49627,49628,49629,49630,49631,49633,49634,49635,49637,49638,49639,49641,49642,49643,49644,49645,49646,49647,49650,49652,49653,49654,49655,49656,49657,49658,49659,49662,49663,49665,49666,49667,49669,49670,49671,49672,49673,49674,49675,49678,49680,49682,49683,49684,49685,49686,49687,49690,49691,49693,49694,49697,49698,49699,49700,49701,49702,49703,49706,49708,49710,49712,49715,49717,49718,49719,49720,49721,49722,49723,49724,49725,49726,49727,49728,49729,49730,49731,49732,49733,null,null,null,null,null,null,49734,49735,49737,49738,49739,49740,49741,49742,49743,49746,49747,49749,49750,49751,49753,49754,49755,49756,49757,49758,49759,49761,49762,49763,49764,49766,null,null,null,null,null,null,49767,49768,49769,49770,49771,49774,49775,49777,49778,49779,49781,49782,49783,49784,49785,49786,49787,49790,49792,49794,49795,49796,49797,49798,49799,49802,49803,49804,49805,49806,49807,49809,49810,49811,49812,49813,49814,49815,49817,49818,49820,49822,49823,49824,49825,49826,49827,49830,49831,49833,49834,49835,49838,49839,49840,49841,49842,49843,49846,49848,49850,49851,49852,49853,49854,49855,49856,49857,49858,49859,49860,49861,49862,49863,49864,49865,49866,49867,49868,49869,49870,49871,49872,49873,49874,49875,49876,49877,49878,49879,49880,49881,49882,49883,49886,49887,49889,49890,49893,49894,49895,49896,49897,49898,49902,49904,49906,49907,49908,49909,49911,49914,49917,49918,49919,49921,49922,49923,49924,49925,49926,49927,49930,49931,49934,49935,49936,49937,49938,49942,49943,49945,49946,49947,49949,49950,49951,49952,49953,49954,49955,49958,49959,49962,49963,49964,49965,49966,49967,49968,49969,49970,null,null,null,null,null,null,49971,49972,49973,49974,49975,49976,49977,49978,49979,49980,49981,49982,49983,49984,49985,49986,49987,49988,49990,49991,49992,49993,49994,49995,49996,49997,null,null,null,null,null,null,49998,49999,50000,50001,50002,50003,50004,50005,50006,50007,50008,50009,50010,50011,50012,50013,50014,50015,50016,50017,50018,50019,50020,50021,50022,50023,50026,50027,50029,50030,50031,50033,50035,50036,50037,50038,50039,50042,50043,50046,50047,50048,50049,50050,50051,50053,50054,50055,50057,50058,50059,50061,50062,50063,50064,50065,50066,50067,50068,50069,50070,50071,50072,50073,50074,50075,50076,50077,50078,50079,50080,50081,50082,50083,50084,50085,50086,50087,50088,50089,50090,50091,50092,50093,50094,50095,50096,50097,50098,50099,50100,50101,50102,50103,50104,50105,50106,50107,50108,50109,50110,50111,50113,50114,50115,50116,50117,50118,50119,50120,50121,50122,50123,50124,50125,50126,50127,50128,50129,50130,50131,50132,50133,50134,50135,50138,50139,50141,50142,50145,50147,50148,50149,50150,50151,50154,50155,50156,50158,50159,50160,50161,50162,50163,50166,50167,50169,50170,50171,50172,50173,50174,null,null,null,null,null,null,50175,50176,50177,50178,50179,50180,50181,50182,50183,50185,50186,50187,50188,50189,50190,50191,50193,50194,50195,50196,50197,50198,50199,50200,50201,50202,null,null,null,null,null,null,50203,50204,50205,50206,50207,50208,50209,50210,50211,50213,50214,50215,50216,50217,50218,50219,50221,50222,50223,50225,50226,50227,50229,50230,50231,50232,50233,50234,50235,50238,50239,50240,50241,50242,50243,50244,50245,50246,50247,50249,50250,50251,50252,50253,50254,50255,50256,50257,50258,50259,50260,50261,50262,50263,50264,50265,50266,50267,50268,50269,50270,50271,50272,50273,50274,50275,50278,50279,50281,50282,50283,50285,50286,50287,50288,50289,50290,50291,50294,50295,50296,50298,50299,50300,50301,50302,50303,50305,50306,50307,50308,50309,50310,50311,50312,50313,50314,50315,50316,50317,50318,50319,50320,50321,50322,50323,50325,50326,50327,50328,50329,50330,50331,50333,50334,50335,50336,50337,50338,50339,50340,50341,50342,50343,50344,50345,50346,50347,50348,50349,50350,50351,50352,50353,50354,50355,50356,50357,50358,50359,50361,50362,50363,50365,50366,50367,50368,50369,50370,50371,50372,50373,null,null,null,null,null,null,50374,50375,50376,50377,50378,50379,50380,50381,50382,50383,50384,50385,50386,50387,50388,50389,50390,50391,50392,50393,50394,50395,50396,50397,50398,50399,null,null,null,null,null,null,50400,50401,50402,50403,50404,50405,50406,50407,50408,50410,50411,50412,50413,50414,50415,50418,50419,50421,50422,50423,50425,50427,50428,50429,50430,50434,50435,50436,50437,50438,50439,50440,50441,50442,50443,50445,50446,50447,50449,50450,50451,50453,50454,50455,50456,50457,50458,50459,50461,50462,50463,50464,50465,50466,50467,50468,50469,50470,50471,50474,50475,50477,50478,50479,50481,50482,50483,50484,50485,50486,50487,50490,50492,50494,50495,50496,50497,50498,50499,50502,50503,50507,50511,50512,50513,50514,50518,50522,50523,50524,50527,50530,50531,50533,50534,50535,50537,50538,50539,50540,50541,50542,50543,50546,50550,50551,50552,50553,50554,50555,50558,50559,50561,50562,50563,50565,50566,50568,50569,50570,50571,50574,50576,50578,50579,50580,50582,50585,50586,50587,50589,50590,50591,50593,50594,50595,50596,50597,50598,50599,50600,50602,50603,50604,50605,50606,50607,50608,50609,50610,50611,50614,null,null,null,null,null,null,50615,50618,50623,50624,50625,50626,50627,50635,50637,50639,50642,50643,50645,50646,50647,50649,50650,50651,50652,50653,50654,50655,50658,50660,50662,50663,null,null,null,null,null,null,50664,50665,50666,50667,50671,50673,50674,50675,50677,50680,50681,50682,50683,50690,50691,50692,50697,50698,50699,50701,50702,50703,50705,50706,50707,50708,50709,50710,50711,50714,50717,50718,50719,50720,50721,50722,50723,50726,50727,50729,50730,50731,50735,50737,50738,50742,50744,50746,50748,50749,50750,50751,50754,50755,50757,50758,50759,50761,50762,50763,50764,50765,50766,50767,50770,50774,50775,50776,50777,50778,50779,50782,50783,50785,50786,50787,50788,50789,50790,50791,50792,50793,50794,50795,50797,50798,50800,50802,50803,50804,50805,50806,50807,50810,50811,50813,50814,50815,50817,50818,50819,50820,50821,50822,50823,50826,50828,50830,50831,50832,50833,50834,50835,50838,50839,50841,50842,50843,50845,50846,50847,50848,50849,50850,50851,50854,50856,50858,50859,50860,50861,50862,50863,50866,50867,50869,50870,50871,50875,50876,50877,50878,50879,50882,50884,50886,50887,50888,50889,50890,50891,50894,null,null,null,null,null,null,50895,50897,50898,50899,50901,50902,50903,50904,50905,50906,50907,50910,50911,50914,50915,50916,50917,50918,50919,50922,50923,50925,50926,50927,50929,50930,null,null,null,null,null,null,50931,50932,50933,50934,50935,50938,50939,50940,50942,50943,50944,50945,50946,50947,50950,50951,50953,50954,50955,50957,50958,50959,50960,50961,50962,50963,50966,50968,50970,50971,50972,50973,50974,50975,50978,50979,50981,50982,50983,50985,50986,50987,50988,50989,50990,50991,50994,50996,50998,51000,51001,51002,51003,51006,51007,51009,51010,51011,51013,51014,51015,51016,51017,51019,51022,51024,51033,51034,51035,51037,51038,51039,51041,51042,51043,51044,51045,51046,51047,51049,51050,51052,51053,51054,51055,51056,51057,51058,51059,51062,51063,51065,51066,51067,51071,51072,51073,51074,51078,51083,51084,51085,51087,51090,51091,51093,51097,51099,51100,51101,51102,51103,51106,51111,51112,51113,51114,51115,51118,51119,51121,51122,51123,51125,51126,51127,51128,51129,51130,51131,51134,51138,51139,51140,51141,51142,51143,51146,51147,51149,51151,51153,51154,51155,51156,51157,51158,51159,51161,51162,51163,51164,null,null,null,null,null,null,51166,51167,51168,51169,51170,51171,51173,51174,51175,51177,51178,51179,51181,51182,51183,51184,51185,51186,51187,51188,51189,51190,51191,51192,51193,51194,null,null,null,null,null,null,51195,51196,51197,51198,51199,51202,51203,51205,51206,51207,51209,51211,51212,51213,51214,51215,51218,51220,51223,51224,51225,51226,51227,51230,51231,51233,51234,51235,51237,51238,51239,51240,51241,51242,51243,51246,51248,51250,51251,51252,51253,51254,51255,51257,51258,51259,51261,51262,51263,51265,51266,51267,51268,51269,51270,51271,51274,51275,51278,51279,51280,51281,51282,51283,51285,51286,51287,51288,51289,51290,51291,51292,51293,51294,51295,51296,51297,51298,51299,51300,51301,51302,51303,51304,51305,51306,51307,51308,51309,51310,51311,51314,51315,51317,51318,51319,51321,51323,51324,51325,51326,51327,51330,51332,51336,51337,51338,51342,51343,51344,51345,51346,51347,51349,51350,51351,51352,51353,51354,51355,51356,51358,51360,51362,51363,51364,51365,51366,51367,51369,51370,51371,51372,51373,51374,51375,51376,51377,51378,51379,51380,51381,51382,51383,51384,51385,51386,51387,51390,51391,51392,51393,null,null,null,null,null,null,51394,51395,51397,51398,51399,51401,51402,51403,51405,51406,51407,51408,51409,51410,51411,51414,51416,51418,51419,51420,51421,51422,51423,51426,51427,51429,null,null,null,null,null,null,51430,51431,51432,51433,51434,51435,51436,51437,51438,51439,51440,51441,51442,51443,51444,51446,51447,51448,51449,51450,51451,51454,51455,51457,51458,51459,51463,51464,51465,51466,51467,51470,12288,12289,12290,183,8229,8230,168,12291,173,8213,8741,65340,8764,8216,8217,8220,8221,12308,12309,12296,12297,12298,12299,12300,12301,12302,12303,12304,12305,177,215,247,8800,8804,8805,8734,8756,176,8242,8243,8451,8491,65504,65505,65509,9794,9792,8736,8869,8978,8706,8711,8801,8786,167,8251,9734,9733,9675,9679,9678,9671,9670,9633,9632,9651,9650,9661,9660,8594,8592,8593,8595,8596,12307,8810,8811,8730,8765,8733,8757,8747,8748,8712,8715,8838,8839,8834,8835,8746,8745,8743,8744,65506,51472,51474,51475,51476,51477,51478,51479,51481,51482,51483,51484,51485,51486,51487,51488,51489,51490,51491,51492,51493,51494,51495,51496,51497,51498,51499,null,null,null,null,null,null,51501,51502,51503,51504,51505,51506,51507,51509,51510,51511,51512,51513,51514,51515,51516,51517,51518,51519,51520,51521,51522,51523,51524,51525,51526,51527,null,null,null,null,null,null,51528,51529,51530,51531,51532,51533,51534,51535,51538,51539,51541,51542,51543,51545,51546,51547,51548,51549,51550,51551,51554,51556,51557,51558,51559,51560,51561,51562,51563,51565,51566,51567,8658,8660,8704,8707,180,65374,711,728,733,730,729,184,731,161,191,720,8750,8721,8719,164,8457,8240,9665,9664,9655,9654,9828,9824,9825,9829,9831,9827,8857,9672,9635,9680,9681,9618,9636,9637,9640,9639,9638,9641,9832,9743,9742,9756,9758,182,8224,8225,8597,8599,8601,8598,8600,9837,9833,9834,9836,12927,12828,8470,13255,8482,13250,13272,8481,8364,174,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,51569,51570,51571,51573,51574,51575,51576,51577,51578,51579,51581,51582,51583,51584,51585,51586,51587,51588,51589,51590,51591,51594,51595,51597,51598,51599,null,null,null,null,null,null,51601,51602,51603,51604,51605,51606,51607,51610,51612,51614,51615,51616,51617,51618,51619,51620,51621,51622,51623,51624,51625,51626,51627,51628,51629,51630,null,null,null,null,null,null,51631,51632,51633,51634,51635,51636,51637,51638,51639,51640,51641,51642,51643,51644,51645,51646,51647,51650,51651,51653,51654,51657,51659,51660,51661,51662,51663,51666,51668,51671,51672,51675,65281,65282,65283,65284,65285,65286,65287,65288,65289,65290,65291,65292,65293,65294,65295,65296,65297,65298,65299,65300,65301,65302,65303,65304,65305,65306,65307,65308,65309,65310,65311,65312,65313,65314,65315,65316,65317,65318,65319,65320,65321,65322,65323,65324,65325,65326,65327,65328,65329,65330,65331,65332,65333,65334,65335,65336,65337,65338,65339,65510,65341,65342,65343,65344,65345,65346,65347,65348,65349,65350,65351,65352,65353,65354,65355,65356,65357,65358,65359,65360,65361,65362,65363,65364,65365,65366,65367,65368,65369,65370,65371,65372,65373,65507,51678,51679,51681,51683,51685,51686,51688,51689,51690,51691,51694,51698,51699,51700,51701,51702,51703,51706,51707,51709,51710,51711,51713,51714,51715,51716,null,null,null,null,null,null,51717,51718,51719,51722,51726,51727,51728,51729,51730,51731,51733,51734,51735,51737,51738,51739,51740,51741,51742,51743,51744,51745,51746,51747,51748,51749,null,null,null,null,null,null,51750,51751,51752,51754,51755,51756,51757,51758,51759,51760,51761,51762,51763,51764,51765,51766,51767,51768,51769,51770,51771,51772,51773,51774,51775,51776,51777,51778,51779,51780,51781,51782,12593,12594,12595,12596,12597,12598,12599,12600,12601,12602,12603,12604,12605,12606,12607,12608,12609,12610,12611,12612,12613,12614,12615,12616,12617,12618,12619,12620,12621,12622,12623,12624,12625,12626,12627,12628,12629,12630,12631,12632,12633,12634,12635,12636,12637,12638,12639,12640,12641,12642,12643,12644,12645,12646,12647,12648,12649,12650,12651,12652,12653,12654,12655,12656,12657,12658,12659,12660,12661,12662,12663,12664,12665,12666,12667,12668,12669,12670,12671,12672,12673,12674,12675,12676,12677,12678,12679,12680,12681,12682,12683,12684,12685,12686,51783,51784,51785,51786,51787,51790,51791,51793,51794,51795,51797,51798,51799,51800,51801,51802,51803,51806,51810,51811,51812,51813,51814,51815,51817,51818,null,null,null,null,null,null,51819,51820,51821,51822,51823,51824,51825,51826,51827,51828,51829,51830,51831,51832,51833,51834,51835,51836,51838,51839,51840,51841,51842,51843,51845,51846,null,null,null,null,null,null,51847,51848,51849,51850,51851,51852,51853,51854,51855,51856,51857,51858,51859,51860,51861,51862,51863,51865,51866,51867,51868,51869,51870,51871,51872,51873,51874,51875,51876,51877,51878,51879,8560,8561,8562,8563,8564,8565,8566,8567,8568,8569,null,null,null,null,null,8544,8545,8546,8547,8548,8549,8550,8551,8552,8553,null,null,null,null,null,null,null,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,931,932,933,934,935,936,937,null,null,null,null,null,null,null,null,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,963,964,965,966,967,968,969,null,null,null,null,null,null,51880,51881,51882,51883,51884,51885,51886,51887,51888,51889,51890,51891,51892,51893,51894,51895,51896,51897,51898,51899,51902,51903,51905,51906,51907,51909,null,null,null,null,null,null,51910,51911,51912,51913,51914,51915,51918,51920,51922,51924,51925,51926,51927,51930,51931,51932,51933,51934,51935,51937,51938,51939,51940,51941,51942,51943,null,null,null,null,null,null,51944,51945,51946,51947,51949,51950,51951,51952,51953,51954,51955,51957,51958,51959,51960,51961,51962,51963,51964,51965,51966,51967,51968,51969,51970,51971,51972,51973,51974,51975,51977,51978,9472,9474,9484,9488,9496,9492,9500,9516,9508,9524,9532,9473,9475,9487,9491,9499,9495,9507,9523,9515,9531,9547,9504,9519,9512,9527,9535,9501,9520,9509,9528,9538,9490,9489,9498,9497,9494,9493,9486,9485,9502,9503,9505,9506,9510,9511,9513,9514,9517,9518,9521,9522,9525,9526,9529,9530,9533,9534,9536,9537,9539,9540,9541,9542,9543,9544,9545,9546,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,51979,51980,51981,51982,51983,51985,51986,51987,51989,51990,51991,51993,51994,51995,51996,51997,51998,51999,52002,52003,52004,52005,52006,52007,52008,52009,null,null,null,null,null,null,52010,52011,52012,52013,52014,52015,52016,52017,52018,52019,52020,52021,52022,52023,52024,52025,52026,52027,52028,52029,52030,52031,52032,52034,52035,52036,null,null,null,null,null,null,52037,52038,52039,52042,52043,52045,52046,52047,52049,52050,52051,52052,52053,52054,52055,52058,52059,52060,52062,52063,52064,52065,52066,52067,52069,52070,52071,52072,52073,52074,52075,52076,13205,13206,13207,8467,13208,13252,13219,13220,13221,13222,13209,13210,13211,13212,13213,13214,13215,13216,13217,13218,13258,13197,13198,13199,13263,13192,13193,13256,13223,13224,13232,13233,13234,13235,13236,13237,13238,13239,13240,13241,13184,13185,13186,13187,13188,13242,13243,13244,13245,13246,13247,13200,13201,13202,13203,13204,8486,13248,13249,13194,13195,13196,13270,13253,13229,13230,13231,13275,13225,13226,13227,13228,13277,13264,13267,13251,13257,13276,13254,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,52077,52078,52079,52080,52081,52082,52083,52084,52085,52086,52087,52090,52091,52092,52093,52094,52095,52096,52097,52098,52099,52100,52101,52102,52103,52104,null,null,null,null,null,null,52105,52106,52107,52108,52109,52110,52111,52112,52113,52114,52115,52116,52117,52118,52119,52120,52121,52122,52123,52125,52126,52127,52128,52129,52130,52131,null,null,null,null,null,null,52132,52133,52134,52135,52136,52137,52138,52139,52140,52141,52142,52143,52144,52145,52146,52147,52148,52149,52150,52151,52153,52154,52155,52156,52157,52158,52159,52160,52161,52162,52163,52164,198,208,170,294,null,306,null,319,321,216,338,186,222,358,330,null,12896,12897,12898,12899,12900,12901,12902,12903,12904,12905,12906,12907,12908,12909,12910,12911,12912,12913,12914,12915,12916,12917,12918,12919,12920,12921,12922,12923,9424,9425,9426,9427,9428,9429,9430,9431,9432,9433,9434,9435,9436,9437,9438,9439,9440,9441,9442,9443,9444,9445,9446,9447,9448,9449,9312,9313,9314,9315,9316,9317,9318,9319,9320,9321,9322,9323,9324,9325,9326,189,8531,8532,188,190,8539,8540,8541,8542,52165,52166,52167,52168,52169,52170,52171,52172,52173,52174,52175,52176,52177,52178,52179,52181,52182,52183,52184,52185,52186,52187,52188,52189,52190,52191,null,null,null,null,null,null,52192,52193,52194,52195,52197,52198,52200,52202,52203,52204,52205,52206,52207,52208,52209,52210,52211,52212,52213,52214,52215,52216,52217,52218,52219,52220,null,null,null,null,null,null,52221,52222,52223,52224,52225,52226,52227,52228,52229,52230,52231,52232,52233,52234,52235,52238,52239,52241,52242,52243,52245,52246,52247,52248,52249,52250,52251,52254,52255,52256,52259,52260,230,273,240,295,305,307,312,320,322,248,339,223,254,359,331,329,12800,12801,12802,12803,12804,12805,12806,12807,12808,12809,12810,12811,12812,12813,12814,12815,12816,12817,12818,12819,12820,12821,12822,12823,12824,12825,12826,12827,9372,9373,9374,9375,9376,9377,9378,9379,9380,9381,9382,9383,9384,9385,9386,9387,9388,9389,9390,9391,9392,9393,9394,9395,9396,9397,9332,9333,9334,9335,9336,9337,9338,9339,9340,9341,9342,9343,9344,9345,9346,185,178,179,8308,8319,8321,8322,8323,8324,52261,52262,52266,52267,52269,52271,52273,52274,52275,52276,52277,52278,52279,52282,52287,52288,52289,52290,52291,52294,52295,52297,52298,52299,52301,52302,null,null,null,null,null,null,52303,52304,52305,52306,52307,52310,52314,52315,52316,52317,52318,52319,52321,52322,52323,52325,52327,52329,52330,52331,52332,52333,52334,52335,52337,52338,null,null,null,null,null,null,52339,52340,52342,52343,52344,52345,52346,52347,52348,52349,52350,52351,52352,52353,52354,52355,52356,52357,52358,52359,52360,52361,52362,52363,52364,52365,52366,52367,52368,52369,52370,52371,12353,12354,12355,12356,12357,12358,12359,12360,12361,12362,12363,12364,12365,12366,12367,12368,12369,12370,12371,12372,12373,12374,12375,12376,12377,12378,12379,12380,12381,12382,12383,12384,12385,12386,12387,12388,12389,12390,12391,12392,12393,12394,12395,12396,12397,12398,12399,12400,12401,12402,12403,12404,12405,12406,12407,12408,12409,12410,12411,12412,12413,12414,12415,12416,12417,12418,12419,12420,12421,12422,12423,12424,12425,12426,12427,12428,12429,12430,12431,12432,12433,12434,12435,null,null,null,null,null,null,null,null,null,null,null,52372,52373,52374,52375,52378,52379,52381,52382,52383,52385,52386,52387,52388,52389,52390,52391,52394,52398,52399,52400,52401,52402,52403,52406,52407,52409,null,null,null,null,null,null,52410,52411,52413,52414,52415,52416,52417,52418,52419,52422,52424,52426,52427,52428,52429,52430,52431,52433,52434,52435,52437,52438,52439,52440,52441,52442,null,null,null,null,null,null,52443,52444,52445,52446,52447,52448,52449,52450,52451,52453,52454,52455,52456,52457,52458,52459,52461,52462,52463,52465,52466,52467,52468,52469,52470,52471,52472,52473,52474,52475,52476,52477,12449,12450,12451,12452,12453,12454,12455,12456,12457,12458,12459,12460,12461,12462,12463,12464,12465,12466,12467,12468,12469,12470,12471,12472,12473,12474,12475,12476,12477,12478,12479,12480,12481,12482,12483,12484,12485,12486,12487,12488,12489,12490,12491,12492,12493,12494,12495,12496,12497,12498,12499,12500,12501,12502,12503,12504,12505,12506,12507,12508,12509,12510,12511,12512,12513,12514,12515,12516,12517,12518,12519,12520,12521,12522,12523,12524,12525,12526,12527,12528,12529,12530,12531,12532,12533,12534,null,null,null,null,null,null,null,null,52478,52479,52480,52482,52483,52484,52485,52486,52487,52490,52491,52493,52494,52495,52497,52498,52499,52500,52501,52502,52503,52506,52508,52510,52511,52512,null,null,null,null,null,null,52513,52514,52515,52517,52518,52519,52521,52522,52523,52525,52526,52527,52528,52529,52530,52531,52532,52533,52534,52535,52536,52538,52539,52540,52541,52542,null,null,null,null,null,null,52543,52544,52545,52546,52547,52548,52549,52550,52551,52552,52553,52554,52555,52556,52557,52558,52559,52560,52561,52562,52563,52564,52565,52566,52567,52568,52569,52570,52571,52573,52574,52575,1040,1041,1042,1043,1044,1045,1025,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,1072,1073,1074,1075,1076,1077,1105,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,null,null,null,null,null,null,null,null,null,null,null,null,null,52577,52578,52579,52581,52582,52583,52584,52585,52586,52587,52590,52592,52594,52595,52596,52597,52598,52599,52601,52602,52603,52604,52605,52606,52607,52608,null,null,null,null,null,null,52609,52610,52611,52612,52613,52614,52615,52617,52618,52619,52620,52621,52622,52623,52624,52625,52626,52627,52630,52631,52633,52634,52635,52637,52638,52639,null,null,null,null,null,null,52640,52641,52642,52643,52646,52648,52650,52651,52652,52653,52654,52655,52657,52658,52659,52660,52661,52662,52663,52664,52665,52666,52667,52668,52669,52670,52671,52672,52673,52674,52675,52677,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,52678,52679,52680,52681,52682,52683,52685,52686,52687,52689,52690,52691,52692,52693,52694,52695,52696,52697,52698,52699,52700,52701,52702,52703,52704,52705,null,null,null,null,null,null,52706,52707,52708,52709,52710,52711,52713,52714,52715,52717,52718,52719,52721,52722,52723,52724,52725,52726,52727,52730,52732,52734,52735,52736,52737,52738,null,null,null,null,null,null,52739,52741,52742,52743,52745,52746,52747,52749,52750,52751,52752,52753,52754,52755,52757,52758,52759,52760,52762,52763,52764,52765,52766,52767,52770,52771,52773,52774,52775,52777,52778,52779,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,52780,52781,52782,52783,52786,52788,52790,52791,52792,52793,52794,52795,52796,52797,52798,52799,52800,52801,52802,52803,52804,52805,52806,52807,52808,52809,null,null,null,null,null,null,52810,52811,52812,52813,52814,52815,52816,52817,52818,52819,52820,52821,52822,52823,52826,52827,52829,52830,52834,52835,52836,52837,52838,52839,52842,52844,null,null,null,null,null,null,52846,52847,52848,52849,52850,52851,52854,52855,52857,52858,52859,52861,52862,52863,52864,52865,52866,52867,52870,52872,52874,52875,52876,52877,52878,52879,52882,52883,52885,52886,52887,52889,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,52890,52891,52892,52893,52894,52895,52898,52902,52903,52904,52905,52906,52907,52910,52911,52912,52913,52914,52915,52916,52917,52918,52919,52920,52921,52922,null,null,null,null,null,null,52923,52924,52925,52926,52927,52928,52930,52931,52932,52933,52934,52935,52936,52937,52938,52939,52940,52941,52942,52943,52944,52945,52946,52947,52948,52949,null,null,null,null,null,null,52950,52951,52952,52953,52954,52955,52956,52957,52958,52959,52960,52961,52962,52963,52966,52967,52969,52970,52973,52974,52975,52976,52977,52978,52979,52982,52986,52987,52988,52989,52990,52991,44032,44033,44036,44039,44040,44041,44042,44048,44049,44050,44051,44052,44053,44054,44055,44057,44058,44059,44060,44061,44064,44068,44076,44077,44079,44080,44081,44088,44089,44092,44096,44107,44109,44116,44120,44124,44144,44145,44148,44151,44152,44154,44160,44161,44163,44164,44165,44166,44169,44170,44171,44172,44176,44180,44188,44189,44191,44192,44193,44200,44201,44202,44204,44207,44208,44216,44217,44219,44220,44221,44225,44228,44232,44236,44245,44247,44256,44257,44260,44263,44264,44266,44268,44271,44272,44273,44275,44277,44278,44284,44285,44288,44292,44294,52994,52995,52997,52998,52999,53001,53002,53003,53004,53005,53006,53007,53010,53012,53014,53015,53016,53017,53018,53019,53021,53022,53023,53025,53026,53027,null,null,null,null,null,null,53029,53030,53031,53032,53033,53034,53035,53038,53042,53043,53044,53045,53046,53047,53049,53050,53051,53052,53053,53054,53055,53056,53057,53058,53059,53060,null,null,null,null,null,null,53061,53062,53063,53064,53065,53066,53067,53068,53069,53070,53071,53072,53073,53074,53075,53078,53079,53081,53082,53083,53085,53086,53087,53088,53089,53090,53091,53094,53096,53098,53099,53100,44300,44301,44303,44305,44312,44316,44320,44329,44332,44333,44340,44341,44344,44348,44356,44357,44359,44361,44368,44372,44376,44385,44387,44396,44397,44400,44403,44404,44405,44406,44411,44412,44413,44415,44417,44418,44424,44425,44428,44432,44444,44445,44452,44471,44480,44481,44484,44488,44496,44497,44499,44508,44512,44516,44536,44537,44540,44543,44544,44545,44552,44553,44555,44557,44564,44592,44593,44596,44599,44600,44602,44608,44609,44611,44613,44614,44618,44620,44621,44622,44624,44628,44630,44636,44637,44639,44640,44641,44645,44648,44649,44652,44656,44664,53101,53102,53103,53106,53107,53109,53110,53111,53113,53114,53115,53116,53117,53118,53119,53121,53122,53123,53124,53126,53127,53128,53129,53130,53131,53133,null,null,null,null,null,null,53134,53135,53136,53137,53138,53139,53140,53141,53142,53143,53144,53145,53146,53147,53148,53149,53150,53151,53152,53154,53155,53156,53157,53158,53159,53161,null,null,null,null,null,null,53162,53163,53164,53165,53166,53167,53169,53170,53171,53172,53173,53174,53175,53176,53177,53178,53179,53180,53181,53182,53183,53184,53185,53186,53187,53189,53190,53191,53192,53193,53194,53195,44665,44667,44668,44669,44676,44677,44684,44732,44733,44734,44736,44740,44748,44749,44751,44752,44753,44760,44761,44764,44776,44779,44781,44788,44792,44796,44807,44808,44813,44816,44844,44845,44848,44850,44852,44860,44861,44863,44865,44866,44867,44872,44873,44880,44892,44893,44900,44901,44921,44928,44932,44936,44944,44945,44949,44956,44984,44985,44988,44992,44999,45000,45001,45003,45005,45006,45012,45020,45032,45033,45040,45041,45044,45048,45056,45057,45060,45068,45072,45076,45084,45085,45096,45124,45125,45128,45130,45132,45134,45139,45140,45141,45143,45145,53196,53197,53198,53199,53200,53201,53202,53203,53204,53205,53206,53207,53208,53209,53210,53211,53212,53213,53214,53215,53218,53219,53221,53222,53223,53225,null,null,null,null,null,null,53226,53227,53228,53229,53230,53231,53234,53236,53238,53239,53240,53241,53242,53243,53245,53246,53247,53249,53250,53251,53253,53254,53255,53256,53257,53258,null,null,null,null,null,null,53259,53260,53261,53262,53263,53264,53266,53267,53268,53269,53270,53271,53273,53274,53275,53276,53277,53278,53279,53280,53281,53282,53283,53284,53285,53286,53287,53288,53289,53290,53291,53292,45149,45180,45181,45184,45188,45196,45197,45199,45201,45208,45209,45210,45212,45215,45216,45217,45218,45224,45225,45227,45228,45229,45230,45231,45233,45235,45236,45237,45240,45244,45252,45253,45255,45256,45257,45264,45265,45268,45272,45280,45285,45320,45321,45323,45324,45328,45330,45331,45336,45337,45339,45340,45341,45347,45348,45349,45352,45356,45364,45365,45367,45368,45369,45376,45377,45380,45384,45392,45393,45396,45397,45400,45404,45408,45432,45433,45436,45440,45442,45448,45449,45451,45453,45458,45459,45460,45464,45468,45480,45516,45520,45524,45532,45533,53294,53295,53296,53297,53298,53299,53302,53303,53305,53306,53307,53309,53310,53311,53312,53313,53314,53315,53318,53320,53322,53323,53324,53325,53326,53327,null,null,null,null,null,null,53329,53330,53331,53333,53334,53335,53337,53338,53339,53340,53341,53342,53343,53345,53346,53347,53348,53349,53350,53351,53352,53353,53354,53355,53358,53359,null,null,null,null,null,null,53361,53362,53363,53365,53366,53367,53368,53369,53370,53371,53374,53375,53376,53378,53379,53380,53381,53382,53383,53384,53385,53386,53387,53388,53389,53390,53391,53392,53393,53394,53395,53396,45535,45544,45545,45548,45552,45561,45563,45565,45572,45573,45576,45579,45580,45588,45589,45591,45593,45600,45620,45628,45656,45660,45664,45672,45673,45684,45685,45692,45700,45701,45705,45712,45713,45716,45720,45721,45722,45728,45729,45731,45733,45734,45738,45740,45744,45748,45768,45769,45772,45776,45778,45784,45785,45787,45789,45794,45796,45797,45798,45800,45803,45804,45805,45806,45807,45811,45812,45813,45815,45816,45817,45818,45819,45823,45824,45825,45828,45832,45840,45841,45843,45844,45845,45852,45908,45909,45910,45912,45915,45916,45918,45919,45924,45925,53397,53398,53399,53400,53401,53402,53403,53404,53405,53406,53407,53408,53409,53410,53411,53414,53415,53417,53418,53419,53421,53422,53423,53424,53425,53426,null,null,null,null,null,null,53427,53430,53432,53434,53435,53436,53437,53438,53439,53442,53443,53445,53446,53447,53450,53451,53452,53453,53454,53455,53458,53462,53463,53464,53465,53466,null,null,null,null,null,null,53467,53470,53471,53473,53474,53475,53477,53478,53479,53480,53481,53482,53483,53486,53490,53491,53492,53493,53494,53495,53497,53498,53499,53500,53501,53502,53503,53504,53505,53506,53507,53508,45927,45929,45931,45934,45936,45937,45940,45944,45952,45953,45955,45956,45957,45964,45968,45972,45984,45985,45992,45996,46020,46021,46024,46027,46028,46030,46032,46036,46037,46039,46041,46043,46045,46048,46052,46056,46076,46096,46104,46108,46112,46120,46121,46123,46132,46160,46161,46164,46168,46176,46177,46179,46181,46188,46208,46216,46237,46244,46248,46252,46261,46263,46265,46272,46276,46280,46288,46293,46300,46301,46304,46307,46308,46310,46316,46317,46319,46321,46328,46356,46357,46360,46363,46364,46372,46373,46375,46376,46377,46378,46384,46385,46388,46392,53509,53510,53511,53512,53513,53514,53515,53516,53518,53519,53520,53521,53522,53523,53524,53525,53526,53527,53528,53529,53530,53531,53532,53533,53534,53535,null,null,null,null,null,null,53536,53537,53538,53539,53540,53541,53542,53543,53544,53545,53546,53547,53548,53549,53550,53551,53554,53555,53557,53558,53559,53561,53563,53564,53565,53566,null,null,null,null,null,null,53567,53570,53574,53575,53576,53577,53578,53579,53582,53583,53585,53586,53587,53589,53590,53591,53592,53593,53594,53595,53598,53600,53602,53603,53604,53605,53606,53607,53609,53610,53611,53613,46400,46401,46403,46404,46405,46411,46412,46413,46416,46420,46428,46429,46431,46432,46433,46496,46497,46500,46504,46506,46507,46512,46513,46515,46516,46517,46523,46524,46525,46528,46532,46540,46541,46543,46544,46545,46552,46572,46608,46609,46612,46616,46629,46636,46644,46664,46692,46696,46748,46749,46752,46756,46763,46764,46769,46804,46832,46836,46840,46848,46849,46853,46888,46889,46892,46895,46896,46904,46905,46907,46916,46920,46924,46932,46933,46944,46948,46952,46960,46961,46963,46965,46972,46973,46976,46980,46988,46989,46991,46992,46993,46994,46998,46999,53614,53615,53616,53617,53618,53619,53620,53621,53622,53623,53624,53625,53626,53627,53629,53630,53631,53632,53633,53634,53635,53637,53638,53639,53641,53642,null,null,null,null,null,null,53643,53644,53645,53646,53647,53648,53649,53650,53651,53652,53653,53654,53655,53656,53657,53658,53659,53660,53661,53662,53663,53666,53667,53669,53670,53671,null,null,null,null,null,null,53673,53674,53675,53676,53677,53678,53679,53682,53684,53686,53687,53688,53689,53691,53693,53694,53695,53697,53698,53699,53700,53701,53702,53703,53704,53705,53706,53707,53708,53709,53710,53711,47000,47001,47004,47008,47016,47017,47019,47020,47021,47028,47029,47032,47047,47049,47084,47085,47088,47092,47100,47101,47103,47104,47105,47111,47112,47113,47116,47120,47128,47129,47131,47133,47140,47141,47144,47148,47156,47157,47159,47160,47161,47168,47172,47185,47187,47196,47197,47200,47204,47212,47213,47215,47217,47224,47228,47245,47272,47280,47284,47288,47296,47297,47299,47301,47308,47312,47316,47325,47327,47329,47336,47337,47340,47344,47352,47353,47355,47357,47364,47384,47392,47420,47421,47424,47428,47436,47439,47441,47448,47449,47452,47456,47464,47465,53712,53713,53714,53715,53716,53717,53718,53719,53721,53722,53723,53724,53725,53726,53727,53728,53729,53730,53731,53732,53733,53734,53735,53736,53737,53738,null,null,null,null,null,null,53739,53740,53741,53742,53743,53744,53745,53746,53747,53749,53750,53751,53753,53754,53755,53756,53757,53758,53759,53760,53761,53762,53763,53764,53765,53766,null,null,null,null,null,null,53768,53770,53771,53772,53773,53774,53775,53777,53778,53779,53780,53781,53782,53783,53784,53785,53786,53787,53788,53789,53790,53791,53792,53793,53794,53795,53796,53797,53798,53799,53800,53801,47467,47469,47476,47477,47480,47484,47492,47493,47495,47497,47498,47501,47502,47532,47533,47536,47540,47548,47549,47551,47553,47560,47561,47564,47566,47567,47568,47569,47570,47576,47577,47579,47581,47582,47585,47587,47588,47589,47592,47596,47604,47605,47607,47608,47609,47610,47616,47617,47624,47637,47672,47673,47676,47680,47682,47688,47689,47691,47693,47694,47699,47700,47701,47704,47708,47716,47717,47719,47720,47721,47728,47729,47732,47736,47747,47748,47749,47751,47756,47784,47785,47787,47788,47792,47794,47800,47801,47803,47805,47812,47816,47832,47833,47868,53802,53803,53806,53807,53809,53810,53811,53813,53814,53815,53816,53817,53818,53819,53822,53824,53826,53827,53828,53829,53830,53831,53833,53834,53835,53836,null,null,null,null,null,null,53837,53838,53839,53840,53841,53842,53843,53844,53845,53846,53847,53848,53849,53850,53851,53853,53854,53855,53856,53857,53858,53859,53861,53862,53863,53864,null,null,null,null,null,null,53865,53866,53867,53868,53869,53870,53871,53872,53873,53874,53875,53876,53877,53878,53879,53880,53881,53882,53883,53884,53885,53886,53887,53890,53891,53893,53894,53895,53897,53898,53899,53900,47872,47876,47885,47887,47889,47896,47900,47904,47913,47915,47924,47925,47926,47928,47931,47932,47933,47934,47940,47941,47943,47945,47949,47951,47952,47956,47960,47969,47971,47980,48008,48012,48016,48036,48040,48044,48052,48055,48064,48068,48072,48080,48083,48120,48121,48124,48127,48128,48130,48136,48137,48139,48140,48141,48143,48145,48148,48149,48150,48151,48152,48155,48156,48157,48158,48159,48164,48165,48167,48169,48173,48176,48177,48180,48184,48192,48193,48195,48196,48197,48201,48204,48205,48208,48221,48260,48261,48264,48267,48268,48270,48276,48277,48279,53901,53902,53903,53906,53907,53908,53910,53911,53912,53913,53914,53915,53917,53918,53919,53921,53922,53923,53925,53926,53927,53928,53929,53930,53931,53933,null,null,null,null,null,null,53934,53935,53936,53938,53939,53940,53941,53942,53943,53946,53947,53949,53950,53953,53955,53956,53957,53958,53959,53962,53964,53965,53966,53967,53968,53969,null,null,null,null,null,null,53970,53971,53973,53974,53975,53977,53978,53979,53981,53982,53983,53984,53985,53986,53987,53990,53991,53992,53993,53994,53995,53996,53997,53998,53999,54002,54003,54005,54006,54007,54009,54010,48281,48282,48288,48289,48292,48295,48296,48304,48305,48307,48308,48309,48316,48317,48320,48324,48333,48335,48336,48337,48341,48344,48348,48372,48373,48374,48376,48380,48388,48389,48391,48393,48400,48404,48420,48428,48448,48456,48457,48460,48464,48472,48473,48484,48488,48512,48513,48516,48519,48520,48521,48522,48528,48529,48531,48533,48537,48538,48540,48548,48560,48568,48596,48597,48600,48604,48617,48624,48628,48632,48640,48643,48645,48652,48653,48656,48660,48668,48669,48671,48708,48709,48712,48716,48718,48724,48725,48727,48729,48730,48731,48736,48737,48740,54011,54012,54013,54014,54015,54018,54020,54022,54023,54024,54025,54026,54027,54031,54033,54034,54035,54037,54039,54040,54041,54042,54043,54046,54050,54051,null,null,null,null,null,null,54052,54054,54055,54058,54059,54061,54062,54063,54065,54066,54067,54068,54069,54070,54071,54074,54078,54079,54080,54081,54082,54083,54086,54087,54088,54089,null,null,null,null,null,null,54090,54091,54092,54093,54094,54095,54096,54097,54098,54099,54100,54101,54102,54103,54104,54105,54106,54107,54108,54109,54110,54111,54112,54113,54114,54115,54116,54117,54118,54119,54120,54121,48744,48746,48752,48753,48755,48756,48757,48763,48764,48765,48768,48772,48780,48781,48783,48784,48785,48792,48793,48808,48848,48849,48852,48855,48856,48864,48867,48868,48869,48876,48897,48904,48905,48920,48921,48923,48924,48925,48960,48961,48964,48968,48976,48977,48981,49044,49072,49093,49100,49101,49104,49108,49116,49119,49121,49212,49233,49240,49244,49248,49256,49257,49296,49297,49300,49304,49312,49313,49315,49317,49324,49325,49327,49328,49331,49332,49333,49334,49340,49341,49343,49344,49345,49349,49352,49353,49356,49360,49368,49369,49371,49372,49373,49380,54122,54123,54124,54125,54126,54127,54128,54129,54130,54131,54132,54133,54134,54135,54136,54137,54138,54139,54142,54143,54145,54146,54147,54149,54150,54151,null,null,null,null,null,null,54152,54153,54154,54155,54158,54162,54163,54164,54165,54166,54167,54170,54171,54173,54174,54175,54177,54178,54179,54180,54181,54182,54183,54186,54188,54190,null,null,null,null,null,null,54191,54192,54193,54194,54195,54197,54198,54199,54201,54202,54203,54205,54206,54207,54208,54209,54210,54211,54214,54215,54218,54219,54220,54221,54222,54223,54225,54226,54227,54228,54229,54230,49381,49384,49388,49396,49397,49399,49401,49408,49412,49416,49424,49429,49436,49437,49438,49439,49440,49443,49444,49446,49447,49452,49453,49455,49456,49457,49462,49464,49465,49468,49472,49480,49481,49483,49484,49485,49492,49493,49496,49500,49508,49509,49511,49512,49513,49520,49524,49528,49541,49548,49549,49550,49552,49556,49558,49564,49565,49567,49569,49573,49576,49577,49580,49584,49597,49604,49608,49612,49620,49623,49624,49632,49636,49640,49648,49649,49651,49660,49661,49664,49668,49676,49677,49679,49681,49688,49689,49692,49695,49696,49704,49705,49707,49709,54231,54233,54234,54235,54236,54237,54238,54239,54240,54242,54244,54245,54246,54247,54248,54249,54250,54251,54254,54255,54257,54258,54259,54261,54262,54263,null,null,null,null,null,null,54264,54265,54266,54267,54270,54272,54274,54275,54276,54277,54278,54279,54281,54282,54283,54284,54285,54286,54287,54288,54289,54290,54291,54292,54293,54294,null,null,null,null,null,null,54295,54296,54297,54298,54299,54300,54302,54303,54304,54305,54306,54307,54308,54309,54310,54311,54312,54313,54314,54315,54316,54317,54318,54319,54320,54321,54322,54323,54324,54325,54326,54327,49711,49713,49714,49716,49736,49744,49745,49748,49752,49760,49765,49772,49773,49776,49780,49788,49789,49791,49793,49800,49801,49808,49816,49819,49821,49828,49829,49832,49836,49837,49844,49845,49847,49849,49884,49885,49888,49891,49892,49899,49900,49901,49903,49905,49910,49912,49913,49915,49916,49920,49928,49929,49932,49933,49939,49940,49941,49944,49948,49956,49957,49960,49961,49989,50024,50025,50028,50032,50034,50040,50041,50044,50045,50052,50056,50060,50112,50136,50137,50140,50143,50144,50146,50152,50153,50157,50164,50165,50168,50184,50192,50212,50220,50224,54328,54329,54330,54331,54332,54333,54334,54335,54337,54338,54339,54341,54342,54343,54344,54345,54346,54347,54348,54349,54350,54351,54352,54353,54354,54355,null,null,null,null,null,null,54356,54357,54358,54359,54360,54361,54362,54363,54365,54366,54367,54369,54370,54371,54373,54374,54375,54376,54377,54378,54379,54380,54382,54384,54385,54386,null,null,null,null,null,null,54387,54388,54389,54390,54391,54394,54395,54397,54398,54401,54403,54404,54405,54406,54407,54410,54412,54414,54415,54416,54417,54418,54419,54421,54422,54423,54424,54425,54426,54427,54428,54429,50228,50236,50237,50248,50276,50277,50280,50284,50292,50293,50297,50304,50324,50332,50360,50364,50409,50416,50417,50420,50424,50426,50431,50432,50433,50444,50448,50452,50460,50472,50473,50476,50480,50488,50489,50491,50493,50500,50501,50504,50505,50506,50508,50509,50510,50515,50516,50517,50519,50520,50521,50525,50526,50528,50529,50532,50536,50544,50545,50547,50548,50549,50556,50557,50560,50564,50567,50572,50573,50575,50577,50581,50583,50584,50588,50592,50601,50612,50613,50616,50617,50619,50620,50621,50622,50628,50629,50630,50631,50632,50633,50634,50636,50638,54430,54431,54432,54433,54434,54435,54436,54437,54438,54439,54440,54442,54443,54444,54445,54446,54447,54448,54449,54450,54451,54452,54453,54454,54455,54456,null,null,null,null,null,null,54457,54458,54459,54460,54461,54462,54463,54464,54465,54466,54467,54468,54469,54470,54471,54472,54473,54474,54475,54477,54478,54479,54481,54482,54483,54485,null,null,null,null,null,null,54486,54487,54488,54489,54490,54491,54493,54494,54496,54497,54498,54499,54500,54501,54502,54503,54505,54506,54507,54509,54510,54511,54513,54514,54515,54516,54517,54518,54519,54521,54522,54524,50640,50641,50644,50648,50656,50657,50659,50661,50668,50669,50670,50672,50676,50678,50679,50684,50685,50686,50687,50688,50689,50693,50694,50695,50696,50700,50704,50712,50713,50715,50716,50724,50725,50728,50732,50733,50734,50736,50739,50740,50741,50743,50745,50747,50752,50753,50756,50760,50768,50769,50771,50772,50773,50780,50781,50784,50796,50799,50801,50808,50809,50812,50816,50824,50825,50827,50829,50836,50837,50840,50844,50852,50853,50855,50857,50864,50865,50868,50872,50873,50874,50880,50881,50883,50885,50892,50893,50896,50900,50908,50909,50912,50913,50920,54526,54527,54528,54529,54530,54531,54533,54534,54535,54537,54538,54539,54541,54542,54543,54544,54545,54546,54547,54550,54552,54553,54554,54555,54556,54557,null,null,null,null,null,null,54558,54559,54560,54561,54562,54563,54564,54565,54566,54567,54568,54569,54570,54571,54572,54573,54574,54575,54576,54577,54578,54579,54580,54581,54582,54583,null,null,null,null,null,null,54584,54585,54586,54587,54590,54591,54593,54594,54595,54597,54598,54599,54600,54601,54602,54603,54606,54608,54610,54611,54612,54613,54614,54615,54618,54619,54621,54622,54623,54625,54626,54627,50921,50924,50928,50936,50937,50941,50948,50949,50952,50956,50964,50965,50967,50969,50976,50977,50980,50984,50992,50993,50995,50997,50999,51004,51005,51008,51012,51018,51020,51021,51023,51025,51026,51027,51028,51029,51030,51031,51032,51036,51040,51048,51051,51060,51061,51064,51068,51069,51070,51075,51076,51077,51079,51080,51081,51082,51086,51088,51089,51092,51094,51095,51096,51098,51104,51105,51107,51108,51109,51110,51116,51117,51120,51124,51132,51133,51135,51136,51137,51144,51145,51148,51150,51152,51160,51165,51172,51176,51180,51200,51201,51204,51208,51210,54628,54630,54631,54634,54636,54638,54639,54640,54641,54642,54643,54646,54647,54649,54650,54651,54653,54654,54655,54656,54657,54658,54659,54662,54666,54667,null,null,null,null,null,null,54668,54669,54670,54671,54673,54674,54675,54676,54677,54678,54679,54680,54681,54682,54683,54684,54685,54686,54687,54688,54689,54690,54691,54692,54694,54695,null,null,null,null,null,null,54696,54697,54698,54699,54700,54701,54702,54703,54704,54705,54706,54707,54708,54709,54710,54711,54712,54713,54714,54715,54716,54717,54718,54719,54720,54721,54722,54723,54724,54725,54726,54727,51216,51217,51219,51221,51222,51228,51229,51232,51236,51244,51245,51247,51249,51256,51260,51264,51272,51273,51276,51277,51284,51312,51313,51316,51320,51322,51328,51329,51331,51333,51334,51335,51339,51340,51341,51348,51357,51359,51361,51368,51388,51389,51396,51400,51404,51412,51413,51415,51417,51424,51425,51428,51445,51452,51453,51456,51460,51461,51462,51468,51469,51471,51473,51480,51500,51508,51536,51537,51540,51544,51552,51553,51555,51564,51568,51572,51580,51592,51593,51596,51600,51608,51609,51611,51613,51648,51649,51652,51655,51656,51658,51664,51665,51667,54730,54731,54733,54734,54735,54737,54739,54740,54741,54742,54743,54746,54748,54750,54751,54752,54753,54754,54755,54758,54759,54761,54762,54763,54765,54766,null,null,null,null,null,null,54767,54768,54769,54770,54771,54774,54776,54778,54779,54780,54781,54782,54783,54786,54787,54789,54790,54791,54793,54794,54795,54796,54797,54798,54799,54802,null,null,null,null,null,null,54806,54807,54808,54809,54810,54811,54813,54814,54815,54817,54818,54819,54821,54822,54823,54824,54825,54826,54827,54828,54830,54831,54832,54833,54834,54835,54836,54837,54838,54839,54842,54843,51669,51670,51673,51674,51676,51677,51680,51682,51684,51687,51692,51693,51695,51696,51697,51704,51705,51708,51712,51720,51721,51723,51724,51725,51732,51736,51753,51788,51789,51792,51796,51804,51805,51807,51808,51809,51816,51837,51844,51864,51900,51901,51904,51908,51916,51917,51919,51921,51923,51928,51929,51936,51948,51956,51976,51984,51988,51992,52000,52001,52033,52040,52041,52044,52048,52056,52057,52061,52068,52088,52089,52124,52152,52180,52196,52199,52201,52236,52237,52240,52244,52252,52253,52257,52258,52263,52264,52265,52268,52270,52272,52280,52281,52283,54845,54846,54847,54849,54850,54851,54852,54854,54855,54858,54860,54862,54863,54864,54866,54867,54870,54871,54873,54874,54875,54877,54878,54879,54880,54881,null,null,null,null,null,null,54882,54883,54884,54885,54886,54888,54890,54891,54892,54893,54894,54895,54898,54899,54901,54902,54903,54904,54905,54906,54907,54908,54909,54910,54911,54912,null,null,null,null,null,null,54913,54914,54916,54918,54919,54920,54921,54922,54923,54926,54927,54929,54930,54931,54933,54934,54935,54936,54937,54938,54939,54940,54942,54944,54946,54947,54948,54949,54950,54951,54953,54954,52284,52285,52286,52292,52293,52296,52300,52308,52309,52311,52312,52313,52320,52324,52326,52328,52336,52341,52376,52377,52380,52384,52392,52393,52395,52396,52397,52404,52405,52408,52412,52420,52421,52423,52425,52432,52436,52452,52460,52464,52481,52488,52489,52492,52496,52504,52505,52507,52509,52516,52520,52524,52537,52572,52576,52580,52588,52589,52591,52593,52600,52616,52628,52629,52632,52636,52644,52645,52647,52649,52656,52676,52684,52688,52712,52716,52720,52728,52729,52731,52733,52740,52744,52748,52756,52761,52768,52769,52772,52776,52784,52785,52787,52789,54955,54957,54958,54959,54961,54962,54963,54964,54965,54966,54967,54968,54970,54972,54973,54974,54975,54976,54977,54978,54979,54982,54983,54985,54986,54987,null,null,null,null,null,null,54989,54990,54991,54992,54994,54995,54997,54998,55000,55002,55003,55004,55005,55006,55007,55009,55010,55011,55013,55014,55015,55017,55018,55019,55020,55021,null,null,null,null,null,null,55022,55023,55025,55026,55027,55028,55030,55031,55032,55033,55034,55035,55038,55039,55041,55042,55043,55045,55046,55047,55048,55049,55050,55051,55052,55053,55054,55055,55056,55058,55059,55060,52824,52825,52828,52831,52832,52833,52840,52841,52843,52845,52852,52853,52856,52860,52868,52869,52871,52873,52880,52881,52884,52888,52896,52897,52899,52900,52901,52908,52909,52929,52964,52965,52968,52971,52972,52980,52981,52983,52984,52985,52992,52993,52996,53000,53008,53009,53011,53013,53020,53024,53028,53036,53037,53039,53040,53041,53048,53076,53077,53080,53084,53092,53093,53095,53097,53104,53105,53108,53112,53120,53125,53132,53153,53160,53168,53188,53216,53217,53220,53224,53232,53233,53235,53237,53244,53248,53252,53265,53272,53293,53300,53301,53304,53308,55061,55062,55063,55066,55067,55069,55070,55071,55073,55074,55075,55076,55077,55078,55079,55082,55084,55086,55087,55088,55089,55090,55091,55094,55095,55097,null,null,null,null,null,null,55098,55099,55101,55102,55103,55104,55105,55106,55107,55109,55110,55112,55114,55115,55116,55117,55118,55119,55122,55123,55125,55130,55131,55132,55133,55134,null,null,null,null,null,null,55135,55138,55140,55142,55143,55144,55146,55147,55149,55150,55151,55153,55154,55155,55157,55158,55159,55160,55161,55162,55163,55166,55167,55168,55170,55171,55172,55173,55174,55175,55178,55179,53316,53317,53319,53321,53328,53332,53336,53344,53356,53357,53360,53364,53372,53373,53377,53412,53413,53416,53420,53428,53429,53431,53433,53440,53441,53444,53448,53449,53456,53457,53459,53460,53461,53468,53469,53472,53476,53484,53485,53487,53488,53489,53496,53517,53552,53553,53556,53560,53562,53568,53569,53571,53572,53573,53580,53581,53584,53588,53596,53597,53599,53601,53608,53612,53628,53636,53640,53664,53665,53668,53672,53680,53681,53683,53685,53690,53692,53696,53720,53748,53752,53767,53769,53776,53804,53805,53808,53812,53820,53821,53823,53825,53832,53852,55181,55182,55183,55185,55186,55187,55188,55189,55190,55191,55194,55196,55198,55199,55200,55201,55202,55203,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,53860,53888,53889,53892,53896,53904,53905,53909,53916,53920,53924,53932,53937,53944,53945,53948,53951,53952,53954,53960,53961,53963,53972,53976,53980,53988,53989,54000,54001,54004,54008,54016,54017,54019,54021,54028,54029,54030,54032,54036,54038,54044,54045,54047,54048,54049,54053,54056,54057,54060,54064,54072,54073,54075,54076,54077,54084,54085,54140,54141,54144,54148,54156,54157,54159,54160,54161,54168,54169,54172,54176,54184,54185,54187,54189,54196,54200,54204,54212,54213,54216,54217,54224,54232,54241,54243,54252,54253,54256,54260,54268,54269,54271,54273,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,54280,54301,54336,54340,54364,54368,54372,54381,54383,54392,54393,54396,54399,54400,54402,54408,54409,54411,54413,54420,54441,54476,54480,54484,54492,54495,54504,54508,54512,54520,54523,54525,54532,54536,54540,54548,54549,54551,54588,54589,54592,54596,54604,54605,54607,54609,54616,54617,54620,54624,54629,54632,54633,54635,54637,54644,54645,54648,54652,54660,54661,54663,54664,54665,54672,54693,54728,54729,54732,54736,54738,54744,54745,54747,54749,54756,54757,54760,54764,54772,54773,54775,54777,54784,54785,54788,54792,54800,54801,54803,54804,54805,54812,54816,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,54820,54829,54840,54841,54844,54848,54853,54856,54857,54859,54861,54865,54868,54869,54872,54876,54887,54889,54896,54897,54900,54915,54917,54924,54925,54928,54932,54941,54943,54945,54952,54956,54960,54969,54971,54980,54981,54984,54988,54993,54996,54999,55001,55008,55012,55016,55024,55029,55036,55037,55040,55044,55057,55064,55065,55068,55072,55080,55081,55083,55085,55092,55093,55096,55100,55108,55111,55113,55120,55121,55124,55126,55127,55128,55129,55136,55137,55139,55141,55145,55148,55152,55156,55164,55165,55169,55176,55177,55180,55184,55192,55193,55195,55197,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,20285,20339,20551,20729,21152,21487,21621,21733,22025,23233,23478,26247,26550,26551,26607,27468,29634,30146,31292,33499,33540,34903,34952,35382,36040,36303,36603,36838,39381,21051,21364,21508,24682,24932,27580,29647,33050,35258,35282,38307,20355,21002,22718,22904,23014,24178,24185,25031,25536,26438,26604,26751,28567,30286,30475,30965,31240,31487,31777,32925,33390,33393,35563,38291,20075,21917,26359,28212,30883,31469,33883,35088,34638,38824,21208,22350,22570,23884,24863,25022,25121,25954,26577,27204,28187,29976,30131,30435,30640,32058,37039,37969,37970,40853,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,21283,23724,30002,32987,37440,38296,21083,22536,23004,23713,23831,24247,24378,24394,24951,27743,30074,30086,31968,32115,32177,32652,33108,33313,34193,35137,35611,37628,38477,40007,20171,20215,20491,20977,22607,24887,24894,24936,25913,27114,28433,30117,30342,30422,31623,33445,33995,63744,37799,38283,21888,23458,22353,63745,31923,32697,37301,20520,21435,23621,24040,25298,25454,25818,25831,28192,28844,31067,36317,36382,63746,36989,37445,37624,20094,20214,20581,24062,24314,24838,26967,33137,34388,36423,37749,39467,20062,20625,26480,26688,20745,21133,21138,27298,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,30652,37392,40660,21163,24623,36850,20552,25001,25581,25802,26684,27268,28608,33160,35233,38548,22533,29309,29356,29956,32121,32365,32937,35211,35700,36963,40273,25225,27770,28500,32080,32570,35363,20860,24906,31645,35609,37463,37772,20140,20435,20510,20670,20742,21185,21197,21375,22384,22659,24218,24465,24950,25004,25806,25964,26223,26299,26356,26775,28039,28805,28913,29855,29861,29898,30169,30828,30956,31455,31478,32069,32147,32789,32831,33051,33686,35686,36629,36885,37857,38915,38968,39514,39912,20418,21843,22586,22865,23395,23622,24760,25106,26690,26800,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,26856,28330,30028,30328,30926,31293,31995,32363,32380,35336,35489,35903,38542,40388,21476,21481,21578,21617,22266,22993,23396,23611,24235,25335,25911,25925,25970,26272,26543,27073,27837,30204,30352,30590,31295,32660,32771,32929,33167,33510,33533,33776,34241,34865,34996,35493,63747,36764,37678,38599,39015,39640,40723,21741,26011,26354,26767,31296,35895,40288,22256,22372,23825,26118,26801,26829,28414,29736,34974,39908,27752,63748,39592,20379,20844,20849,21151,23380,24037,24656,24685,25329,25511,25915,29657,31354,34467,36002,38799,20018,23521,25096,26524,29916,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,31185,33747,35463,35506,36328,36942,37707,38982,24275,27112,34303,37101,63749,20896,23448,23532,24931,26874,27454,28748,29743,29912,31649,32592,33733,35264,36011,38364,39208,21038,24669,25324,36866,20362,20809,21281,22745,24291,26336,27960,28826,29378,29654,31568,33009,37979,21350,25499,32619,20054,20608,22602,22750,24618,24871,25296,27088,39745,23439,32024,32945,36703,20132,20689,21676,21932,23308,23968,24039,25898,25934,26657,27211,29409,30350,30703,32094,32761,33184,34126,34527,36611,36686,37066,39171,39509,39851,19992,20037,20061,20167,20465,20855,21246,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,21312,21475,21477,21646,22036,22389,22434,23495,23943,24272,25084,25304,25937,26552,26601,27083,27472,27590,27628,27714,28317,28792,29399,29590,29699,30655,30697,31350,32127,32777,33276,33285,33290,33503,34914,35635,36092,36544,36881,37041,37476,37558,39378,39493,40169,40407,40860,22283,23616,33738,38816,38827,40628,21531,31384,32676,35033,36557,37089,22528,23624,25496,31391,23470,24339,31353,31406,33422,36524,20518,21048,21240,21367,22280,25331,25458,27402,28099,30519,21413,29527,34152,36470,38357,26426,27331,28528,35437,36556,39243,63750,26231,27512,36020,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,39740,63751,21483,22317,22862,25542,27131,29674,30789,31418,31429,31998,33909,35215,36211,36917,38312,21243,22343,30023,31584,33740,37406,63752,27224,20811,21067,21127,25119,26840,26997,38553,20677,21156,21220,25027,26020,26681,27135,29822,31563,33465,33771,35250,35641,36817,39241,63753,20170,22935,25810,26129,27278,29748,31105,31165,33449,34942,34943,35167,63754,37670,20235,21450,24613,25201,27762,32026,32102,20120,20834,30684,32943,20225,20238,20854,20864,21980,22120,22331,22522,22524,22804,22855,22931,23492,23696,23822,24049,24190,24524,25216,26071,26083,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,26398,26399,26462,26827,26820,27231,27450,27683,27773,27778,28103,29592,29734,29738,29826,29859,30072,30079,30849,30959,31041,31047,31048,31098,31637,32000,32186,32648,32774,32813,32908,35352,35663,35912,36215,37665,37668,39138,39249,39438,39439,39525,40594,32202,20342,21513,25326,26708,37329,21931,20794,63755,63756,23068,25062,63757,25295,25343,63758,63759,63760,63761,63762,63763,37027,63764,63765,63766,63767,63768,35582,63769,63770,63771,63772,26262,63773,29014,63774,63775,38627,63776,25423,25466,21335,63777,26511,26976,28275,63778,30007,63779,63780,63781,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,32013,63782,63783,34930,22218,23064,63784,63785,63786,63787,63788,20035,63789,20839,22856,26608,32784,63790,22899,24180,25754,31178,24565,24684,25288,25467,23527,23511,21162,63791,22900,24361,24594,63792,63793,63794,29785,63795,63796,63797,63798,63799,63800,39377,63801,63802,63803,63804,63805,63806,63807,63808,63809,63810,63811,28611,63812,63813,33215,36786,24817,63814,63815,33126,63816,63817,23615,63818,63819,63820,63821,63822,63823,63824,63825,23273,35365,26491,32016,63826,63827,63828,63829,63830,63831,33021,63832,63833,23612,27877,21311,28346,22810,33590,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,20025,20150,20294,21934,22296,22727,24406,26039,26086,27264,27573,28237,30701,31471,31774,32222,34507,34962,37170,37723,25787,28606,29562,30136,36948,21846,22349,25018,25812,26311,28129,28251,28525,28601,30192,32835,33213,34113,35203,35527,35674,37663,27795,30035,31572,36367,36957,21776,22530,22616,24162,25095,25758,26848,30070,31958,34739,40680,20195,22408,22382,22823,23565,23729,24118,24453,25140,25825,29619,33274,34955,36024,38538,40667,23429,24503,24755,20498,20992,21040,22294,22581,22615,23566,23648,23798,23947,24230,24466,24764,25361,25481,25623,26691,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,26873,27330,28120,28193,28372,28644,29182,30428,30585,31153,31291,33796,35241,36077,36339,36424,36867,36884,36947,37117,37709,38518,38876,27602,28678,29272,29346,29544,30563,31167,31716,32411,35712,22697,24775,25958,26109,26302,27788,28958,29129,35930,38931,20077,31361,20189,20908,20941,21205,21516,24999,26481,26704,26847,27934,28540,30140,30643,31461,33012,33891,37509,20828,26007,26460,26515,30168,31431,33651,63834,35910,36887,38957,23663,33216,33434,36929,36975,37389,24471,23965,27225,29128,30331,31561,34276,35588,37159,39472,21895,25078,63835,30313,32645,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,34367,34746,35064,37007,63836,27931,28889,29662,32097,33853,63837,37226,39409,63838,20098,21365,27396,27410,28734,29211,34349,40478,21068,36771,23888,25829,25900,27414,28651,31811,32412,34253,35172,35261,25289,33240,34847,24266,26391,28010,29436,29701,29807,34690,37086,20358,23821,24480,33802,20919,25504,30053,20142,20486,20841,20937,26753,27153,31918,31921,31975,33391,35538,36635,37327,20406,20791,21237,21570,24300,24942,25150,26053,27354,28670,31018,34268,34851,38317,39522,39530,40599,40654,21147,26310,27511,28701,31019,36706,38722,24976,25088,25891,28451,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,29001,29833,32244,32879,34030,36646,36899,37706,20925,21015,21155,27916,28872,35010,24265,25986,27566,28610,31806,29557,20196,20278,22265,63839,23738,23994,24604,29618,31533,32666,32718,32838,36894,37428,38646,38728,38936,40801,20363,28583,31150,37300,38583,21214,63840,25736,25796,27347,28510,28696,29200,30439,32769,34310,34396,36335,36613,38706,39791,40442,40565,30860,31103,32160,33737,37636,40575,40595,35542,22751,24324,26407,28711,29903,31840,32894,20769,28712,29282,30922,36034,36058,36084,38647,20102,20698,23534,24278,26009,29134,30274,30637,32842,34044,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,36988,39719,40845,22744,23105,23650,27155,28122,28431,30267,32047,32311,34078,35128,37860,38475,21129,26066,26611,27060,27969,28316,28687,29705,29792,30041,30244,30827,35628,39006,20845,25134,38520,20374,20523,23833,28138,32184,36650,24459,24900,26647,63841,38534,21202,32907,20956,20940,26974,31260,32190,33777,38517,20442,21033,21400,21519,21774,23653,24743,26446,26792,28012,29313,29432,29702,29827,63842,30178,31852,32633,32696,33673,35023,35041,37324,37328,38626,39881,21533,28542,29136,29848,34298,36522,38563,40023,40607,26519,28107,29747,33256,38678,30764,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,31435,31520,31890,25705,29802,30194,30908,30952,39340,39764,40635,23518,24149,28448,33180,33707,37000,19975,21325,23081,24018,24398,24930,25405,26217,26364,28415,28459,28771,30622,33836,34067,34875,36627,39237,39995,21788,25273,26411,27819,33545,35178,38778,20129,22916,24536,24537,26395,32178,32596,33426,33579,33725,36638,37017,22475,22969,23186,23504,26151,26522,26757,27599,29028,32629,36023,36067,36993,39749,33032,35978,38476,39488,40613,23391,27667,29467,30450,30431,33804,20906,35219,20813,20885,21193,26825,27796,30468,30496,32191,32236,38754,40629,28357,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,34065,20901,21517,21629,26126,26269,26919,28319,30399,30609,33559,33986,34719,37225,37528,40180,34946,20398,20882,21215,22982,24125,24917,25720,25721,26286,26576,27169,27597,27611,29279,29281,29761,30520,30683,32791,33468,33541,35584,35624,35980,26408,27792,29287,30446,30566,31302,40361,27519,27794,22818,26406,33945,21359,22675,22937,24287,25551,26164,26483,28218,29483,31447,33495,37672,21209,24043,25006,25035,25098,25287,25771,26080,26969,27494,27595,28961,29687,30045,32326,33310,33538,34154,35491,36031,38695,40289,22696,40664,20497,21006,21563,21839,25991,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,27766,32010,32011,32862,34442,38272,38639,21247,27797,29289,21619,23194,23614,23883,24396,24494,26410,26806,26979,28220,28228,30473,31859,32654,34183,35598,36855,38753,40692,23735,24758,24845,25003,25935,26107,26108,27665,27887,29599,29641,32225,38292,23494,34588,35600,21085,21338,25293,25615,25778,26420,27192,27850,29632,29854,31636,31893,32283,33162,33334,34180,36843,38649,39361,20276,21322,21453,21467,25292,25644,25856,26001,27075,27886,28504,29677,30036,30242,30436,30460,30928,30971,31020,32070,33324,34784,36820,38930,39151,21187,25300,25765,28196,28497,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,30332,36299,37297,37474,39662,39747,20515,20621,22346,22952,23592,24135,24439,25151,25918,26041,26049,26121,26507,27036,28354,30917,32033,32938,33152,33323,33459,33953,34444,35370,35607,37030,38450,40848,20493,20467,63843,22521,24472,25308,25490,26479,28227,28953,30403,32972,32986,35060,35061,35097,36064,36649,37197,38506,20271,20336,24091,26575,26658,30333,30334,39748,24161,27146,29033,29140,30058,63844,32321,34115,34281,39132,20240,31567,32624,38309,20961,24070,26805,27710,27726,27867,29359,31684,33539,27861,29754,20731,21128,22721,25816,27287,29863,30294,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,30887,34327,38370,38713,63845,21342,24321,35722,36776,36783,37002,21029,30629,40009,40712,19993,20482,20853,23643,24183,26142,26170,26564,26821,28851,29953,30149,31177,31453,36647,39200,39432,20445,22561,22577,23542,26222,27493,27921,28282,28541,29668,29995,33769,35036,35091,35676,36628,20239,20693,21264,21340,23443,24489,26381,31119,33145,33583,34068,35079,35206,36665,36667,39333,39954,26412,20086,20472,22857,23553,23791,23792,25447,26834,28925,29090,29739,32299,34028,34562,36898,37586,40179,19981,20184,20463,20613,21078,21103,21542,21648,22496,22827,23142,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,23386,23413,23500,24220,63846,25206,25975,26023,28014,28325,29238,31526,31807,32566,33104,33105,33178,33344,33433,33705,35331,36000,36070,36091,36212,36282,37096,37340,38428,38468,39385,40167,21271,20998,21545,22132,22707,22868,22894,24575,24996,25198,26128,27774,28954,30406,31881,31966,32027,33452,36033,38640,63847,20315,24343,24447,25282,23849,26379,26842,30844,32323,40300,19989,20633,21269,21290,21329,22915,23138,24199,24754,24970,25161,25209,26000,26503,27047,27604,27606,27607,27608,27832,63848,29749,30202,30738,30865,31189,31192,31875,32203,32737,32933,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,33086,33218,33778,34586,35048,35513,35692,36027,37145,38750,39131,40763,22188,23338,24428,25996,27315,27567,27996,28657,28693,29277,29613,36007,36051,38971,24977,27703,32856,39425,20045,20107,20123,20181,20282,20284,20351,20447,20735,21490,21496,21766,21987,22235,22763,22882,23057,23531,23546,23556,24051,24107,24473,24605,25448,26012,26031,26614,26619,26797,27515,27801,27863,28195,28681,29509,30722,31038,31040,31072,31169,31721,32023,32114,32902,33293,33678,34001,34503,35039,35408,35422,35613,36060,36198,36781,37034,39164,39391,40605,21066,63849,26388,63850,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,20632,21034,23665,25955,27733,29642,29987,30109,31639,33948,37240,38704,20087,25746,27578,29022,34217,19977,63851,26441,26862,28183,33439,34072,34923,25591,28545,37394,39087,19978,20663,20687,20767,21830,21930,22039,23360,23577,23776,24120,24202,24224,24258,24819,26705,27233,28248,29245,29248,29376,30456,31077,31665,32724,35059,35316,35443,35937,36062,38684,22622,29885,36093,21959,63852,31329,32034,33394,29298,29983,29989,63853,31513,22661,22779,23996,24207,24246,24464,24661,25234,25471,25933,26257,26329,26360,26646,26866,29312,29790,31598,32110,32214,32626,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,32997,33298,34223,35199,35475,36893,37604,40653,40736,22805,22893,24109,24796,26132,26227,26512,27728,28101,28511,30707,30889,33990,37323,37675,20185,20682,20808,21892,23307,23459,25159,25982,26059,28210,29053,29697,29764,29831,29887,30316,31146,32218,32341,32680,33146,33203,33337,34330,34796,35445,36323,36984,37521,37925,39245,39854,21352,23633,26964,27844,27945,28203,33292,34203,35131,35373,35498,38634,40807,21089,26297,27570,32406,34814,36109,38275,38493,25885,28041,29166,63854,22478,22995,23468,24615,24826,25104,26143,26207,29481,29689,30427,30465,31596,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,32854,32882,33125,35488,37266,19990,21218,27506,27927,31237,31545,32048,63855,36016,21484,22063,22609,23477,23567,23569,24034,25152,25475,25620,26157,26803,27836,28040,28335,28703,28836,29138,29990,30095,30094,30233,31505,31712,31787,32032,32057,34092,34157,34311,35380,36877,36961,37045,37559,38902,39479,20439,23660,26463,28049,31903,32396,35606,36118,36895,23403,24061,25613,33984,36956,39137,29575,23435,24730,26494,28126,35359,35494,36865,38924,21047,63856,28753,30862,37782,34928,37335,20462,21463,22013,22234,22402,22781,23234,23432,23723,23744,24101,24833,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,25101,25163,25480,25628,25910,25976,27193,27530,27700,27929,28465,29159,29417,29560,29703,29874,30246,30561,31168,31319,31466,31929,32143,32172,32353,32670,33065,33585,33936,34010,34282,34966,35504,35728,36664,36930,36995,37228,37526,37561,38539,38567,38568,38614,38656,38920,39318,39635,39706,21460,22654,22809,23408,23487,28113,28506,29087,29729,29881,32901,33789,24033,24455,24490,24642,26092,26642,26991,27219,27529,27957,28147,29667,30462,30636,31565,32020,33059,33308,33600,34036,34147,35426,35524,37255,37662,38918,39348,25100,34899,36848,37477,23815,23847,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,23913,29791,33181,34664,28629,25342,32722,35126,35186,19998,20056,20711,21213,21319,25215,26119,32361,34821,38494,20365,21273,22070,22987,23204,23608,23630,23629,24066,24337,24643,26045,26159,26178,26558,26612,29468,30690,31034,32709,33940,33997,35222,35430,35433,35553,35925,35962,22516,23508,24335,24687,25325,26893,27542,28252,29060,31698,34645,35672,36606,39135,39166,20280,20353,20449,21627,23072,23480,24892,26032,26216,29180,30003,31070,32051,33102,33251,33688,34218,34254,34563,35338,36523,36763,63857,36805,22833,23460,23526,24713,23529,23563,24515,27777,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,63858,28145,28683,29978,33455,35574,20160,21313,63859,38617,27663,20126,20420,20818,21854,23077,23784,25105,29273,33469,33706,34558,34905,35357,38463,38597,39187,40201,40285,22538,23731,23997,24132,24801,24853,25569,27138,28197,37122,37716,38990,39952,40823,23433,23736,25353,26191,26696,30524,38593,38797,38996,39839,26017,35585,36555,38332,21813,23721,24022,24245,26263,30284,33780,38343,22739,25276,29390,40232,20208,22830,24591,26171,27523,31207,40230,21395,21696,22467,23830,24859,26326,28079,30861,33406,38552,38724,21380,25212,25494,28082,32266,33099,38989,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,27387,32588,40367,40474,20063,20539,20918,22812,24825,25590,26928,29242,32822,63860,37326,24369,63861,63862,32004,33509,33903,33979,34277,36493,63863,20335,63864,63865,22756,23363,24665,25562,25880,25965,26264,63866,26954,27171,27915,28673,29036,30162,30221,31155,31344,63867,32650,63868,35140,63869,35731,37312,38525,63870,39178,22276,24481,26044,28417,30208,31142,35486,39341,39770,40812,20740,25014,25233,27277,33222,20547,22576,24422,28937,35328,35578,23420,34326,20474,20796,22196,22852,25513,28153,23978,26989,20870,20104,20313,63871,63872,63873,22914,63874,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,63875,27487,27741,63876,29877,30998,63877,33287,33349,33593,36671,36701,63878,39192,63879,63880,63881,20134,63882,22495,24441,26131,63883,63884,30123,32377,35695,63885,36870,39515,22181,22567,23032,23071,23476,63886,24310,63887,63888,25424,25403,63889,26941,27783,27839,28046,28051,28149,28436,63890,28895,28982,29017,63891,29123,29141,63892,30799,30831,63893,31605,32227,63894,32303,63895,34893,36575,63896,63897,63898,37467,63899,40182,63900,63901,63902,24709,28037,63903,29105,63904,63905,38321,21421,63906,63907,63908,26579,63909,28814,28976,29744,33398,33490,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,63910,38331,39653,40573,26308,63911,29121,33865,63912,63913,22603,63914,63915,23992,24433,63916,26144,26254,27001,27054,27704,27891,28214,28481,28634,28699,28719,29008,29151,29552,63917,29787,63918,29908,30408,31310,32403,63919,63920,33521,35424,36814,63921,37704,63922,38681,63923,63924,20034,20522,63925,21000,21473,26355,27757,28618,29450,30591,31330,33454,34269,34306,63926,35028,35427,35709,35947,63927,37555,63928,38675,38928,20116,20237,20425,20658,21320,21566,21555,21978,22626,22714,22887,23067,23524,24735,63929,25034,25942,26111,26212,26791,27738,28595,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,28879,29100,29522,31613,34568,35492,39986,40711,23627,27779,29508,29577,37434,28331,29797,30239,31337,32277,34314,20800,22725,25793,29934,29973,30320,32705,37013,38605,39252,28198,29926,31401,31402,33253,34521,34680,35355,23113,23436,23451,26785,26880,28003,29609,29715,29740,30871,32233,32747,33048,33109,33694,35916,38446,38929,26352,24448,26106,26505,27754,29579,20525,23043,27498,30702,22806,23916,24013,29477,30031,63930,63931,20709,20985,22575,22829,22934,23002,23525,63932,63933,23970,25303,25622,25747,25854,63934,26332,63935,27208,63936,29183,29796,63937,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,31368,31407,32327,32350,32768,33136,63938,34799,35201,35616,36953,63939,36992,39250,24958,27442,28020,32287,35109,36785,20433,20653,20887,21191,22471,22665,23481,24248,24898,27029,28044,28263,28342,29076,29794,29992,29996,32883,33592,33993,36362,37780,37854,63940,20110,20305,20598,20778,21448,21451,21491,23431,23507,23588,24858,24962,26100,29275,29591,29760,30402,31056,31121,31161,32006,32701,33419,34261,34398,36802,36935,37109,37354,38533,38632,38633,21206,24423,26093,26161,26671,29020,31286,37057,38922,20113,63941,27218,27550,28560,29065,32792,33464,34131,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,36939,38549,38642,38907,34074,39729,20112,29066,38596,20803,21407,21729,22291,22290,22435,23195,23236,23491,24616,24895,25588,27781,27961,28274,28304,29232,29503,29783,33489,34945,36677,36960,63942,38498,39000,40219,26376,36234,37470,20301,20553,20702,21361,22285,22996,23041,23561,24944,26256,28205,29234,29771,32239,32963,33806,33894,34111,34655,34907,35096,35586,36949,38859,39759,20083,20369,20754,20842,63943,21807,21929,23418,23461,24188,24189,24254,24736,24799,24840,24841,25540,25912,26377,63944,26580,26586,63945,26977,26978,27833,27943,63946,28216,63947,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,28641,29494,29495,63948,29788,30001,63949,30290,63950,63951,32173,33278,33848,35029,35480,35547,35565,36400,36418,36938,36926,36986,37193,37321,37742,63952,63953,22537,63954,27603,32905,32946,63955,63956,20801,22891,23609,63957,63958,28516,29607,32996,36103,63959,37399,38287,63960,63961,63962,63963,32895,25102,28700,32104,34701,63964,22432,24681,24903,27575,35518,37504,38577,20057,21535,28139,34093,38512,38899,39150,25558,27875,37009,20957,25033,33210,40441,20381,20506,20736,23452,24847,25087,25836,26885,27589,30097,30691,32681,33380,34191,34811,34915,35516,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,35696,37291,20108,20197,20234,63965,63966,22839,23016,63967,24050,24347,24411,24609,63968,63969,63970,63971,29246,29669,63972,30064,30157,63973,31227,63974,32780,32819,32900,33505,33617,63975,63976,36029,36019,36999,63977,63978,39156,39180,63979,63980,28727,30410,32714,32716,32764,35610,20154,20161,20995,21360,63981,21693,22240,23035,23493,24341,24525,28270,63982,63983,32106,33589,63984,34451,35469,63985,38765,38775,63986,63987,19968,20314,20350,22777,26085,28322,36920,37808,39353,20219,22764,22922,23001,24641,63988,63989,31252,63990,33615,36035,20837,21316,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,63991,63992,63993,20173,21097,23381,33471,20180,21050,21672,22985,23039,23376,23383,23388,24675,24904,28363,28825,29038,29574,29943,30133,30913,32043,32773,33258,33576,34071,34249,35566,36039,38604,20316,21242,22204,26027,26152,28796,28856,29237,32189,33421,37196,38592,40306,23409,26855,27544,28538,30430,23697,26283,28507,31668,31786,34870,38620,19976,20183,21280,22580,22715,22767,22892,23559,24115,24196,24373,25484,26290,26454,27167,27299,27404,28479,29254,63994,29520,29835,31456,31911,33144,33247,33255,33674,33900,34083,34196,34255,35037,36115,37292,38263,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,38556,20877,21705,22312,23472,25165,26448,26685,26771,28221,28371,28797,32289,35009,36001,36617,40779,40782,29229,31631,35533,37658,20295,20302,20786,21632,22992,24213,25269,26485,26990,27159,27822,28186,29401,29482,30141,31672,32053,33511,33785,33879,34295,35419,36015,36487,36889,37048,38606,40799,21219,21514,23265,23490,25688,25973,28404,29380,63995,30340,31309,31515,31821,32318,32735,33659,35627,36042,36196,36321,36447,36842,36857,36969,37841,20291,20346,20659,20840,20856,21069,21098,22625,22652,22880,23560,23637,24283,24731,25136,26643,27583,27656,28593,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,29006,29728,30000,30008,30033,30322,31564,31627,31661,31686,32399,35438,36670,36681,37439,37523,37666,37931,38651,39002,39019,39198,20999,25130,25240,27993,30308,31434,31680,32118,21344,23742,24215,28472,28857,31896,38673,39822,40670,25509,25722,34678,19969,20117,20141,20572,20597,21576,22979,23450,24128,24237,24311,24449,24773,25402,25919,25972,26060,26230,26232,26622,26984,27273,27491,27712,28096,28136,28191,28254,28702,28833,29582,29693,30010,30555,30855,31118,31243,31357,31934,32142,33351,35330,35562,35998,37165,37194,37336,37478,37580,37664,38662,38742,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,38748,38914,40718,21046,21137,21884,22564,24093,24351,24716,25552,26799,28639,31085,31532,33229,34234,35069,35576,36420,37261,38500,38555,38717,38988,40778,20430,20806,20939,21161,22066,24340,24427,25514,25805,26089,26177,26362,26361,26397,26781,26839,27133,28437,28526,29031,29157,29226,29866,30522,31062,31066,31199,31264,31381,31895,31967,32068,32368,32903,34299,34468,35412,35519,36249,36481,36896,36973,37347,38459,38613,40165,26063,31751,36275,37827,23384,23562,21330,25305,29469,20519,23447,24478,24752,24939,26837,28121,29742,31278,32066,32156,32305,33131,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,36394,36405,37758,37912,20304,22352,24038,24231,25387,32618,20027,20303,20367,20570,23005,32964,21610,21608,22014,22863,23449,24030,24282,26205,26417,26609,26666,27880,27954,28234,28557,28855,29664,30087,31820,32002,32044,32162,33311,34523,35387,35461,36208,36490,36659,36913,37198,37202,37956,39376,31481,31909,20426,20737,20934,22472,23535,23803,26201,27197,27994,28310,28652,28940,30063,31459,34850,36897,36981,38603,39423,33537,20013,20210,34886,37325,21373,27355,26987,27713,33914,22686,24974,26366,25327,28893,29969,30151,32338,33976,35657,36104,20043,21482,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,21675,22320,22336,24535,25345,25351,25711,25903,26088,26234,26525,26547,27490,27744,27802,28460,30693,30757,31049,31063,32025,32930,33026,33267,33437,33463,34584,35468,63996,36100,36286,36978,30452,31257,31287,32340,32887,21767,21972,22645,25391,25634,26185,26187,26733,27035,27524,27941,28337,29645,29800,29857,30043,30137,30433,30494,30603,31206,32265,32285,33275,34095,34967,35386,36049,36587,36784,36914,37805,38499,38515,38663,20356,21489,23018,23241,24089,26702,29894,30142,31209,31378,33187,34541,36074,36300,36845,26015,26389,63997,22519,28503,32221,36655,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,37878,38598,24501,25074,28548,19988,20376,20511,21449,21983,23919,24046,27425,27492,30923,31642,63998,36425,36554,36974,25417,25662,30528,31364,37679,38015,40810,25776,28591,29158,29864,29914,31428,31762,32386,31922,32408,35738,36106,38013,39184,39244,21049,23519,25830,26413,32046,20717,21443,22649,24920,24921,25082,26028,31449,35730,35734,20489,20513,21109,21809,23100,24288,24432,24884,25950,26124,26166,26274,27085,28356,28466,29462,30241,31379,33081,33369,33750,33980,20661,22512,23488,23528,24425,25505,30758,32181,33756,34081,37319,37365,20874,26613,31574,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,36012,20932,22971,24765,34389,20508,63999,21076,23610,24957,25114,25299,25842,26021,28364,30240,33034,36448,38495,38587,20191,21315,21912,22825,24029,25797,27849,28154,29588,31359,33307,34214,36068,36368,36983,37351,38369,38433,38854,20984,21746,21894,24505,25764,28552,32180,36639,36685,37941,20681,23574,27838,28155,29979,30651,31805,31844,35449,35522,22558,22974,24086,25463,29266,30090,30571,35548,36028,36626,24307,26228,28152,32893,33729,35531,38737,39894,64000,21059,26367,28053,28399,32224,35558,36910,36958,39636,21021,21119,21736,24980,25220,25307,26786,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,26898,26970,27189,28818,28966,30813,30977,30990,31186,31245,32918,33400,33493,33609,34121,35970,36229,37218,37259,37294,20419,22225,29165,30679,34560,35320,23544,24534,26449,37032,21474,22618,23541,24740,24961,25696,32317,32880,34085,37507,25774,20652,23828,26368,22684,25277,25512,26894,27000,27166,28267,30394,31179,33467,33833,35535,36264,36861,37138,37195,37276,37648,37656,37786,38619,39478,39949,19985,30044,31069,31482,31569,31689,32302,33988,36441,36468,36600,36880,26149,26943,29763,20986,26414,40668,20805,24544,27798,34802,34909,34935,24756,33205,33795,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,36101,21462,21561,22068,23094,23601,28810,32736,32858,33030,33261,36259,37257,39519,40434,20596,20164,21408,24827,28204,23652,20360,20516,21988,23769,24159,24677,26772,27835,28100,29118,30164,30196,30305,31258,31305,32199,32251,32622,33268,34473,36636,38601,39347,40786,21063,21189,39149,35242,19971,26578,28422,20405,23522,26517,27784,28024,29723,30759,37341,37756,34756,31204,31281,24555,20182,21668,21822,22702,22949,24816,25171,25302,26422,26965,33333,38464,39345,39389,20524,21331,21828,22396,64001,25176,64002,25826,26219,26589,28609,28655,29730,29752,35351,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,37944,21585,22022,22374,24392,24986,27470,28760,28845,32187,35477,22890,33067,25506,30472,32829,36010,22612,25645,27067,23445,24081,28271,64003,34153,20812,21488,22826,24608,24907,27526,27760,27888,31518,32974,33492,36294,37040,39089,64004,25799,28580,25745,25860,20814,21520,22303,35342,24927,26742,64005,30171,31570,32113,36890,22534,27084,33151,35114,36864,38969,20600,22871,22956,25237,36879,39722,24925,29305,38358,22369,23110,24052,25226,25773,25850,26487,27874,27966,29228,29750,30772,32631,33453,36315,38935,21028,22338,26495,29256,29923,36009,36774,37393,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,38442,20843,21485,25420,20329,21764,24726,25943,27803,28031,29260,29437,31255,35207,35997,24429,28558,28921,33192,24846,20415,20559,25153,29255,31687,32232,32745,36941,38829,39449,36022,22378,24179,26544,33805,35413,21536,23318,24163,24290,24330,25987,32954,34109,38281,38491,20296,21253,21261,21263,21638,21754,22275,24067,24598,25243,25265,25429,64006,27873,28006,30129,30770,32990,33071,33502,33889,33970,34957,35090,36875,37610,39165,39825,24133,26292,26333,28689,29190,64007,20469,21117,24426,24915,26451,27161,28418,29922,31080,34920,35961,39111,39108,39491,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,21697,31263,26963,35575,35914,39080,39342,24444,25259,30130,30382,34987,36991,38466,21305,24380,24517,27852,29644,30050,30091,31558,33534,39325,20047,36924,19979,20309,21414,22799,24264,26160,27827,29781,33655,34662,36032,36944,38686,39957,22737,23416,34384,35604,40372,23506,24680,24717,26097,27735,28450,28579,28698,32597,32752,38289,38290,38480,38867,21106,36676,20989,21547,21688,21859,21898,27323,28085,32216,33382,37532,38519,40569,21512,21704,30418,34532,38308,38356,38492,20130,20233,23022,23270,24055,24658,25239,26477,26689,27782,28207,32568,32923,33322,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,64008,64009,38917,20133,20565,21683,22419,22874,23401,23475,25032,26999,28023,28707,34809,35299,35442,35559,36994,39405,39608,21182,26680,20502,24184,26447,33607,34892,20139,21521,22190,29670,37141,38911,39177,39255,39321,22099,22687,34395,35377,25010,27382,29563,36562,27463,38570,39511,22869,29184,36203,38761,20436,23796,24358,25080,26203,27883,28843,29572,29625,29694,30505,30541,32067,32098,32291,33335,34898,64010,36066,37449,39023,23377,31348,34880,38913,23244,20448,21332,22846,23805,25406,28025,29433,33029,33031,33698,37583,38960,20136,20804,21009,22411,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,24418,27842,28366,28677,28752,28847,29074,29673,29801,33610,34722,34913,36872,37026,37795,39336,20846,24407,24800,24935,26291,34137,36426,37295,38795,20046,20114,21628,22741,22778,22909,23733,24359,25142,25160,26122,26215,27627,28009,28111,28246,28408,28564,28640,28649,28765,29392,29733,29786,29920,30355,31068,31946,32286,32993,33446,33899,33983,34382,34399,34676,35703,35946,37804,38912,39013,24785,25110,37239,23130,26127,28151,28222,29759,39746,24573,24794,31503,21700,24344,27742,27859,27946,28888,32005,34425,35340,40251,21270,21644,23301,27194,28779,30069,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,31117,31166,33457,33775,35441,35649,36008,38772,64011,25844,25899,30906,30907,31339,20024,21914,22864,23462,24187,24739,25563,27489,26213,26707,28185,29029,29872,32008,36996,39529,39973,27963,28369,29502,35905,38346,20976,24140,24488,24653,24822,24880,24908,26179,26180,27045,27841,28255,28361,28514,29004,29852,30343,31681,31783,33618,34647,36945,38541,40643,21295,22238,24315,24458,24674,24724,25079,26214,26371,27292,28142,28590,28784,29546,32362,33214,33588,34516,35496,36036,21123,29554,23446,27243,37892,21742,22150,23389,25928,25989,26313,26783,28045,28102,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,29243,32948,37237,39501,20399,20505,21402,21518,21564,21897,21957,24127,24460,26429,29030,29661,36869,21211,21235,22628,22734,28932,29071,29179,34224,35347,26248,34216,21927,26244,29002,33841,21321,21913,27585,24409,24509,25582,26249,28999,35569,36637,40638,20241,25658,28875,30054,34407,24676,35662,40440,20807,20982,21256,27958,33016,40657,26133,27427,28824,30165,21507,23673,32007,35350,27424,27453,27462,21560,24688,27965,32725,33288,20694,20958,21916,22123,22221,23020,23305,24076,24985,24984,25137,26206,26342,29081,29113,29114,29351,31143,31232,32690,35440,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null}; + +// https://encoding.spec.whatwg.org/#euc-kr-decoder +decoder::result euc_kr_decoder::handler(inout string& input, inout int& index, out int ch[2]) +{ + int b = index == (int)input.size() ? EOF : (byte)input[index++]; // read input byte + + // 1. If byte is end-of-queue and EUC-KR lead is not 0x00, set EUC-KR lead to 0x00 and return error. + if (b == EOF && m_lead != 0) + { + m_lead = 0; + return result_error; + } + + // 2. If byte is end-of-queue and EUC-KR lead is 0x00, return finished. + if (b == EOF && m_lead == 0) + return result_finished; + + // 3. + if (m_lead != 0) + { + int lead = m_lead; + int pointer = null; + m_lead = 0; + + // 1. + if (b >= 0x41 && b <= 0xFE) pointer = (lead - 0x81) * 190 + (b - 0x41); + + // 2. + int code_point = pointer != null ? index_code_point(pointer, m_index) : null; + + // 3. + if (code_point != null) + { + *ch = code_point; + return result_codepoint; + } + + // 4. If byte is an ASCII byte, restore byte to ioQueue. + if (b >= 0 && b <= 0x7F) index--; + + // 5. + return result_error; + } + + // 4. If byte is an ASCII byte, return a code point whose value is byte. + if (b >= 0 && b <= 0x7F) + { + *ch = b; + return result_codepoint; + } + + // 5. If byte is in the range 0x81 to 0xFE, inclusive, set EUC-KR lead to byte and return continue. + if (b >= 0x81 && b <= 0xFE) + { + m_lead = b; + return result_continue; + } + + // 6. + return result_error; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +struct replacement_decoder final : decoder +{ + bool error_returned = false; + + result handler(string& input, int& index, int ch[2]) override; +}; + +// https://encoding.spec.whatwg.org/#replacement-decoder +decoder::result replacement_decoder::handler(inout string& input, inout int& index, int[2]) +{ + // 1. If byte is end-of-queue, return finished. + if (index == (int)input.size()) + return result_finished; + + // 2. If replacement error returned is false, set replacement error returned to true and return error. + if (!error_returned) + { + error_returned = true; + return result_error; + } + + // 3. Return finished. + return result_finished; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +struct utf_16_decoder final : decoder +{ + int m_lead_byte = null; + int m_lead_surrogate = null; + bool m_utf_16be; + + utf_16_decoder(encoding _encoding) : m_utf_16be(_encoding == encoding::utf_16be) {} + + result handler(string& input, int& index, int ch[2]) override; +}; + +// https://encoding.spec.whatwg.org/#shared-utf-16-decoder +decoder::result utf_16_decoder::handler(inout string& input, inout int& index, out int ch[2]) +{ + int b = index == (int)input.size() ? EOF : (byte)input[index++]; // read input byte + + // 1. If byte is end-of-queue and either UTF-16 lead byte or UTF-16 lead surrogate is non-null, set UTF-16 lead byte and UTF-16 lead surrogate to null, and return error. + if (b == EOF && (m_lead_byte != null || m_lead_surrogate != null)) + { + m_lead_byte = null; + m_lead_surrogate = null; + return result_error; + } + + // 2. If byte is end-of-queue and UTF-16 lead byte and UTF-16 lead surrogate are null, return finished. + if (b == EOF && m_lead_byte == null && m_lead_surrogate == null) + return result_finished; + + // 3. If UTF-16 lead byte is null, set UTF-16 lead byte to byte and return continue. + if (m_lead_byte == null) + { + m_lead_byte = b; + return result_continue; + } + + // 4. + int code_unit = m_utf_16be ? (m_lead_byte << 8) + b : (b << 8) + m_lead_byte; + m_lead_byte = null; + + // 5. + if (m_lead_surrogate != null) + { + int lead_surrogate = m_lead_surrogate; + m_lead_surrogate = null; + + // 1. If code unit is in the range U+DC00 to U+DFFF, inclusive, return a code point whose value is 0x10000 + ((lead surrogate − 0xD800) << 10) + (code unit − 0xDC00). + if (code_unit >= 0xDC00 && code_unit <= 0xDFFF) + { + *ch = 0x10000 + ((lead_surrogate - 0xD800) << 10) + (code_unit - 0xDC00); + return result_codepoint; + } + + // 2,3. + char b1 = char(code_unit >> 8); + char b2 = char(code_unit & 0xFF); + + // 4. Let bytes be two bytes whose values are byte1 and byte2, if is UTF-16BE decoder is true, and byte2 and byte1 otherwise. + string bytes = m_utf_16be ? string{b1, b2} : string{b2, b1}; + + // 5. Restore bytes to ioQueue and return error. + input.insert(index, bytes); + return result_error; + } + + // 6. + if (code_unit >= 0xD800 && code_unit <= 0xDBFF) + { + m_lead_surrogate = code_unit; + return result_continue; + } + + // 7. + if (code_unit >= 0xDC00 && code_unit <= 0xDFFF) + return result_error; + + // 8. + *ch = code_unit; + return result_codepoint; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +struct x_user_defined_decoder final : decoder +{ + result handler(string& input, int& index, int ch[2]) override; +}; + +// https://encoding.spec.whatwg.org/#x-user-defined-decoder +decoder::result x_user_defined_decoder::handler(inout string& input, inout int& index, out int ch[2]) +{ + int b = index == (int)input.size() ? EOF : (byte)input[index++]; // read input byte + + // 1. If byte is end-of-queue, return finished. + if (b == EOF) + return result_finished; + + // 2. If byte is an ASCII byte, return a code point whose value is byte. + if (b >= 0 && b <= 0x7F) + { + *ch = b; + return result_codepoint; + } + + // 3. Return a code point whose value is 0xF780 + byte − 0x80. + *ch = 0xF780 + b - 0x80; + return result_codepoint; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +decoder::ptr get_decoder(encoding _encoding) +{ + switch (_encoding) + { + case encoding::utf_8: + return make_shared<utf_8_decoder>(); + + case encoding::gbk: // https://encoding.spec.whatwg.org/#gbk-decoder + case encoding::gb18030: + return make_shared<gb18030_decoder>(); + + case encoding::big5: + return make_shared<big5_decoder>(); + + case encoding::euc_jp: + return make_shared<euc_jp_decoder>(); + + case encoding::iso_2022_jp: + return make_shared<iso_2022_jp_decoder>(); + + case encoding::shift_jis: + return make_shared<shift_jis_decoder>(); + + case encoding::euc_kr: + return make_shared<euc_kr_decoder>(); + + + case encoding::replacement: + return make_shared<replacement_decoder>(); + + case encoding::utf_16be: + case encoding::utf_16le: + return make_shared<utf_16_decoder>(_encoding); + + case encoding::x_user_defined: + return make_shared<x_user_defined_decoder>(); + + default: + // single-byte encoding + if (_encoding >= encoding::ibm866 && _encoding <= encoding::x_mac_cyrillic) + return make_shared<single_byte_decoder>(_encoding); + } + + return nullptr; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// https://encoding.spec.whatwg.org/#names-and-labels +// https://encoding.spec.whatwg.org/encodings.json +struct { + const char* name; + encoding coding; +} labels[] = +{ + { "unicode-1-1-utf-8", encoding::utf_8 }, + { "unicode11utf8", encoding::utf_8 }, + { "unicode20utf8", encoding::utf_8 }, + { "utf-8", encoding::utf_8 }, + { "utf8", encoding::utf_8 }, + { "x-unicode20utf8", encoding::utf_8 }, + + { "866", encoding::ibm866 }, + { "cp866", encoding::ibm866 }, + { "csibm866", encoding::ibm866 }, + { "ibm866", encoding::ibm866 }, + + { "csisolatin2", encoding::iso_8859_2 }, + { "iso-8859-2", encoding::iso_8859_2 }, + { "iso-ir-101", encoding::iso_8859_2 }, + { "iso8859-2", encoding::iso_8859_2 }, + { "iso88592", encoding::iso_8859_2 }, + { "iso_8859-2", encoding::iso_8859_2 }, + { "iso_8859-2:1987", encoding::iso_8859_2 }, + { "l2", encoding::iso_8859_2 }, + { "latin2", encoding::iso_8859_2 }, + + { "csisolatin3", encoding::iso_8859_3 }, + { "iso-8859-3", encoding::iso_8859_3 }, + { "iso-ir-109", encoding::iso_8859_3 }, + { "iso8859-3", encoding::iso_8859_3 }, + { "iso88593", encoding::iso_8859_3 }, + { "iso_8859-3", encoding::iso_8859_3 }, + { "iso_8859-3:1988", encoding::iso_8859_3 }, + { "l3", encoding::iso_8859_3 }, + { "latin3", encoding::iso_8859_3 }, + + { "csisolatin4", encoding::iso_8859_4 }, + { "iso-8859-4", encoding::iso_8859_4 }, + { "iso-ir-110", encoding::iso_8859_4 }, + { "iso8859-4", encoding::iso_8859_4 }, + { "iso88594", encoding::iso_8859_4 }, + { "iso_8859-4", encoding::iso_8859_4 }, + { "iso_8859-4:1988", encoding::iso_8859_4 }, + { "l4", encoding::iso_8859_4 }, + { "latin4", encoding::iso_8859_4 }, + + { "csisolatincyrillic", encoding::iso_8859_5 }, + { "cyrillic", encoding::iso_8859_5 }, + { "iso-8859-5", encoding::iso_8859_5 }, + { "iso-ir-144", encoding::iso_8859_5 }, + { "iso8859-5", encoding::iso_8859_5 }, + { "iso88595", encoding::iso_8859_5 }, + { "iso_8859-5", encoding::iso_8859_5 }, + { "iso_8859-5:1988", encoding::iso_8859_5 }, + + { "arabic", encoding::iso_8859_6 }, + { "asmo-708", encoding::iso_8859_6 }, + { "csiso88596e", encoding::iso_8859_6 }, + { "csiso88596i", encoding::iso_8859_6 }, + { "csisolatinarabic", encoding::iso_8859_6 }, + { "ecma-114", encoding::iso_8859_6 }, + { "iso-8859-6", encoding::iso_8859_6 }, + { "iso-8859-6-e", encoding::iso_8859_6 }, + { "iso-8859-6-i", encoding::iso_8859_6 }, + { "iso-ir-127", encoding::iso_8859_6 }, + { "iso8859-6", encoding::iso_8859_6 }, + { "iso88596", encoding::iso_8859_6 }, + { "iso_8859-6", encoding::iso_8859_6 }, + { "iso_8859-6:1987", encoding::iso_8859_6 }, + + { "csisolatingreek", encoding::iso_8859_7 }, + { "ecma-118", encoding::iso_8859_7 }, + { "elot_928", encoding::iso_8859_7 }, + { "greek", encoding::iso_8859_7 }, + { "greek8", encoding::iso_8859_7 }, + { "iso-8859-7", encoding::iso_8859_7 }, + { "iso-ir-126", encoding::iso_8859_7 }, + { "iso8859-7", encoding::iso_8859_7 }, + { "iso88597", encoding::iso_8859_7 }, + { "iso_8859-7", encoding::iso_8859_7 }, + { "iso_8859-7:1987", encoding::iso_8859_7 }, + { "sun_eu_greek", encoding::iso_8859_7 }, + + { "csiso88598e", encoding::iso_8859_8 }, + { "csisolatinhebrew", encoding::iso_8859_8 }, + { "hebrew", encoding::iso_8859_8 }, + { "iso-8859-8", encoding::iso_8859_8 }, + { "iso-8859-8-e", encoding::iso_8859_8 }, + { "iso-ir-138", encoding::iso_8859_8 }, + { "iso8859-8", encoding::iso_8859_8 }, + { "iso88598", encoding::iso_8859_8 }, + { "iso_8859-8", encoding::iso_8859_8 }, + { "iso_8859-8:1988", encoding::iso_8859_8 }, + { "visual", encoding::iso_8859_8 }, + + { "csiso88598i", encoding::iso_8859_8_i }, + { "iso-8859-8-i", encoding::iso_8859_8_i }, + { "logical", encoding::iso_8859_8_i }, + + { "csisolatin6", encoding::iso_8859_10 }, + { "iso-8859-10", encoding::iso_8859_10 }, + { "iso-ir-157", encoding::iso_8859_10 }, + { "iso8859-10", encoding::iso_8859_10 }, + { "iso885910", encoding::iso_8859_10 }, + { "l6", encoding::iso_8859_10 }, + { "latin6", encoding::iso_8859_10 }, + + { "iso-8859-13", encoding::iso_8859_13 }, + { "iso8859-13", encoding::iso_8859_13 }, + { "iso885913", encoding::iso_8859_13 }, + + { "iso-8859-14", encoding::iso_8859_14 }, + { "iso8859-14", encoding::iso_8859_14 }, + { "iso885914", encoding::iso_8859_14 }, + + { "csisolatin9", encoding::iso_8859_15 }, + { "iso-8859-15", encoding::iso_8859_15 }, + { "iso8859-15", encoding::iso_8859_15 }, + { "iso885915", encoding::iso_8859_15 }, + { "iso_8859-15", encoding::iso_8859_15 }, + { "l9", encoding::iso_8859_15 }, + + { "iso-8859-16", encoding::iso_8859_16 }, + + { "cskoi8r", encoding::koi8_r }, + { "koi", encoding::koi8_r }, + { "koi8", encoding::koi8_r }, + { "koi8-r", encoding::koi8_r }, + { "koi8_r", encoding::koi8_r }, + + { "koi8-ru", encoding::koi8_u }, + { "koi8-u", encoding::koi8_u }, + + { "csmacintosh", encoding::macintosh }, + { "mac", encoding::macintosh }, + { "macintosh", encoding::macintosh }, + { "x-mac-roman", encoding::macintosh }, + + { "dos-874", encoding::windows_874 }, + { "iso-8859-11", encoding::windows_874 }, + { "iso8859-11", encoding::windows_874 }, + { "iso885911", encoding::windows_874 }, + { "tis-620", encoding::windows_874 }, + { "windows-874", encoding::windows_874 }, + + { "cp1250", encoding::windows_1250 }, + { "windows-1250", encoding::windows_1250 }, + { "x-cp1250", encoding::windows_1250 }, + + { "cp1251", encoding::windows_1251 }, + { "windows-1251", encoding::windows_1251 }, + { "x-cp1251", encoding::windows_1251 }, + + { "ansi_x3.4-1968", encoding::windows_1252 }, + { "ascii", encoding::windows_1252 }, + { "cp1252", encoding::windows_1252 }, + { "cp819", encoding::windows_1252 }, + { "csisolatin1", encoding::windows_1252 }, + { "ibm819", encoding::windows_1252 }, + { "iso-8859-1", encoding::windows_1252 }, + { "iso-ir-100", encoding::windows_1252 }, + { "iso8859-1", encoding::windows_1252 }, + { "iso88591", encoding::windows_1252 }, + { "iso_8859-1", encoding::windows_1252 }, + { "iso_8859-1:1987", encoding::windows_1252 }, + { "l1", encoding::windows_1252 }, + { "latin1", encoding::windows_1252 }, + { "us-ascii", encoding::windows_1252 }, + { "windows-1252", encoding::windows_1252 }, + { "x-cp1252", encoding::windows_1252 }, + + { "cp1253", encoding::windows_1253 }, + { "windows-1253", encoding::windows_1253 }, + { "x-cp1253", encoding::windows_1253 }, + + { "cp1254", encoding::windows_1254 }, + { "csisolatin5", encoding::windows_1254 }, + { "iso-8859-9", encoding::windows_1254 }, + { "iso-ir-148", encoding::windows_1254 }, + { "iso8859-9", encoding::windows_1254 }, + { "iso88599", encoding::windows_1254 }, + { "iso_8859-9", encoding::windows_1254 }, + { "iso_8859-9:1989", encoding::windows_1254 }, + { "l5", encoding::windows_1254 }, + { "latin5", encoding::windows_1254 }, + { "windows-1254", encoding::windows_1254 }, + { "x-cp1254", encoding::windows_1254 }, + + { "cp1255", encoding::windows_1255 }, + { "windows-1255", encoding::windows_1255 }, + { "x-cp1255", encoding::windows_1255 }, + + { "cp1256", encoding::windows_1256 }, + { "windows-1256", encoding::windows_1256 }, + { "x-cp1256", encoding::windows_1256 }, + + { "cp1257", encoding::windows_1257 }, + { "windows-1257", encoding::windows_1257 }, + { "x-cp1257", encoding::windows_1257 }, + + { "cp1258", encoding::windows_1258 }, + { "windows-1258", encoding::windows_1258 }, + { "x-cp1258", encoding::windows_1258 }, + + { "x-mac-cyrillic", encoding::x_mac_cyrillic }, + { "x-mac-ukrainian", encoding::x_mac_cyrillic }, + + { "chinese", encoding::gbk }, + { "csgb2312", encoding::gbk }, + { "csiso58gb231280", encoding::gbk }, + { "gb2312", encoding::gbk }, + { "gb_2312", encoding::gbk }, + { "gb_2312-80", encoding::gbk }, + { "gbk", encoding::gbk }, + { "iso-ir-58", encoding::gbk }, + { "x-gbk", encoding::gbk }, + + { "gb18030", encoding::gb18030 }, + + { "big5", encoding::big5 }, + { "big5-hkscs", encoding::big5 }, + { "cn-big5", encoding::big5 }, + { "csbig5", encoding::big5 }, + { "x-x-big5", encoding::big5 }, + + { "cseucpkdfmtjapanese", encoding::euc_jp }, + { "euc-jp", encoding::euc_jp }, + { "x-euc-jp", encoding::euc_jp }, + + { "csiso2022jp", encoding::iso_2022_jp }, + { "iso-2022-jp", encoding::iso_2022_jp }, + + { "csshiftjis", encoding::shift_jis }, + { "ms932", encoding::shift_jis }, + { "ms_kanji", encoding::shift_jis }, + { "shift-jis", encoding::shift_jis }, + { "shift_jis", encoding::shift_jis }, + { "sjis", encoding::shift_jis }, + { "windows-31j", encoding::shift_jis }, + { "x-sjis", encoding::shift_jis }, + + { "cseuckr", encoding::euc_kr }, + { "csksc56011987", encoding::euc_kr }, + { "euc-kr", encoding::euc_kr }, + { "iso-ir-149", encoding::euc_kr }, + { "korean", encoding::euc_kr }, + { "ks_c_5601-1987", encoding::euc_kr }, + { "ks_c_5601-1989", encoding::euc_kr }, + { "ksc5601", encoding::euc_kr }, + { "ksc_5601", encoding::euc_kr }, + { "windows-949", encoding::euc_kr }, + + { "csiso2022kr", encoding::replacement }, + { "hz-gb-2312", encoding::replacement }, + { "iso-2022-cn", encoding::replacement }, + { "iso-2022-cn-ext", encoding::replacement }, + { "iso-2022-kr", encoding::replacement }, + { "replacement", encoding::replacement }, + + { "unicodefffe", encoding::utf_16be }, + { "utf-16be", encoding::utf_16be }, + + { "csunicode", encoding::utf_16le }, + { "iso-10646-ucs-2", encoding::utf_16le }, + { "ucs-2", encoding::utf_16le }, + { "unicode", encoding::utf_16le }, + { "unicodefeff", encoding::utf_16le }, + { "utf-16", encoding::utf_16le }, + { "utf-16le", encoding::utf_16le }, + + { "x-user-defined", encoding::x_user_defined } +}; + +// https://encoding.spec.whatwg.org/#concept-encoding-get +encoding get_encoding(string label) +{ + lcase(trim(label)); + for (const auto& l : labels) + { + if (label == l.name) + return l.coding; + } + return encoding::null; +} + +const size_t EOL = string::npos; + +// https://html.spec.whatwg.org/multipage/urls-and-fetching.html#algorithm-for-extracting-a-character-encoding-from-a-meta-element +encoding extract_encoding_from_meta_element(string s) +{ + lcase(s); // for step 2. + + // 1. Let position be a pointer into s, initially pointing at the start of the string. + size_t pos = 0; + + // 2. Loop: Find the first seven characters in s after position that are an ASCII case-insensitive match for + // the word "charset". If no such match is found, return nothing. +loop: + pos = s.find("charset", pos); + if (pos == EOL) return encoding::null; + + // 3. Skip any ASCII whitespace that immediately follow the word "charset" (there might not be any). + pos += strlen("charset"); // skip "charset" + while (is_whitespace(s[pos])) pos++; + + // 4. If the next character is not a U+003D (=), then move position to point just before that next character, + // and jump back to the step labeled loop. + if (s[pos] != '=') goto loop; + + // 5. Skip any ASCII whitespace that immediately follow the equals sign (there might not be any). + pos++; // skip '=' + while (is_whitespace(s[pos])) pos++; + + // 6. Process the next character as follows: + size_t end; + // -> If it is a U+0022 (") and there is a later U+0022 (") in s + // -> If it is a U+0027 (') and there is a later U+0027 (') in s + if ((s[pos] == '"' && (end = s.find('"', pos + 1)) != EOL) || + (s[pos] == '\'' && (end = s.find('\'', pos + 1)) != EOL)) + { + // Return the result of getting an encoding from the substring that is between this character and the next earliest occurrence of this character. + return get_encoding(s.substr(pos + 1, end - pos - 1)); + } + // -> If it is an unmatched U+0022 (") + // -> If it is an unmatched U+0027 (') + // -> If there is no next character + if (s[pos] == '"' || s[pos] == '\'' || s[pos] == 0) + return encoding::null; // Return nothing. + + // -> Otherwise + // Return the result of getting an encoding from the substring that consists of this character up to + // but not including the first ASCII whitespace or U+003B (;), or the end of s, whichever comes first. + end = s.find_first_of(" \n\r\f\t;", pos + 1); + return get_encoding(s.substr(pos, end - pos)); // works for end == EOL too +} + +// see step 5 of https://html.spec.whatwg.org/multipage/parsing.html#encoding-sniffing-algorithm +bool end_condition(int index) +{ + return index >= 1024; +} + +void increment(int& index, const string& str) +{ + index++; + if (index >= (int)str.size() || end_condition(index)) + throw 0; // abort prescan +} + +// https://html.spec.whatwg.org/multipage/parsing.html#concept-get-attributes-when-sniffing +bool prescan_get_attribute(const string& str, inout int& index, out string& name, out string& value) +{ + // 1. + while (is_whitespace(str[index]) || str[index] == '/') increment(index, str); + + // 2. + if (str[index] == '>') return false; + + // 3. + name = value = ""; + + // 4. +step_4: + if (str[index] == '=' && name != "") + { + increment(index, str); + goto process_value; + } + else if (is_whitespace(str[index])) + goto spaces; + else if (str[index] == '/' || str[index] == '>') + return true; + else // A..Z or anything else + name += (char)lowcase(str[index]); + + // 5. + increment(index, str); + goto step_4; + + // 6. +spaces: + while (is_whitespace(str[index])) increment(index, str); + + // 7. + if (str[index] != '=') + return true; + + // 8. + increment(index, str); // skip '=' + + // 9. +process_value: + while (is_whitespace(str[index])) increment(index, str); + + // 10. + if (str[index] == '"' || str[index] == '\'') + { + // 1. + char b = str[index]; + + // 2. + quote_loop: + increment(index, str); + + // 3. + if (str[index] == b) + { + increment(index, str); + return true; + } + + // 4,5. + else + value += (char)lowcase(str[index]); + + // 6. + goto quote_loop; + } + else if (str[index] == '>') + return true; + else // A..Z or anything else + value += (char)lowcase(str[index]); + + // 11. +step_11: + if (is_whitespace(str[index]) || str[index] == '>') + return true; + else // A..Z or anything else + value += (char)lowcase(str[index]); + + // 12. + increment(index, str); + goto step_11; +} + +// https://html.spec.whatwg.org/multipage/parsing.html#prescan-a-byte-stream-to-determine-its-encoding +encoding prescan_a_byte_stream_to_determine_its_encoding(const string& str) +{ + // 1. Let fallback encoding be null. - bogus, never used + // 2. Let position be a pointer to a byte in the input byte stream, initially pointing at the first byte. + int index = 0; + + // 3. Prescan for UTF-16 XML declarations: + if (match(str, index, {"<\0?\0x\0", 6})) return encoding::utf_16le; + if (match(str, index, {"\0<\0?\0x", 6})) return encoding::utf_16be; + + // 4. +loop: + if (match(str, index, "<!--")) + { + index = (int)str.find("-->", index); + if (index == -1 || end_condition(index)) throw 0; // abort prescan + index += 2; // not 3 because it will be incremented one more time in step 5 (next_byte) + } + else if (match_i(str, index, "<meta") && (is_whitespace(str[index + 5]) || str[index + 5] == '/')) + { + // 1. + // NOTE: Should be 6, but the standard says 5. It doesn't really matter because prescan_get_attribute will skip the WS or / anyway. + index += 5; + // 2,3,4,5. + string_vector attribute_list; + bool got_pragma = false; + int need_pragma = -1; // three values: -1 ("null"), true and false + encoding charset = encoding::null; + + // 6. + attributes: + string attr_name, attr_value; + if (!prescan_get_attribute(str, index, attr_name, attr_value)) + goto processing; + + // 7. If the attribute's name is already in attribute list, then return to the step labeled attributes. + if (contains(attribute_list, attr_name)) + goto attributes; + + // 8. + attribute_list.push_back(attr_name); + + // 9. + // NOTE: attr_name and attr_value are already lowcased, see prescan_get_attribute + if (attr_name == "http-equiv" && attr_value == "content-type") + { + got_pragma = true; + } + else if (attr_name == "content") + { + auto encoding = extract_encoding_from_meta_element(attr_value); + // If a character encoding is returned, and if charset is still set to null + if (encoding != encoding::null && charset == encoding::null) + { + charset = encoding; + need_pragma = true; + } + } + else if (attr_name == "charset") + { + charset = get_encoding(attr_value); + need_pragma = false; + } + + // 10. + goto attributes; + + // 11. Processing: If need pragma is null, then jump to the step below labeled next byte. + processing: + if (need_pragma == -1) + goto next_byte; + + // 12. + if (need_pragma == (int)true && !got_pragma) + goto next_byte; + + // 13. + if (charset == encoding::null) + goto next_byte; + + // 14. + if (charset == encoding::utf_16be || charset == encoding::utf_16le) + charset = encoding::utf_8; + + // 15. + if (charset == encoding::x_user_defined) + charset = encoding::windows_1252; + + // 16. + return charset; + } + else if ((str[index] == '<' && str[index + 1] == '/' && is_letter(str[index + 2])) || + (str[index] == '<' && is_letter(str[index + 1]))) + { + // 1. + index = (int)str.find_first_of(" \t\r\n\f>", index); + if (index == -1 || end_condition(index)) throw 0; // abort prescan + + // 2. + string tmp; + while (prescan_get_attribute(str, index, tmp, tmp)) {} + goto next_byte; + } + else if (str[index] == '<' && is_one_of(str[index + 1], '!', '/', '?')) + { + index = (int)str.find('>', index); + if (index == -1 || end_condition(index)) throw 0; // abort prescan + } + + // 5. +next_byte: + increment(index, str); + goto loop; +} + +// https://html.spec.whatwg.org/multipage/parsing.html#concept-get-xml-encoding-when-sniffing +encoding get_xml_encoding(const string& str) +{ + // 1. Let encodingPosition be a pointer to the start of the stream. + int index = 0; + + // 2. + if (!match(str, index, "<?xml")) + return encoding::null; + + // 3. + // NOTE: xmlDeclarationEnd is unused + index = (int)str.find('>', index); + if (index == -1) return encoding::null; + + // 4. + index = (int)str.find("encoding", index); + if (index == -1) return encoding::null; + + // 5. + index += (int)strlen("encoding"); + + // 6. + while ((byte)str[index] <= 0x20 && index < (int)str.size()) index++; + + // 7. + if (str[index] != '=') return encoding::null; + + // 8. + index++; // skip '=' + + // 9. + while ((byte)str[index] <= 0x20 && index < (int)str.size()) index++; + + // 10. Let quoteMark be the byte at encodingPosition. + char q = str[index]; + + // 11. + if (q != '"' && q != '\'') return encoding::null; + + // 12. + index++; // skip q + + // 13. Let encodingEndPosition be the position of the next occurrence of quoteMark + size_t end = str.find(q, index); + if (index == -1) return encoding::null; + + // 14. + string potentialEncoding = str.substr(index, end - index); + + // 15. + for(byte ch: potentialEncoding) if (ch <= 0x20) return encoding::null; + + // 16. + // NOTE: all encoding labels are pure ASCII, no need to do isomorphic decoding + encoding encoding = get_encoding(potentialEncoding); + + // 17. + if (encoding == encoding::utf_16be || encoding == encoding::utf_16le) + encoding = encoding::utf_8; + + // 18. + return encoding; +} + +// https://html.spec.whatwg.org/multipage/parsing.html#prescan-a-byte-stream-to-determine-its-encoding +encoding prescan_for_encoding(const string& str) +{ + try { + return prescan_a_byte_stream_to_determine_its_encoding(str); + } + catch (int) + { + return get_xml_encoding(str); + } +} + +// https://html.spec.whatwg.org/multipage/parsing.html#encoding-sniffing-algorithm +// see also doc/document_createFromString.txt +void encoding_sniffing_algorithm(estring& str) +{ + // 1. If the result of BOM sniffing is an encoding, return that encoding with confidence certain. + encoding encoding = bom_sniff(str); + if (encoding != encoding::null) + { + str.encoding = encoding; + str.confidence = confidence::certain; + return; + } + + // 2. User-defined override encoding -> return { encoding, confidence: certain} + + // 3. optionally wait for more bytes of the resource to be available + + // 4. HTTP encoding -> return { encoding, confidence: certain} + + if (str.encoding != encoding::null && str.confidence == confidence::certain) + return; + + // all below return confidence: tentative + + // 5. Optionally prescan the byte stream to determine its encoding -> return { encoding, confidence: tentative} + encoding = prescan_for_encoding(str); + if (encoding != encoding::null) + { + str.encoding = encoding; + str.confidence = confidence::tentative; + return; + } + + // 6. encoding from parent document + + // 7. encoding of the page when it was last visited + + // 8. detect encoding with frequency analysis + + // 9. return an implementation-defined or user-specified default character encoding + // may be UTF-8 or depends on locale or smth else + + // if str has no encoding, use the default one + if (str.encoding == encoding::null) + { + str.encoding = encoding::utf_8; + str.confidence = confidence::tentative; // tentative means it will be overriden by <meta> encoding if present + } + // otherwise use str.encoding (tentative) +} + +} // namespace litehtml diff --git a/src/flex_item.cpp b/src/flex_item.cpp index d0fbaecd7..687c7374e 100644 --- a/src/flex_item.cpp +++ b/src/flex_item.cpp @@ -1,8 +1,6 @@ -#include "html.h" +#include <cmath> #include "flex_item.h" -#include "render_item.h" #include "flex_line.h" -#include <cmath> void litehtml::flex_item::init(const litehtml::containing_block_context &self_size, litehtml::formatting_context *fmt_ctx, flex_align_items align_items) @@ -50,9 +48,13 @@ void litehtml::flex_item::place(flex_line &ln, int main_pos, case flex_align_items_flex_end: if(ln.reverse_cross) { + /// If cross axis is reversed position item from start set_cross_position(ln.cross_start); - break; /// If cross axis is reversed position item from start + } else + { + set_cross_position(ln.cross_start + ln.cross_size - get_el_cross_size()); } + break; case flex_align_items_end: set_cross_position(ln.cross_start + ln.cross_size - get_el_cross_size()); break; @@ -63,8 +65,11 @@ void litehtml::flex_item::place(flex_line &ln, int main_pos, if(ln.reverse_cross) /// If cross axis is reversed position item from end { set_cross_position(ln.cross_start + ln.cross_size - get_el_cross_size()); - break; + } else + { + set_cross_position(ln.cross_start); } + break; case flex_align_items_start: set_cross_position(ln.cross_start); break; @@ -153,16 +158,18 @@ void litehtml::flex_item_row_direction::direction_specific_init(const litehtml:: if (flex_basis_predefined) { + if(predef == flex_basis_auto && el->css().get_width().is_predefined()) + { + // if width is not predefined, use content size as base size + predef = flex_basis_content; + } + switch (predef) { case flex_basis_auto: - if (!el->css().get_width().is_predefined()) - { - base_size = el->css().get_width().calc_percent(self_size.render_width) + - el->content_offset_width(); - break; - } - // if width is not predefined, use content size as base size + base_size = el->css().get_width().calc_percent(self_size.render_width) + + el->render_offset_width(); + break; case flex_basis_fit_content: case flex_basis_content: base_size = el->render(0, 0, self_size.new_width(self_size.render_width + el->content_offset_width(), @@ -262,8 +269,8 @@ void litehtml::flex_item_row_direction::align_stretch(flex_line &ln, const conta } void litehtml::flex_item_row_direction::align_baseline(litehtml::flex_line &ln, - const containing_block_context &self_size, - formatting_context *fmt_ctx) + const containing_block_context &/*self_size*/, + formatting_context */*fmt_ctx*/) { if (align & flex_align_items_last) { @@ -335,15 +342,16 @@ void litehtml::flex_item_column_direction::direction_specific_init(const litehtm if (flex_basis_predefined) { + if(predef == flex_basis_auto && el->css().get_height().is_predefined()) + { + predef = flex_basis_fit_content; + } switch (predef) { case flex_basis_auto: - if (!el->css().get_height().is_predefined()) - { - base_size = el->css().get_height().calc_percent(self_size.height) + - el->content_offset_height(); - break; - } + base_size = el->css().get_height().calc_percent(self_size.height) + + el->content_offset_height(); + break; case flex_basis_max_content: case flex_basis_fit_content: el->render(0, 0, self_size, fmt_ctx); @@ -452,8 +460,8 @@ void litehtml::flex_item_column_direction::align_stretch(flex_line &ln, const co } void litehtml::flex_item_column_direction::align_baseline(litehtml::flex_line &ln, - const containing_block_context &self_size, - formatting_context *fmt_ctx) + const containing_block_context &/*self_size*/, + formatting_context */*fmt_ctx*/) { // The fallback alignment for first baseline is start, the one for last baseline is end. if(align & flex_align_items_last) diff --git a/src/flex_line.cpp b/src/flex_line.cpp index 5f966b3b1..4e22d0aca 100644 --- a/src/flex_line.cpp +++ b/src/flex_line.cpp @@ -1,7 +1,5 @@ -#include "html.h" #include "flex_line.h" #include "flex_item.h" -#include "render_item.h" void litehtml::flex_line::distribute_free_space(int container_main_size) { @@ -49,7 +47,6 @@ void litehtml::flex_line::distribute_free_space(int container_main_size) while (processed) { int sum_scaled_flex_shrink_factor = 0; - int sum_flex_factors = 0; int remaining_free_space = container_main_size; int total_not_frozen = 0; for (auto &item: items) @@ -57,13 +54,6 @@ void litehtml::flex_line::distribute_free_space(int container_main_size) if (!item->frozen) { sum_scaled_flex_shrink_factor += item->scaled_flex_shrink_factor; - if(grow) - { - sum_flex_factors += item->grow; - } else - { - sum_flex_factors += item->shrink; - } remaining_free_space -= item->base_size; total_not_frozen++; } else @@ -261,7 +251,7 @@ void litehtml::flex_line::init(int container_main_size, bool fit_container, bool { item->el->render(0, 0, - self_size.new_width(item->main_size - item->el->content_offset_width(), containing_block_context::size_mode_exact_width), fmt_ctx, false); + self_size.new_width(item->main_size - item->el->render_offset_width(), containing_block_context::size_mode_exact_width), fmt_ctx, false); if((item->align & 0xFF) == flex_align_items_baseline) { diff --git a/src/formatting_context.cpp b/src/formatting_context.cpp index 4e41c7b5f..ab5e65a00 100644 --- a/src/formatting_context.cpp +++ b/src/formatting_context.cpp @@ -1,4 +1,3 @@ -#include "html.h" #include "render_item.h" #include "formatting_context.h" @@ -66,7 +65,7 @@ void litehtml::formatting_context::add_float(const std::shared_ptr<render_item> int litehtml::formatting_context::get_floats_height(element_float el_float) const { - int h = 0; + int h = m_current_top; for(const auto& fb : m_floats_left) { diff --git a/src/gradient.cpp b/src/gradient.cpp new file mode 100644 index 000000000..b7c7883d6 --- /dev/null +++ b/src/gradient.cpp @@ -0,0 +1,565 @@ +#include "html.h" +#include "gradient.h" +#include "css_parser.h" + +namespace litehtml +{ + +bool parse_linear_gradient_direction(const css_token_vector& tokens, int& index, float& angle, int& side); +bool parse_linear_gradient_direction_and_interpolation(const css_token_vector& tokens, gradient& gradient); +bool parse_color_interpolation_method(const css_token_vector& tokens, int& index, color_space_t& color_space, hue_interpolation_t& hue_interpolation); +bool parse_gradient_position(const css_token_vector& tokens, int& index, gradient& gradient); +bool parse_radial_gradient_shape_size_position_interpolation(const css_token_vector& tokens, gradient& result); +bool parse_conic_gradient_angle_position_interpolation(const css_token_vector& tokens, gradient& gradient); +template<class T> +bool parse_color_stop_list(const vector<css_token_vector>& list, gradient& grad, document_container* container); + +//////////////////////////////////////////////////////////////////////////////////////////// +// These combinators are currently used only in one place because the code is usually shorter without them. + +using parse_fn = std::function<bool(const css_token_vector& tokens, int& index)>; + +// a? +parse_fn opt(parse_fn a) +{ + return [=](auto&... x) + { + a(x...); + return true; + }; +} + +// a b +parse_fn seq(parse_fn a, parse_fn b) +{ + return [=](auto& t, auto& i) + { + auto save = i; + bool result = a(t, i) && b(t, i); + if (!result) i = save; // backtrack + return result; + }; +} + +// Not overloading operator|| because it is easier to get a bug: a || b || c does the wrong thing, +// see the note at https://www.w3.org/TR/css-values-4/#component-combinators. +// a || b +parse_fn oror(parse_fn a, parse_fn b) +{ + return [=](auto&... x) + { + if (a(x...)) + { + b(x...); + return true; + } + else if (b(x...)) + { + a(x...); + return true; + } + return false; + }; +} + +parse_fn operator""_x(const char* str, size_t len) +{ + return [=](const css_token_vector& tokens, int& index) + { + if (at(tokens, index).ident() == string(str, len)) + { + index++; + return true; + } + return false; + }; +} + +bool end(const css_token_vector& tokens, int index) +{ + return index == (int)tokens.size(); +} + +//////////////////////////////////////////////////////////////////////////////////////////// + +// https://drafts.csswg.org/css-images-4/#gradients +// +// <gradient> = +// <linear-gradient()> | <repeating-linear-gradient()> | +// <radial-gradient()> | <repeating-radial-gradient()> | +// <conic-gradient()> | <repeating-conic-gradient()> +// +bool parse_gradient(const css_token& token, gradient& result, document_container* container) +{ + if (token.type != CV_FUNCTION) + return false; + + auto type = _id(lowcase(token.name)); + + if (!is_one_of(type, + _linear_gradient_, _repeating_linear_gradient_, + _radial_gradient_, _repeating_radial_gradient_, + _conic_gradient_, _repeating_conic_gradient_)) + return false; + + gradient grad(type); + + if (!grad.is_linear()) { + // radial and conic position defaults to 'center' + // https://drafts.csswg.org/css-images-3/#valdef-radial-gradient-position + // https://drafts.csswg.org/css-images-4/#valdef-conic-gradient-position + grad.m_side = gradient_side_x_center | gradient_side_y_center; + } + + auto list = parse_comma_separated_list(token.value); + if (list.empty()) return false; + + bool ok; + + if (grad.is_linear()) + ok = parse_linear_gradient_direction_and_interpolation(list[0], grad); + else if (grad.is_radial()) + ok = parse_radial_gradient_shape_size_position_interpolation(list[0], grad); + else + ok = parse_conic_gradient_angle_position_interpolation(list[0], grad); + + if (ok) remove(list, 0); + + + if (grad.is_conic()) + ok = parse_color_stop_list<float>(list, grad, container); + else + ok = parse_color_stop_list<css_length>(list, grad, container); + + if (!ok) return false; + + + result = grad; + return true; +} + +// parse <length-percentage> or <angle-percentage> +bool parse_lenang(const css_token& tok, css_length& length) +{ + return parse_length(tok, length, f_length_percentage); +} +bool parse_lenang(const css_token& tok, float& angle) +{ + return parse_angle(tok, angle, true); +} + +// <color-hint> = <length-percentage> | <angle-percentage> +template<class T> // T == css_length or float +bool parse_color_hint(const css_token_vector& tokens, vector<gradient::color_stop>& color_stops) +{ + T lenang; + if (tokens.size() == 1 && parse_lenang(tokens[0], lenang)) + { + color_stops.push_back(lenang); + return true; + } + return false; +} + +// <linear-color-stop> = <color> <length-percentage>{1,2}? +// <angular-color-stop> = <color> <angle-percentage>{1,2}? +template<class T> // T == css_length or float +bool parse_color_stop(const css_token_vector& tokens, vector<gradient::color_stop>& color_stops, document_container* container) +{ + if (tokens.empty() || tokens.size() > 3) + return false; + + web_color color; + if (!parse_color(tokens[0], color, container)) + return false; + + if (tokens.size() == 1) // <color> + { + color_stops.emplace_back(color); + return true; + } + else if (tokens.size() == 2) // <color> <length-angle-percentage> + { + T lenang; + if (parse_lenang(tokens[1], lenang)) + { + color_stops.emplace_back(color, lenang); + return true; + } + } + else if (tokens.size() == 3) // <color> <length-angle-percentage> <length-angle-percentage> + { + T lenang1, lenang2; + if (parse_lenang(tokens[1], lenang1) && + parse_lenang(tokens[2], lenang2)) + { + color_stops.emplace_back(color, lenang1); + color_stops.emplace_back(color, lenang2); + return true; + } + } + return false; +} + +// <color-stop-list> = <color-stop> , [ <color-hint>? , <color-stop> ]# +template<class T> // T == css_length or float +bool parse_color_stop_list(const vector<css_token_vector>& list, gradient& grad, document_container* container) +{ + if (list.size() < 2) // at least two color-stops must be present + return false; + + if (!parse_color_stop<T>(list[0], grad.m_colors, container)) + return false; + + // [ <color-hint>? , <color-stop> ]# + for (size_t i = 1; i < list.size(); i++) + { + if (parse_color_hint<T>(list[i], grad.m_colors)) + { + i++; + if (i == list.size()) return false; // color-hint not followed by color-stop + } + if (!parse_color_stop<T>(list[i], grad.m_colors, container)) + return false; + } + return true; +} + +// https://drafts.csswg.org/css-images-4/#linear-gradients +// [ <angle> | to <side-or-corner> ] || <color-interpolation-method> +bool parse_linear_gradient_direction_and_interpolation(const css_token_vector& tokens, gradient& gradient) +{ + float angle = 180; + int side = gradient_side_none; + auto color_space = color_space_oklab; + auto hue_interpolation = hue_interpolation_shorter; + + int index = 0; + if (parse_linear_gradient_direction(tokens, index, angle, side)) + { + parse_color_interpolation_method(tokens, index, color_space, hue_interpolation); + } + else if (parse_color_interpolation_method(tokens, index, color_space, hue_interpolation)) + { + parse_linear_gradient_direction(tokens, index, angle, side); + } + else + return false; + + if (index != (int)tokens.size()) return false; + + gradient.angle = angle; + gradient.m_side = side; + gradient.color_space = color_space; + gradient.hue_interpolation = hue_interpolation; + return true; +} + +// https://drafts.csswg.org/css-images-4/#linear-gradients +// <angle> | to <side-or-corner> +// <side-or-corner> = [left | right] || [top | bottom] +bool parse_linear_gradient_direction(const css_token_vector& tokens, int& index, float& angle, int& side) +{ + if (parse_angle(at(tokens, index), angle)) + { + index++; + return true; + } + + if (at(tokens, index).ident() != "to") + return false; + + string a = at(tokens, index + 1).ident(); + string b = at(tokens, index + 2).ident(); + + if (is_one_of(a, "left", "right", "top", "bottom")) + { + if (!is_one_of(b, "left", "right", "top", "bottom")) + { + switch (_id(a)) + { + case _top_: angle = 0; break; + case _bottom_: angle = 180; break; + case _left_: angle = 270; break; + case _right_: angle = 90; break; + default: return false; + } + index += 2; + return true; + } + else + { + // fix order + if (is_one_of(a, "top", "bottom")) + swap(a, b); + + // check order + if (!is_one_of(a, "left", "right") || !is_one_of(b, "top", "bottom")) + return false; + + side = a == "left" ? gradient_side_left : gradient_side_right; + side |= b == "top" ? gradient_side_top : gradient_side_bottom; + index += 3; + return true; + } + } + return false; +} + +// https://drafts.csswg.org/css-images-4/#typedef-conic-gradient-syntax +// [ from <angle> ]? [ at <position> ]? +bool parse_conic_angle_position(const css_token_vector& tokens, int& index, gradient& gradient) +{ + if (at(tokens, index).ident() == "from" && parse_angle(at(tokens, index + 1), gradient.conic_from_angle)) + index += 2; + + int i = index; + if (at(tokens, i).ident() == "at" && parse_gradient_position(tokens, ++i, gradient)) + index = i; + + return true; +} +// [ [ from <angle> ]? [ at <position> ]? ] || <color-interpolation-method> +bool parse_conic_gradient_angle_position_interpolation(const css_token_vector& tokens, gradient& gradient) +{ + if (tokens.empty()) return false; + + auto color_space = color_space_oklab; + auto hue_interpolation = hue_interpolation_shorter; + + int index = 0; + // checking color interpolation first because parse_conic_angle_position always succeeds + if (parse_color_interpolation_method(tokens, index, color_space, hue_interpolation)) + { + parse_conic_angle_position(tokens, index, gradient); + } + else if (parse_conic_angle_position(tokens, index, gradient)) + { + parse_color_interpolation_method(tokens, index, color_space, hue_interpolation); + } + else + return false; + + if (index != (int)tokens.size()) return false; + + gradient.color_space = color_space; + gradient.hue_interpolation = hue_interpolation; + return true; +} + +const float pi = 3.14159265f; + +// https://drafts.csswg.org/css-values-4/#angles +bool parse_angle(const css_token& tok, float& angle, bool percents_allowed) +{ + // The unit identifier may be omitted if the <angle> is zero. https://drafts.csswg.org/css-images-3/#linear-gradient-syntax + if (tok.type == NUMBER && tok.n.number == 0) + { + angle = 0; + return true; + } + + // <angle-percentage> in conic gradient + if (tok.type == PERCENTAGE && percents_allowed) + { + angle = tok.n.number * 360 / 100; + return true; + } + + if (tok.type == DIMENSION) + { + switch (_id(lowcase(tok.unit))) + { + case _deg_: angle = tok.n.number; break; + case _grad_: angle = (tok.n.number / 400) * 360; break; + case _rad_: angle = (tok.n.number / (2 * pi)) * 360; break; + case _turn_: angle = tok.n.number * 360; break; + default: return false; + } + return true; + } + + return false; +} + +// https://www.w3.org/TR/css-color-4/#color-interpolation-method +// <rectangular-color-space> = srgb | srgb-linear | display-p3 | a98-rgb | prophoto-rgb | rec2020 | lab | oklab | xyz | xyz-d50 | xyz-d65 +// <polar-color-space> = hsl | hwb | lch | oklch +// <hue-interpolation-method> = [ shorter | longer | increasing | decreasing ] hue +// <color-interpolation-method> = in [ <rectangular-color-space> | <polar-color-space> <hue-interpolation-method>? ] +bool parse_color_interpolation_method(const css_token_vector& tokens, int& index, + color_space_t& color_space, hue_interpolation_t& hue_interpolation) +{ + if (at(tokens, index).ident() == "in" && + parse_keyword(at(tokens, index + 1), color_space, color_space_strings, 1)) + { + index += 2; + } + else + return false; + + if (color_space >= color_space_polar_start && + at(tokens, index + 1).ident() == "hue" && // must be checked before parse_keyword, otherwise hue_interpolation may be assigned a value when there is no "hue" keyword + parse_keyword(at(tokens, index), hue_interpolation, hue_interpolation_strings, 1)) + { + index += 2; + } + return true; +} + +// https://www.w3.org/TR/css-images-3/#typedef-radial-size +// <radial-size> = <radial-extent> | <length [0,∞]> | <length-percentage [0,∞]>{2} +// <radial-extent> = closest-corner | closest-side | farthest-corner | farthest-side +// Permitted values also depend on <radial-shape>, see parse_radial_gradient_shape_size_position_interpolation. +// TODO: <radial-size> syntax was extended in https://drafts.csswg.org/css-images-4/#radial-size +bool parse_radial_size(const css_token_vector& tokens, int& index, gradient& gradient) +{ + auto& tok0 = at(tokens, index); + auto& tok1 = at(tokens, index + 1); + + if (parse_keyword(tok0, gradient.radial_extent, radial_extent_strings, 1)) + { + index++; + return true; + } + + css_length length[2]; + if (length[0].from_token(tok0, f_length_percentage | f_positive) && + length[1].from_token(tok1, f_length_percentage | f_positive)) + { + gradient.radial_extent = radial_extent_none; + gradient.radial_radius_x = length[0]; + gradient.radial_radius_y = length[1]; + index += 2; + return true; + } + + if (length[0].from_token(tok0, f_length | f_positive)) + { + gradient.radial_extent = radial_extent_none; + gradient.radial_radius_x = length[0]; + index++; + return true; + } + + return false; +} + +bool parse_gradient_position(const css_token_vector& tokens, int& index, gradient& gradient) +{ + css_length x, y; + if (!parse_bg_position(tokens, index, x, y, false)) + return false; + + gradient.m_side = 0; + if (x.is_predefined()) + { + if (x.predef() == background_position_center) + gradient.m_side |= gradient_side_x_center; + else + gradient.m_side |= 1 << x.predef(); + } + else + { + gradient.m_side |= gradient_side_x_length; + gradient.position_x = x; + } + + if (y.is_predefined()) + { + if (y.predef() == background_position_center) + gradient.m_side |= gradient_side_y_center; + else + gradient.m_side |= 1 << y.predef(); + } + else + { + gradient.m_side |= gradient_side_y_length; + gradient.position_y = y; + } + return true; +} + +// https://drafts.csswg.org/css-images-4/#radial-gradients +// [ [ <radial-shape> || <radial-size> ]? [ at <position> ]? ] || <color-interpolation-method> +bool parse_radial_gradient_shape_size_position_interpolation(const css_token_vector& tokens, gradient& result) +{ + // this check is needed because parse may succeed without consuming any input + if (tokens.empty()) return false; + + auto shape = radial_shape_none; + auto radial_shape = [&](const css_token_vector& tokens, int& index) + { + if (!parse_keyword(at(tokens, index), shape, "circle;ellipse", 1)) + return false; + index++; + return true; + }; + + using namespace std::placeholders; + gradient grad; + // sets grad.radial_extent or grad.radial_radius_{x,y} + parse_fn radial_size = std::bind( parse_radial_size, _1, _2, std::ref(grad) ); + // sets grad.m_side and grad.radial_position_{x,y} + parse_fn radial_position = std::bind( parse_gradient_position, _1, _2, std::ref(grad) ); + + auto color_space = color_space_oklab; + auto hue_interpolation = hue_interpolation_shorter; + auto color_interpolation_method = [&](const css_token_vector& tokens, int& index) + { + return parse_color_interpolation_method(tokens, index, color_space, hue_interpolation); + }; + + ///////////////////////////////////////////////////////////////////////////////////////// + + auto parse = oror( + color_interpolation_method, // first trying this because seq(opt,opt) always succeeds + seq(opt(oror(radial_shape, radial_size)), opt(seq("at"_x, radial_position))) + ); + + int index = 0; + bool ok = parse(tokens, index) && end(tokens, index); + if (!ok) return false; + + ///////////////////////////////////////////////////////////////////////////////////////// + + // If <radial-shape> is specified as circle or is omitted, the <radial-size> may be given explicitly as <length [0,∞]> + if (shape == radial_shape_ellipse && + // radius_x is specified, but radius_y is not + !grad.radial_radius_x.is_predefined() && grad.radial_radius_y.is_predefined()) + return false; + + // If <radial-shape> is specified as ellipse or is omitted, <radial-size> may instead be given explicitly as <length-percentage [0,∞]>{2} + if (shape == radial_shape_circle && + // both radius_x and radius_y are specified + !grad.radial_radius_y.is_predefined()) + return false; + + // If <radial-shape> is omitted, the ending shape defaults to a circle if the <radial-size> is a single <length>, and to an ellipse otherwise. + if (shape == radial_shape_none) + { + if (!grad.radial_radius_x.is_predefined() && grad.radial_radius_y.is_predefined()) + shape = radial_shape_circle; + else + shape = radial_shape_ellipse; + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + result.radial_shape = shape; + + result.radial_extent = grad.radial_extent; + result.radial_radius_x = grad.radial_radius_x; + result.radial_radius_y = grad.radial_radius_y; + + result.m_side = grad.m_side; + result.position_x = grad.position_x; + result.position_y = grad.position_y; + + result.color_space = color_space; + result.hue_interpolation = hue_interpolation; + + return true; +} + +} diff --git a/src/gumbo/CMakeLists.txt b/src/gumbo/CMakeLists.txt index 17843c989..9c0dc5b07 100644 --- a/src/gumbo/CMakeLists.txt +++ b/src/gumbo/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.11) project(gumbo C) @@ -46,6 +46,10 @@ set(HEADER_GUMBO set(PROJECT_LIB_VERSION ${PROJECT_MAJOR}.${PROJECT_MINOR}.0) set(PROJECT_SO_VERSION ${PROJECT_MAJOR}) +if (MSVC) + add_compile_options(/wd4244 /wd4267) +endif() + add_library(${PROJECT_NAME} ${SOURCE_GUMBO}) set_target_properties(${PROJECT_NAME} PROPERTIES VERSION ${PROJECT_LIB_VERSION} SOVERSION ${PROJECT_SO_VERSION}) @@ -54,7 +58,7 @@ set_target_properties(${PROJECT_NAME} PROPERTIES PUBLIC_HEADER "${HEADER_GUMBO}" ) -if(MSVC) +if (MSVC) target_include_directories(${PROJECT_NAME} PRIVATE visualc/include) endif() diff --git a/src/gumbo/char_ref.c b/src/gumbo/char_ref.c index b3c5eccaf..406afad03 100644 --- a/src/gumbo/char_ref.c +++ b/src/gumbo/char_ref.c @@ -136,7 +136,7 @@ static bool consume_numeric_ref( return false; } - int codepoint = 0; + unsigned int codepoint = 0; bool status = true; do { codepoint = (codepoint * (is_hex ? 16 : 10)) + digit; @@ -23009,7 +23009,7 @@ static bool consume_named_ref(struct GumboInternalParser* parser, if (cs >= 7623) { assert(output->first != kGumboNoChar); char last_char = *(te - 1); - ptrdiff_t len = te - start; + int len = te - start; if (last_char == ';') { bool matched = utf8iterator_maybe_consume_match(input, start, len, true); assert(matched); diff --git a/src/gumbo/char_ref.rl b/src/gumbo/char_ref.rl index 7c1c410d9..464e1690b 100644 --- a/src/gumbo/char_ref.rl +++ b/src/gumbo/char_ref.rl @@ -162,7 +162,7 @@ static bool consume_numeric_ref( return false; } - int codepoint = 0; + unsigned int codepoint = 0; bool status = true; do { codepoint = (codepoint * (is_hex ? 16 : 10)) + digit; @@ -2493,7 +2493,7 @@ static bool consume_named_ref( if (cs >= %%{ write first_final; }%%) { assert(output->first != kGumboNoChar); char last_char = *(te - 1); - ptrdiff_t len = te - start; + int len = te - start; if (last_char == ';') { bool matched = utf8iterator_maybe_consume_match(input, start, len, true); assert(matched); diff --git a/src/gumbo/error.c b/src/gumbo/error.c index 369e7c11e..057e89ce2 100644 --- a/src/gumbo/error.c +++ b/src/gumbo/error.c @@ -33,7 +33,7 @@ static int print_message( GumboParser* parser, GumboStringBuffer* output, const char* format, ...) { va_list args; - size_t remaining_capacity = output->capacity - output->length; + int remaining_capacity = output->capacity - output->length; va_start(args, format); int bytes_written = vsnprintf( output->data + output->length, remaining_capacity, format, args); @@ -59,7 +59,7 @@ static int print_message( } #endif - if (bytes_written > remaining_capacity) { + if (bytes_written >= remaining_capacity) { gumbo_string_buffer_reserve( parser, output->capacity + bytes_written, output); remaining_capacity = output->capacity - output->length; @@ -79,7 +79,7 @@ static void print_tag_stack(GumboParser* parser, const GumboParserError* error, if (i) { print_message(parser, output, ", "); } - GumboTag tag = (GumboTag)(uintptr_t) error->tag_stack.data[i]; + GumboTag tag = (GumboTag) error->tag_stack.data[i]; print_message(parser, output, gumbo_normalized_tagname(tag)); } gumbo_string_buffer_append_codepoint(parser, '.', output); @@ -136,6 +136,13 @@ static const char* find_last_newline( const char* original_text, const char* error_location) { assert(error_location >= original_text); const char* c = error_location; + // If the error location itself is a newline then start searching for the + // preceding newline one character earlier, if possible. See: + // https://github.com/rubys/nokogumbo/commit/bd623555730cdd260f6cec6d7cf990ff297da63d + // https://github.com/google/gumbo-parser/pull/371 + if (*c == '\n' && c != original_text) { + c -= 1; + } for (; c != original_text && *c != '\n'; --c) { // There may be an error at EOF, which would be a nul byte. assert(*c || c == error_location); diff --git a/src/gumbo/include/gumbo.h b/src/gumbo/include/gumbo.h index 27e6c6c57..83cd22d5d 100644 --- a/src/gumbo/include/gumbo.h +++ b/src/gumbo/include/gumbo.h @@ -43,13 +43,9 @@ #define GUMBO_GUMBO_H_ #ifdef _MSC_VER -#ifndef _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS -#endif -#ifndef fileno #define fileno _fileno #endif -#endif #include <stdbool.h> #include <stddef.h> @@ -393,7 +389,10 @@ typedef enum { */ GUMBO_INSERTION_CONVERTED_FROM_END_TAG = 1 << 4, - /** A flag for nodes that are converted from the parse of an <isindex> tag. */ + /** + * Deprecated! Do not use! + * It's not used anymore since special handling for <isindex> is obsolete. + */ GUMBO_INSERTION_FROM_ISINDEX = 1 << 5, /** A flag for <image> tags that are rewritten as <img>. */ diff --git a/src/gumbo/include/gumbo/error.h b/src/gumbo/include/gumbo/error.h index 3aa54a6b2..afc998b1f 100644 --- a/src/gumbo/include/gumbo/error.h +++ b/src/gumbo/include/gumbo/error.h @@ -19,10 +19,8 @@ #ifndef GUMBO_ERROR_H_ #define GUMBO_ERROR_H_ #ifdef _MSC_VER -#ifndef _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS #endif -#endif #include <stdint.h> #include "gumbo.h" @@ -201,7 +199,7 @@ void gumbo_error_destroy(struct GumboInternalParser* parser, GumboError* error); // Prints an error to a string. This fills an empty GumboStringBuffer with a // freshly-allocated buffer containing the error message text. The caller is // responsible for deleting the buffer. (Note that the buffer is allocated with -// the allocator specified in the GumboParser ~config and hence should be freed +// the allocator specified in the GumboParser config and hence should be freed // by gumbo_parser_deallocate().) void gumbo_error_to_string(struct GumboInternalParser* parser, const GumboError* error, GumboStringBuffer* output); @@ -209,7 +207,7 @@ void gumbo_error_to_string(struct GumboInternalParser* parser, // Prints a caret diagnostic to a string. This fills an empty GumboStringBuffer // with a freshly-allocated buffer containing the error message text. The // caller is responsible for deleting the buffer. (Note that the buffer is -// allocated with the allocator specified in the GumboParser ~config and hence +// allocated with the allocator specified in the GumboParser config and hence // should be freed by gumbo_parser_deallocate().) void gumbo_caret_diagnostic_to_string(struct GumboInternalParser* parser, const GumboError* error, const char* source_text, diff --git a/src/gumbo/include/gumbo/tag_enum.h b/src/gumbo/include/gumbo/tag_enum.h index 6d7aeb3d7..4237b1dec 100644 --- a/src/gumbo/include/gumbo/tag_enum.h +++ b/src/gumbo/include/gumbo/tag_enum.h @@ -73,6 +73,7 @@ GUMBO_TAG_INS, GUMBO_TAG_DEL, GUMBO_TAG_IMAGE, GUMBO_TAG_IMG, +GUMBO_TAG_PICTURE, GUMBO_TAG_IFRAME, GUMBO_TAG_EMBED, GUMBO_TAG_OBJECT, @@ -149,5 +150,7 @@ GUMBO_TAG_MARQUEE, GUMBO_TAG_MULTICOL, GUMBO_TAG_NOBR, GUMBO_TAG_SPACER, +GUMBO_TAG_DIALOG, +GUMBO_TAG_SEARCH, GUMBO_TAG_TT, GUMBO_TAG_RTC, diff --git a/src/gumbo/include/gumbo/tag_gperf.h b/src/gumbo/include/gumbo/tag_gperf.h index 378eaf958..10691e473 100644 --- a/src/gumbo/include/gumbo/tag_gperf.h +++ b/src/gumbo/include/gumbo/tag_gperf.h @@ -1,105 +1,331 @@ -static unsigned int tag_hash( - register const char *str, register unsigned int len) { - static unsigned short asso_values[] = {296, 296, 296, 296, 296, 296, 296, 296, - 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, - 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, - 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 6, 4, 3, 1, 1, 0, - 1, 0, 0, 296, 296, 296, 296, 296, 296, 296, 22, 73, 151, 4, 13, 59, 65, 2, - 69, 0, 134, 9, 16, 52, 55, 28, 101, 0, 1, 6, 63, 126, 104, 93, 124, 296, - 296, 296, 296, 296, 296, 296, 22, 73, 151, 4, 13, 59, 65, 2, 69, 0, 134, - 9, 16, 52, 55, 28, 101, 0, 1, 6, 63, 126, 104, 93, 124, 296, 296, 296, - 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, - 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, - 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, - 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, - 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, - 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, - 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, - 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, - 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296}; +static unsigned int tag_hash(register const char *str, register size_t len) +{ + static unsigned short asso_values[] = + { + 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, + 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, + 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, + 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, + 284, 284, 284, 284, 284, 284, 284, 284, 284, 9, + 5, 4, 2, 2, 1, 2, 1, 1, 284, 284, + 284, 284, 284, 284, 284, 56, 62, 156, 5, 14, + 47, 102, 3, 78, 32, 136, 10, 28, 36, 61, + 22, 108, 1, 2, 7, 55, 80, 123, 115, 76, + 284, 284, 284, 284, 284, 284, 284, 56, 62, 156, + 5, 14, 47, 102, 3, 78, 32, 136, 10, 28, + 36, 61, 22, 108, 1, 2, 7, 55, 80, 123, + 115, 76, 284, 284, 284, 284, 284, 284, 284, 284, + 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, + 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, + 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, + 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, + 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, + 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, + 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, + 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, + 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, + 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, + 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, + 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, + 284, 284, 284, 284, 284, 284, 284, 284, 284 + }; register unsigned int hval = len; - switch (hval) { - default: - hval += asso_values[(unsigned char) str[1] + 3]; - /*FALLTHROUGH*/ - case 1: - hval += asso_values[(unsigned char) str[0]]; - break; - } - return hval + asso_values[(unsigned char) str[len - 1]]; + switch (hval) + { + default: + hval += asso_values[(unsigned char)str[1]+3]; + /*FALLTHROUGH*/ + case 1: + hval += asso_values[(unsigned char)str[0]]; + break; + } + return hval + asso_values[(unsigned char)str[len - 1]]; } -static const unsigned char kGumboTagMap[] = {GUMBO_TAG_LAST, GUMBO_TAG_LAST, - GUMBO_TAG_LAST, GUMBO_TAG_S, GUMBO_TAG_H6, GUMBO_TAG_H5, GUMBO_TAG_H4, - GUMBO_TAG_H3, GUMBO_TAG_SPACER, GUMBO_TAG_H2, GUMBO_TAG_HEADER, - GUMBO_TAG_H1, GUMBO_TAG_HEAD, GUMBO_TAG_LAST, GUMBO_TAG_DETAILS, - GUMBO_TAG_SELECT, GUMBO_TAG_DIR, GUMBO_TAG_LAST, GUMBO_TAG_DEL, - GUMBO_TAG_LAST, GUMBO_TAG_SOURCE, GUMBO_TAG_LEGEND, GUMBO_TAG_DATALIST, - GUMBO_TAG_METER, GUMBO_TAG_MGLYPH, GUMBO_TAG_LAST, GUMBO_TAG_MATH, - GUMBO_TAG_LABEL, GUMBO_TAG_TABLE, GUMBO_TAG_TEMPLATE, GUMBO_TAG_LAST, - GUMBO_TAG_RP, GUMBO_TAG_TIME, GUMBO_TAG_TITLE, GUMBO_TAG_DATA, - GUMBO_TAG_APPLET, GUMBO_TAG_HGROUP, GUMBO_TAG_SAMP, GUMBO_TAG_TEXTAREA, - GUMBO_TAG_ABBR, GUMBO_TAG_MARQUEE, GUMBO_TAG_LAST, GUMBO_TAG_MENUITEM, - GUMBO_TAG_SMALL, GUMBO_TAG_META, GUMBO_TAG_A, GUMBO_TAG_LAST, - GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_EMBED, - GUMBO_TAG_MAP, GUMBO_TAG_LAST, GUMBO_TAG_PARAM, GUMBO_TAG_LAST, - GUMBO_TAG_LAST, GUMBO_TAG_NOBR, GUMBO_TAG_P, GUMBO_TAG_SPAN, GUMBO_TAG_EM, - GUMBO_TAG_LAST, GUMBO_TAG_NOFRAMES, GUMBO_TAG_SECTION, GUMBO_TAG_NOEMBED, - GUMBO_TAG_NEXTID, GUMBO_TAG_FOOTER, GUMBO_TAG_NOSCRIPT, GUMBO_TAG_HR, - GUMBO_TAG_LAST, GUMBO_TAG_FONT, GUMBO_TAG_DL, GUMBO_TAG_TR, - GUMBO_TAG_SCRIPT, GUMBO_TAG_MO, GUMBO_TAG_LAST, GUMBO_TAG_DD, - GUMBO_TAG_MAIN, GUMBO_TAG_TD, GUMBO_TAG_FOREIGNOBJECT, GUMBO_TAG_FORM, - GUMBO_TAG_OBJECT, GUMBO_TAG_LAST, GUMBO_TAG_FIELDSET, GUMBO_TAG_LAST, - GUMBO_TAG_BGSOUND, GUMBO_TAG_MENU, GUMBO_TAG_TFOOT, GUMBO_TAG_FIGURE, - GUMBO_TAG_RB, GUMBO_TAG_LI, GUMBO_TAG_LISTING, GUMBO_TAG_BASEFONT, - GUMBO_TAG_OPTGROUP, GUMBO_TAG_LAST, GUMBO_TAG_BASE, GUMBO_TAG_ADDRESS, - GUMBO_TAG_MI, GUMBO_TAG_LAST, GUMBO_TAG_PLAINTEXT, GUMBO_TAG_LAST, - GUMBO_TAG_PROGRESS, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, - GUMBO_TAG_ACRONYM, GUMBO_TAG_ARTICLE, GUMBO_TAG_LAST, GUMBO_TAG_PRE, - GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_AREA, - GUMBO_TAG_RT, GUMBO_TAG_LAST, GUMBO_TAG_OPTION, GUMBO_TAG_IMAGE, - GUMBO_TAG_DT, GUMBO_TAG_LAST, GUMBO_TAG_TT, GUMBO_TAG_HTML, GUMBO_TAG_WBR, - GUMBO_TAG_OL, GUMBO_TAG_LAST, GUMBO_TAG_STYLE, GUMBO_TAG_STRIKE, - GUMBO_TAG_SUP, GUMBO_TAG_MULTICOL, GUMBO_TAG_U, GUMBO_TAG_DFN, GUMBO_TAG_UL, - GUMBO_TAG_FIGCAPTION, GUMBO_TAG_MTEXT, GUMBO_TAG_LAST, GUMBO_TAG_VAR, - GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_FRAMESET, GUMBO_TAG_LAST, - GUMBO_TAG_BR, GUMBO_TAG_I, GUMBO_TAG_FRAME, GUMBO_TAG_LAST, GUMBO_TAG_DIV, - GUMBO_TAG_LAST, GUMBO_TAG_TH, GUMBO_TAG_MS, GUMBO_TAG_ANNOTATION_XML, - GUMBO_TAG_B, GUMBO_TAG_TBODY, GUMBO_TAG_THEAD, GUMBO_TAG_BIG, - GUMBO_TAG_BLOCKQUOTE, GUMBO_TAG_XMP, GUMBO_TAG_LAST, GUMBO_TAG_KBD, - GUMBO_TAG_LAST, GUMBO_TAG_LINK, GUMBO_TAG_IFRAME, GUMBO_TAG_MARK, - GUMBO_TAG_CENTER, GUMBO_TAG_OUTPUT, GUMBO_TAG_DESC, GUMBO_TAG_CANVAS, - GUMBO_TAG_COL, GUMBO_TAG_MALIGNMARK, GUMBO_TAG_IMG, GUMBO_TAG_ASIDE, - GUMBO_TAG_LAST, GUMBO_TAG_CODE, GUMBO_TAG_LAST, GUMBO_TAG_SUB, GUMBO_TAG_MN, - GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_INS, GUMBO_TAG_AUDIO, - GUMBO_TAG_STRONG, GUMBO_TAG_CITE, GUMBO_TAG_LAST, GUMBO_TAG_LAST, - GUMBO_TAG_LAST, GUMBO_TAG_INPUT, GUMBO_TAG_LAST, GUMBO_TAG_LAST, - GUMBO_TAG_LAST, GUMBO_TAG_NAV, GUMBO_TAG_LAST, GUMBO_TAG_COLGROUP, - GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, - GUMBO_TAG_LAST, GUMBO_TAG_SVG, GUMBO_TAG_KEYGEN, GUMBO_TAG_VIDEO, - GUMBO_TAG_BDO, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, - GUMBO_TAG_LAST, GUMBO_TAG_BODY, GUMBO_TAG_LAST, GUMBO_TAG_Q, GUMBO_TAG_LAST, - GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_TRACK, - GUMBO_TAG_LAST, GUMBO_TAG_BDI, GUMBO_TAG_LAST, GUMBO_TAG_LAST, - GUMBO_TAG_LAST, GUMBO_TAG_CAPTION, GUMBO_TAG_LAST, GUMBO_TAG_LAST, - GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, - GUMBO_TAG_RUBY, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_BUTTON, - GUMBO_TAG_SUMMARY, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, - GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, - GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, - GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, - GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, - GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, - GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, - GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, - GUMBO_TAG_LAST, GUMBO_TAG_RTC, GUMBO_TAG_LAST, GUMBO_TAG_LAST, - GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, - GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_BLINK, GUMBO_TAG_LAST, - GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, - GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, - GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, - GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, - GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, - GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_LAST, - GUMBO_TAG_LAST, GUMBO_TAG_LAST, GUMBO_TAG_ISINDEX}; +static const unsigned char kGumboTagMap[] = { + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_S, + GUMBO_TAG_LAST, + GUMBO_TAG_H6, + GUMBO_TAG_H5, + GUMBO_TAG_H4, + GUMBO_TAG_H3, + GUMBO_TAG_SPACER, + GUMBO_TAG_H2, + GUMBO_TAG_HEADER, + GUMBO_TAG_SEARCH, + GUMBO_TAG_HEAD, + GUMBO_TAG_H1, + GUMBO_TAG_DETAILS, + GUMBO_TAG_SELECT, + GUMBO_TAG_DIR, + GUMBO_TAG_LAST, + GUMBO_TAG_DEL, + GUMBO_TAG_LAST, + GUMBO_TAG_SOURCE, + GUMBO_TAG_LEGEND, + GUMBO_TAG_DATALIST, + GUMBO_TAG_LAST, + GUMBO_TAG_RP, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LABEL, + GUMBO_TAG_TABLE, + GUMBO_TAG_TEMPLATE, + GUMBO_TAG_SAMP, + GUMBO_TAG_LAST, + GUMBO_TAG_TIME, + GUMBO_TAG_TITLE, + GUMBO_TAG_METER, + GUMBO_TAG_LAST, + GUMBO_TAG_SMALL, + GUMBO_TAG_MATH, + GUMBO_TAG_LAST, + GUMBO_TAG_NOBR, + GUMBO_TAG_LAST, + GUMBO_TAG_SPAN, + GUMBO_TAG_P, + GUMBO_TAG_EMBED, + GUMBO_TAG_NOFRAMES, + GUMBO_TAG_SECTION, + GUMBO_TAG_NOEMBED, + GUMBO_TAG_NEXTID, + GUMBO_TAG_LAST, + GUMBO_TAG_NOSCRIPT, + GUMBO_TAG_PICTURE, + GUMBO_TAG_MARQUEE, + GUMBO_TAG_FOOTER, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_MAP, + GUMBO_TAG_FONT, + GUMBO_TAG_PARAM, + GUMBO_TAG_HR, + GUMBO_TAG_SCRIPT, + GUMBO_TAG_HGROUP, + GUMBO_TAG_LAST, + GUMBO_TAG_TR, + GUMBO_TAG_EM, + GUMBO_TAG_MENUITEM, + GUMBO_TAG_FOREIGNOBJECT, + GUMBO_TAG_MGLYPH, + GUMBO_TAG_DATA, + GUMBO_TAG_APPLET, + GUMBO_TAG_FIELDSET, + GUMBO_TAG_MAIN, + GUMBO_TAG_TEXTAREA, + GUMBO_TAG_ABBR, + GUMBO_TAG_LAST, + GUMBO_TAG_FIGURE, + GUMBO_TAG_DL, + GUMBO_TAG_RB, + GUMBO_TAG_FORM, + GUMBO_TAG_LAST, + GUMBO_TAG_BASEFONT, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_BASE, + GUMBO_TAG_LAST, + GUMBO_TAG_PROGRESS, + GUMBO_TAG_OBJECT, + GUMBO_TAG_VAR, + GUMBO_TAG_MENU, + GUMBO_TAG_META, + GUMBO_TAG_MO, + GUMBO_TAG_OPTGROUP, + GUMBO_TAG_PRE, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_TFOOT, + GUMBO_TAG_DIV, + GUMBO_TAG_PLAINTEXT, + GUMBO_TAG_LI, + GUMBO_TAG_LAST, + GUMBO_TAG_TBODY, + GUMBO_TAG_FIGCAPTION, + GUMBO_TAG_LAST, + GUMBO_TAG_OPTION, + GUMBO_TAG_BGSOUND, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_U, + GUMBO_TAG_MS, + GUMBO_TAG_A, + GUMBO_TAG_DD, + GUMBO_TAG_LAST, + GUMBO_TAG_TD, + GUMBO_TAG_FRAMESET, + GUMBO_TAG_MI, + GUMBO_TAG_IMAGE, + GUMBO_TAG_BR, + GUMBO_TAG_FRAME, + GUMBO_TAG_DFN, + GUMBO_TAG_DIALOG, + GUMBO_TAG_NAV, + GUMBO_TAG_B, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_UL, + GUMBO_TAG_LISTING, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_ARTICLE, + GUMBO_TAG_RT, + GUMBO_TAG_OL, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_DT, + GUMBO_TAG_ACRONYM, + GUMBO_TAG_TT, + GUMBO_TAG_HTML, + GUMBO_TAG_WBR, + GUMBO_TAG_SUP, + GUMBO_TAG_BODY, + GUMBO_TAG_STYLE, + GUMBO_TAG_STRIKE, + GUMBO_TAG_LAST, + GUMBO_TAG_BLOCKQUOTE, + GUMBO_TAG_TH, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_THEAD, + GUMBO_TAG_LAST, + GUMBO_TAG_ASIDE, + GUMBO_TAG_VIDEO, + GUMBO_TAG_I, + GUMBO_TAG_KBD, + GUMBO_TAG_LAST, + GUMBO_TAG_LINK, + GUMBO_TAG_MULTICOL, + GUMBO_TAG_XMP, + GUMBO_TAG_MTEXT, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_CENTER, + GUMBO_TAG_ADDRESS, + GUMBO_TAG_DESC, + GUMBO_TAG_CANVAS, + GUMBO_TAG_COL, + GUMBO_TAG_AREA, + GUMBO_TAG_LAST, + GUMBO_TAG_MARK, + GUMBO_TAG_MN, + GUMBO_TAG_CODE, + GUMBO_TAG_IFRAME, + GUMBO_TAG_BIG, + GUMBO_TAG_LAST, + GUMBO_TAG_MALIGNMARK, + GUMBO_TAG_LAST, + GUMBO_TAG_KEYGEN, + GUMBO_TAG_SUB, + GUMBO_TAG_SVG, + GUMBO_TAG_CITE, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_COLGROUP, + GUMBO_TAG_ANNOTATION_XML, + GUMBO_TAG_OUTPUT, + GUMBO_TAG_LAST, + GUMBO_TAG_INS, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_RUBY, + GUMBO_TAG_LAST, + GUMBO_TAG_INPUT, + GUMBO_TAG_LAST, + GUMBO_TAG_SUMMARY, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_TRACK, + GUMBO_TAG_CAPTION, + GUMBO_TAG_IMG, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_Q, + GUMBO_TAG_LAST, + GUMBO_TAG_BUTTON, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_BDO, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_STRONG, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_AUDIO, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_BDI, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_BLINK, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_ISINDEX, + GUMBO_TAG_LAST, + GUMBO_TAG_LAST, + GUMBO_TAG_RTC +}; diff --git a/src/gumbo/include/gumbo/tag_sizes.h b/src/gumbo/include/gumbo/tag_sizes.h index 7c92de073..9dc952999 100644 --- a/src/gumbo/include/gumbo/tag_sizes.h +++ b/src/gumbo/include/gumbo/tag_sizes.h @@ -1,4 +1,4 @@ // Generated via `gentags.py src/tag.in`. // Do not edit; edit src/tag.in instead. // clang-format off -4, 4, 5, 4, 4, 4, 5, 6, 8, 8, 4, 7, 7, 3, 5, 2, 2, 2, 2, 2, 2, 6, 6, 6, 7, 1, 2, 3, 10, 2, 2, 2, 2, 2, 2, 6, 10, 4, 3, 1, 2, 6, 5, 1, 4, 1, 3, 4, 4, 4, 4, 3, 4, 3, 3, 3, 1, 1, 1, 4, 4, 2, 2, 3, 3, 4, 2, 3, 3, 3, 5, 3, 6, 5, 6, 5, 5, 5, 6, 5, 6, 3, 4, 4, 2, 2, 2, 2, 5, 6, 10, 14, 3, 13, 4, 5, 7, 8, 3, 5, 5, 5, 2, 2, 2, 4, 8, 6, 5, 5, 6, 6, 8, 8, 6, 8, 6, 6, 8, 5, 7, 7, 4, 8, 6, 7, 7, 3, 5, 8, 8, 7, 7, 3, 6, 7, 9, 2, 6, 8, 3, 5, 6, 4, 7, 8, 4, 6, 2, 3, \ No newline at end of file +4, 4, 5, 4, 4, 4, 5, 6, 8, 8, 4, 7, 7, 3, 5, 2, 2, 2, 2, 2, 2, 6, 6, 6, 7, 1, 2, 3, 10, 2, 2, 2, 2, 2, 2, 6, 10, 4, 3, 1, 2, 6, 5, 1, 4, 1, 3, 4, 4, 4, 4, 3, 4, 3, 3, 3, 1, 1, 1, 4, 4, 2, 2, 3, 3, 4, 2, 3, 3, 3, 5, 3, 7, 6, 5, 6, 5, 5, 5, 6, 5, 6, 3, 4, 4, 2, 2, 2, 2, 5, 6, 10, 14, 3, 13, 4, 5, 7, 8, 3, 5, 5, 5, 2, 2, 2, 4, 8, 6, 5, 5, 6, 6, 8, 8, 6, 8, 6, 6, 8, 5, 7, 7, 4, 8, 6, 7, 7, 3, 5, 8, 8, 7, 7, 3, 6, 7, 9, 2, 6, 8, 3, 5, 6, 4, 7, 8, 4, 6, 6, 6, 2, 3, \ No newline at end of file diff --git a/src/gumbo/include/gumbo/tag_strings.h b/src/gumbo/include/gumbo/tag_strings.h index 6540e2e6b..188bcda00 100644 --- a/src/gumbo/include/gumbo/tag_strings.h +++ b/src/gumbo/include/gumbo/tag_strings.h @@ -73,6 +73,7 @@ "del", "image", "img", +"picture", "iframe", "embed", "object", @@ -149,5 +150,7 @@ "multicol", "nobr", "spacer", +"dialog", +"search", "tt", "rtc", diff --git a/src/gumbo/include/gumbo/utf8.h b/src/gumbo/include/gumbo/utf8.h index ee852abfb..bd31a781e 100644 --- a/src/gumbo/include/gumbo/utf8.h +++ b/src/gumbo/include/gumbo/utf8.h @@ -62,7 +62,7 @@ typedef struct GumboInternalUtf8Iterator { int _current; // The width in bytes of the current code point. - ptrdiff_t _width; + int _width; // The SourcePosition for the current location. GumboSourcePosition _pos; diff --git a/src/gumbo/include/gumbo/util.h b/src/gumbo/include/gumbo/util.h index 98a7d1c46..6ad65649e 100644 --- a/src/gumbo/include/gumbo/util.h +++ b/src/gumbo/include/gumbo/util.h @@ -20,10 +20,8 @@ #ifndef GUMBO_UTIL_H_ #define GUMBO_UTIL_H_ #ifdef _MSC_VER -#ifndef _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS #endif -#endif #include <stdbool.h> #include <stddef.h> @@ -43,12 +41,12 @@ struct GumboInternalParser; char* gumbo_copy_stringz(struct GumboInternalParser* parser, const char* str); // Allocate a chunk of memory, using the allocator specified in the Parser's -// ~config options. +// config options. void* gumbo_parser_allocate( struct GumboInternalParser* parser, size_t num_bytes); // Deallocate a chunk of memory, using the deallocator specified in the Parser's -// ~config options. +// config options. void gumbo_parser_deallocate(struct GumboInternalParser* parser, void* ptr); // Debug wrapper for printf, to make it easier to turn off debugging info when diff --git a/src/gumbo/parser.c b/src/gumbo/parser.c index 653fd85ac..0ab3f9208 100644 --- a/src/gumbo/parser.c +++ b/src/gumbo/parser.c @@ -45,7 +45,7 @@ typedef char gumbo_tagset[GUMBO_TAG_LAST]; #define TAG_MATHML(tag) [GUMBO_TAG_##tag] = (1 << GUMBO_NAMESPACE_MATHML) #define TAGSET_INCLUDES(tagset, namespace, tag) \ - (tag < GUMBO_TAG_LAST && tagset[(int) tag] == (1 << (int) namespace)) + (tag < GUMBO_TAG_LAST && tagset[(int) tag] & (1 << (int) namespace)) // selected forward declarations as it is getting hard to find // an appropriate order @@ -291,17 +291,16 @@ typedef struct _NamespacedAttributeReplacement { static const NamespacedAttributeReplacement kForeignAttributeReplacements[] = { {"xlink:actuate", "actuate", GUMBO_ATTR_NAMESPACE_XLINK}, - {"xlink:actuate", "actuate", GUMBO_ATTR_NAMESPACE_XLINK}, - {"xlink:href", "href", GUMBO_ATTR_NAMESPACE_XLINK}, - {"xlink:role", "role", GUMBO_ATTR_NAMESPACE_XLINK}, - {"xlink:show", "show", GUMBO_ATTR_NAMESPACE_XLINK}, - {"xlink:title", "title", GUMBO_ATTR_NAMESPACE_XLINK}, - {"xlink:type", "type", GUMBO_ATTR_NAMESPACE_XLINK}, - {"xml:base", "base", GUMBO_ATTR_NAMESPACE_XML}, - {"xml:lang", "lang", GUMBO_ATTR_NAMESPACE_XML}, - {"xml:space", "space", GUMBO_ATTR_NAMESPACE_XML}, - {"xmlns", "xmlns", GUMBO_ATTR_NAMESPACE_XMLNS}, - {"xmlns:xlink", "xlink", GUMBO_ATTR_NAMESPACE_XMLNS}, + {"xlink:arcrole", "arcrole", GUMBO_ATTR_NAMESPACE_XLINK}, + {"xlink:href", "href", GUMBO_ATTR_NAMESPACE_XLINK}, + {"xlink:role", "role", GUMBO_ATTR_NAMESPACE_XLINK}, + {"xlink:show", "show", GUMBO_ATTR_NAMESPACE_XLINK}, + {"xlink:title", "title", GUMBO_ATTR_NAMESPACE_XLINK}, + {"xlink:type", "type", GUMBO_ATTR_NAMESPACE_XLINK}, + {"xml:lang", "lang", GUMBO_ATTR_NAMESPACE_XML}, + {"xml:space", "space", GUMBO_ATTR_NAMESPACE_XML}, + {"xmlns", "xmlns", GUMBO_ATTR_NAMESPACE_XMLNS}, + {"xmlns:xlink", "xlink", GUMBO_ATTR_NAMESPACE_XMLNS}, }; // The "scope marker" for the list of active formatting elements. We use a @@ -572,6 +571,10 @@ static GumboInsertionMode get_appropriate_insertion_mode( } assert(node->type == GUMBO_NODE_ELEMENT || node->type == GUMBO_NODE_TEMPLATE); + if (node->v.element.tag_namespace != GUMBO_NAMESPACE_HTML) + return is_last ? + GUMBO_INSERTION_MODE_IN_BODY : GUMBO_INSERTION_MODE_INITIAL; + switch (node->v.element.tag) { case GUMBO_TAG_SELECT: { if (is_last) { @@ -812,7 +815,7 @@ InsertionLocation get_appropriate_insertion_location( GumboNode* last_table = open_elements->data[last_table_index]; if (last_table->parent != NULL) { retval.target = last_table->parent; - retval.index = (int)last_table->index_within_parent; + retval.index = last_table->index_within_parent; return retval; } @@ -1560,12 +1563,12 @@ static bool is_special_node(const GumboNode* node) { (gumbo_tagset){TAG(ADDRESS), TAG(APPLET), TAG(AREA), TAG(ARTICLE), TAG(ASIDE), TAG(BASE), TAG(BASEFONT), TAG(BGSOUND), TAG(BLOCKQUOTE), TAG(BODY), TAG(BR), TAG(BUTTON), TAG(CAPTION), TAG(CENTER), TAG(COL), - TAG(COLGROUP), TAG(MENUITEM), TAG(DD), TAG(DETAILS), TAG(DIR), + TAG(COLGROUP), TAG(DD), TAG(DETAILS), TAG(DIR), TAG(DIV), TAG(DL), TAG(DT), TAG(EMBED), TAG(FIELDSET), TAG(FIGCAPTION), TAG(FIGURE), TAG(FOOTER), TAG(FORM), TAG(FRAME), TAG(FRAMESET), TAG(H1), TAG(H2), TAG(H3), TAG(H4), TAG(H5), TAG(H6), TAG(HEAD), TAG(HEADER), TAG(HGROUP), TAG(HR), TAG(HTML), TAG(IFRAME), - TAG(IMG), TAG(INPUT), TAG(ISINDEX), TAG(LI), TAG(LINK), TAG(LISTING), + TAG(IMG), TAG(INPUT), TAG(LI), TAG(LINK), TAG(LISTING), TAG(MARQUEE), TAG(MENU), TAG(META), TAG(NAV), TAG(NOEMBED), TAG(NOFRAMES), TAG(NOSCRIPT), TAG(OBJECT), TAG(OL), TAG(P), TAG(PARAM), TAG(PLAINTEXT), TAG(PRE), TAG(SCRIPT), TAG(SECTION), @@ -2175,7 +2178,7 @@ static bool handle_in_head(GumboParser* parser, GumboToken* token) { return handle_in_body(parser, token); } else if (tag_in(token, kStartTag, (gumbo_tagset){TAG(BASE), TAG(BASEFONT), TAG(BGSOUND), - TAG(MENUITEM), TAG(LINK)})) { + TAG(LINK)})) { insert_element_from_token(parser, token); pop_current_node(parser); acknowledge_self_closing_tag(parser); @@ -2415,7 +2418,7 @@ static bool handle_in_body(GumboParser* parser, GumboToken* token) { return false; } else if (tag_in(token, kStartTag, (gumbo_tagset){TAG(BASE), TAG(BASEFONT), TAG(BGSOUND), - TAG(MENUITEM), TAG(LINK), TAG(META), TAG(NOFRAMES), + TAG(LINK), TAG(META), TAG(NOFRAMES), TAG(SCRIPT), TAG(STYLE), TAG(TEMPLATE), TAG(TITLE)}) || tag_is(token, kEndTag, GUMBO_TAG_TEMPLATE)) { return handle_in_head(parser, token); @@ -2510,13 +2513,13 @@ static bool handle_in_body(GumboParser* parser, GumboToken* token) { record_end_of_element(state->_current_token, &body->v.element); } return success; - } else if (tag_in(token, kStartTag, - (gumbo_tagset){TAG(ADDRESS), TAG(ARTICLE), TAG(ASIDE), - TAG(BLOCKQUOTE), TAG(CENTER), TAG(DETAILS), TAG(DIR), - TAG(DIV), TAG(DL), TAG(FIELDSET), TAG(FIGCAPTION), - TAG(FIGURE), TAG(FOOTER), TAG(HEADER), TAG(HGROUP), - TAG(MENU), TAG(MAIN), TAG(NAV), TAG(OL), TAG(P), - TAG(SECTION), TAG(SUMMARY), TAG(UL)})) { + } else if (tag_in(token, kStartTag, (gumbo_tagset){ + TAG(ADDRESS), TAG(ARTICLE), TAG(ASIDE), TAG(BLOCKQUOTE), TAG(CENTER), + TAG(DETAILS), TAG(DIALOG), TAG(DIR), TAG(DIV), TAG(DL), TAG(FIELDSET), + TAG(FIGCAPTION), TAG(FIGURE), TAG(FOOTER), TAG(HEADER), TAG(HGROUP), + TAG(MENU), TAG(MAIN), TAG(NAV), TAG(OL), TAG(P), TAG(SECTION), + TAG(SUMMARY), TAG(UL), TAG(SEARCH)})) + { bool result = maybe_implicitly_close_p_tag(parser, token); insert_element_from_token(parser, token); return result; @@ -2579,13 +2582,13 @@ static bool handle_in_body(GumboParser* parser, GumboToken* token) { insert_element_from_token(parser, token); state->_frameset_ok = false; return true; - } else if (tag_in(token, kEndTag, - (gumbo_tagset){TAG(ADDRESS), TAG(ARTICLE), TAG(ASIDE), - TAG(BLOCKQUOTE), TAG(BUTTON), TAG(CENTER), TAG(DETAILS), - TAG(DIR), TAG(DIV), TAG(DL), TAG(FIELDSET), - TAG(FIGCAPTION), TAG(FIGURE), TAG(FOOTER), TAG(HEADER), - TAG(HGROUP), TAG(LISTING), TAG(MAIN), TAG(MENU), TAG(NAV), - TAG(OL), TAG(PRE), TAG(SECTION), TAG(SUMMARY), TAG(UL)})) { + } else if (tag_in(token, kEndTag, (gumbo_tagset){ + TAG(ADDRESS), TAG(ARTICLE), TAG(ASIDE), TAG(BLOCKQUOTE), TAG(BUTTON), + TAG(CENTER), TAG(DETAILS), TAG(DIALOG), TAG(DIR), TAG(DIV), TAG(DL), + TAG(FIELDSET), TAG(FIGCAPTION), TAG(FIGURE), TAG(FOOTER), TAG(HEADER), + TAG(HGROUP), TAG(LISTING), TAG(MAIN), TAG(MENU), TAG(NAV), TAG(OL), + TAG(PRE), TAG(SECTION), TAG(SUMMARY), TAG(UL), TAG(SEARCH)})) + { GumboTag tag = token->v.end_tag; if (!has_an_element_in_scope(parser, tag)) { parser_add_parse_error(parser, token); @@ -2613,7 +2616,7 @@ static bool handle_in_body(GumboParser* parser, GumboToken* token) { return success; } else { bool result = true; - const GumboNode* node = state->_form_element; + GumboNode* node = state->_form_element; assert(!node || node->type == GUMBO_NODE_ELEMENT); state->_form_element = NULL; if (!node || !has_node_in_scope(parser, node)) { @@ -2622,10 +2625,15 @@ static bool handle_in_body(GumboParser* parser, GumboToken* token) { ignore_token(parser); return false; } + // Since we remove the form node without popping, we need to make sure + // that we flush any text nodes at the end of the form. + maybe_flush_text_node_buffer(parser); // This differs from implicitly_close_tags because we remove *only* the // <form> element; other nodes are left in scope. generate_implied_end_tags(parser, GUMBO_TAG_LAST); - if (get_current_node(parser) != node) { + if (get_current_node(parser) == node) { + record_end_of_element(token, &node->v.element); + } else { parser_add_parse_error(parser, token); result = false; } @@ -2811,100 +2819,13 @@ static bool handle_in_body(GumboParser* parser, GumboToken* token) { set_frameset_not_ok(parser); return result; } else if (tag_is(token, kStartTag, GUMBO_TAG_ISINDEX)) { - parser_add_parse_error(parser, token); - if (parser->_parser_state->_form_element != NULL && - !has_open_element(parser, GUMBO_TAG_TEMPLATE)) { - ignore_token(parser); - return false; - } - acknowledge_self_closing_tag(parser); - maybe_implicitly_close_p_tag(parser, token); - set_frameset_not_ok(parser); - - GumboVector* token_attrs = &token->v.start_tag.attributes; - GumboAttribute* prompt_attr = gumbo_get_attribute(token_attrs, "prompt"); - GumboAttribute* action_attr = gumbo_get_attribute(token_attrs, "action"); - GumboAttribute* name_attr = gumbo_get_attribute(token_attrs, "name"); - - GumboNode* form = insert_element_of_tag_type( - parser, GUMBO_TAG_FORM, GUMBO_INSERTION_FROM_ISINDEX); - if (!has_open_element(parser, GUMBO_TAG_TEMPLATE)) { - parser->_parser_state->_form_element = form; - } - if (action_attr) { - gumbo_vector_add(parser, action_attr, &form->v.element.attributes); - } - insert_element_of_tag_type( - parser, GUMBO_TAG_HR, GUMBO_INSERTION_FROM_ISINDEX); - pop_current_node(parser); // <hr> - - insert_element_of_tag_type( - parser, GUMBO_TAG_LABEL, GUMBO_INSERTION_FROM_ISINDEX); - TextNodeBufferState* text_state = &parser->_parser_state->_text_node; - text_state->_start_original_text = token->original_text.data; - text_state->_start_position = token->position; - text_state->_type = GUMBO_NODE_TEXT; - if (prompt_attr) { - size_t prompt_attr_length = strlen(prompt_attr->value); - gumbo_string_buffer_destroy(parser, &text_state->_buffer); - text_state->_buffer.data = gumbo_copy_stringz(parser, prompt_attr->value); - text_state->_buffer.length = prompt_attr_length; - text_state->_buffer.capacity = prompt_attr_length + 1; - gumbo_destroy_attribute(parser, prompt_attr); - } else { - GumboStringPiece prompt_text = - GUMBO_STRING("This is a searchable index. Enter search keywords: "); - gumbo_string_buffer_append_string( - parser, &prompt_text, &text_state->_buffer); - } - - GumboNode* input = insert_element_of_tag_type( - parser, GUMBO_TAG_INPUT, GUMBO_INSERTION_FROM_ISINDEX); - for (unsigned int i = 0; i < token_attrs->length; ++i) { - GumboAttribute* attr = token_attrs->data[i]; - if (attr != prompt_attr && attr != action_attr && attr != name_attr) { - gumbo_vector_add(parser, attr, &input->v.element.attributes); - } - token_attrs->data[i] = NULL; - } - - // All attributes have been successfully transferred and nulled out at this - // point, so the call to ignore_token will free the memory for it without - // touching the attributes. - ignore_token(parser); - - // The name attribute, if present, should be destroyed since it's ignored - // when copying over. The action attribute should be kept since it's moved - // to the form. - if (name_attr) { - gumbo_destroy_attribute(parser, name_attr); - } - - GumboAttribute* name = - gumbo_parser_allocate(parser, sizeof(GumboAttribute)); - GumboStringPiece name_str = GUMBO_STRING("name"); - GumboStringPiece isindex_str = GUMBO_STRING("isindex"); - name->attr_namespace = GUMBO_ATTR_NAMESPACE_NONE; - name->name = gumbo_copy_stringz(parser, "name"); - name->value = gumbo_copy_stringz(parser, "isindex"); - name->original_name = name_str; - name->original_value = isindex_str; - name->name_start = kGumboEmptySourcePosition; - name->name_end = kGumboEmptySourcePosition; - name->value_start = kGumboEmptySourcePosition; - name->value_end = kGumboEmptySourcePosition; - gumbo_vector_add(parser, name, &input->v.element.attributes); - - pop_current_node(parser); // <input> - pop_current_node(parser); // <label> - insert_element_of_tag_type( - parser, GUMBO_TAG_HR, GUMBO_INSERTION_FROM_ISINDEX); - pop_current_node(parser); // <hr> - pop_current_node(parser); // <form> - if (!has_open_element(parser, GUMBO_TAG_TEMPLATE)) { - parser->_parser_state->_form_element = NULL; + reconstruct_active_formatting_elements(parser); + insert_element_from_token(parser, token); + if (token->v.start_tag.is_self_closing) { + pop_current_node(parser); + acknowledge_self_closing_tag(parser); } - return false; + return true; } else if (tag_is(token, kStartTag, GUMBO_TAG_TEXTAREA)) { run_generic_parsing_algorithm(parser, token, GUMBO_LEX_RCDATA); parser->_parser_state->_ignore_next_linefeed = true; @@ -3482,6 +3403,17 @@ static bool handle_in_select(GumboParser* parser, GumboToken* token) { } insert_element_from_token(parser, token); return true; + } else if (tag_is(token, kStartTag, GUMBO_TAG_HR)) { + if (node_html_tag_is(get_current_node(parser), GUMBO_TAG_OPTION)) { + pop_current_node(parser); + } + if (node_html_tag_is(get_current_node(parser), GUMBO_TAG_OPTGROUP)) { + pop_current_node(parser); + } + insert_element_from_token(parser, token); + pop_current_node(parser); + acknowledge_self_closing_tag(parser); + return true; } else if (tag_is(token, kEndTag, GUMBO_TAG_OPTGROUP)) { GumboVector* open_elements = &parser->_parser_state->_open_elements; if (node_html_tag_is(get_current_node(parser), GUMBO_TAG_OPTION) && @@ -3845,40 +3777,33 @@ static bool handle_in_foreign_content(GumboParser* parser, GumboToken* token) { // Fall through to the if-statements below. break; } - // Order matters for these clauses. - if (tag_in(token, kStartTag, - (gumbo_tagset){TAG(B), TAG(BIG), TAG(BLOCKQUOTE), TAG(BODY), TAG(BR), - TAG(CENTER), TAG(CODE), TAG(DD), TAG(DIV), TAG(DL), TAG(DT), - TAG(EM), TAG(EMBED), TAG(H1), TAG(H2), TAG(H3), TAG(H4), TAG(H5), - TAG(H6), TAG(HEAD), TAG(HR), TAG(I), TAG(IMG), TAG(LI), - TAG(LISTING), TAG(MENU), TAG(META), TAG(NOBR), TAG(OL), TAG(P), - TAG(PRE), TAG(RUBY), TAG(S), TAG(SMALL), TAG(SPAN), TAG(STRONG), - TAG(STRIKE), TAG(SUB), TAG(SUP), TAG(TABLE), TAG(TT), TAG(U), - TAG(UL), TAG(VAR)}) || - (tag_is(token, kStartTag, GUMBO_TAG_FONT) && - (token_has_attribute(token, "color") || - token_has_attribute(token, "face") || - token_has_attribute(token, "size")))) { + + if (tag_in(token, kStartTag, (gumbo_tagset){ + TAG(B), TAG(BIG), TAG(BLOCKQUOTE), TAG(BODY), TAG(BR), TAG(CENTER), + TAG(CODE), TAG(DD), TAG(DIV), TAG(DL), TAG(DT), TAG(EM), TAG(EMBED), + TAG(H1), TAG(H2), TAG(H3), TAG(H4), TAG(H5), TAG(H6), TAG(HEAD), + TAG(HR), TAG(I), TAG(IMG), TAG(LI), TAG(LISTING), TAG(MENU), TAG(META), + TAG(NOBR), TAG(OL), TAG(P), TAG(PRE), TAG(RUBY), TAG(S), TAG(SMALL), + TAG(SPAN), TAG(STRONG), TAG(STRIKE), TAG(SUB), TAG(SUP), TAG(TABLE), + TAG(TT), TAG(U), TAG(UL), TAG(VAR)}) + || tag_in(token, kEndTag, (gumbo_tagset){TAG(BR), TAG(P)}) + || (tag_is(token, kStartTag, GUMBO_TAG_FONT) + && (token_has_attribute(token, "color") + || token_has_attribute(token, "face") + || token_has_attribute(token, "size")))) + { /* Parse error */ parser_add_parse_error(parser, token); - /* - * Fragment case: If the parser was originally created for the HTML - * fragment parsing algorithm, then act as described in the "any other - * start tag" entry below. - */ - if (!is_fragment_parser(parser)) { - do { - pop_current_node(parser); - } while (!(is_mathml_integration_point(get_current_node(parser)) || - is_html_integration_point(get_current_node(parser)) || - get_current_node(parser)->v.element.tag_namespace == - GUMBO_NAMESPACE_HTML)); - parser->_parser_state->_reprocess_current_token = true; - return false; + while (!is_mathml_integration_point(get_current_node(parser)) + && !is_html_integration_point(get_current_node(parser)) + && get_current_node(parser)->v.element.tag_namespace != GUMBO_NAMESPACE_HTML) + { + pop_current_node(parser); } - assert(token->type == GUMBO_TOKEN_START_TAG); + handle_html_content(parser, token); + return false; } if (token->type == GUMBO_TOKEN_START_TAG) { diff --git a/src/gumbo/tag.c b/src/gumbo/tag.c index a394c0a67..85d58d28f 100644 --- a/src/gumbo/tag.c +++ b/src/gumbo/tag.c @@ -41,7 +41,6 @@ void gumbo_tag_from_original_text(GumboStringPiece* text) { if (text->data == NULL) { return; } - assert(text->length >= 2); assert(text->data[0] == '<'); assert(text->data[text->length - 1] == '>'); @@ -54,10 +53,10 @@ void gumbo_tag_from_original_text(GumboStringPiece* text) { // Start tag. text->data += 1; // Move past < text->length -= 2; - // strnchr is apparently not a standard C library function, so I loop // explicitly looking for whitespace or other illegal tag characters. + // see https://html.spec.whatwg.org/multipage/syntax.html#tag-name-state for (const char* c = text->data; c != text->data + text->length; ++c) { - if (isspace(*c) || *c == '/') { + if (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\f' || *c == '/') { text->length = c - text->data; break; } @@ -91,5 +90,5 @@ GumboTag gumbo_tagn_enum(const char* tagname, unsigned int length) { } GumboTag gumbo_tag_enum(const char* tagname) { - return gumbo_tagn_enum(tagname, (unsigned)strlen(tagname)); + return gumbo_tagn_enum(tagname, strlen(tagname)); } diff --git a/src/gumbo/tag.in b/src/gumbo/tag.in index 4c2526485..571646291 100644 --- a/src/gumbo/tag.in +++ b/src/gumbo/tag.in @@ -70,6 +70,7 @@ ins del image img +picture iframe embed object @@ -146,5 +147,6 @@ marquee multicol nobr spacer +dialog tt rtc diff --git a/src/gumbo/tokenizer.c b/src/gumbo/tokenizer.c index 0d0ea0f24..71b5d32ad 100644 --- a/src/gumbo/tokenizer.c +++ b/src/gumbo/tokenizer.c @@ -377,7 +377,7 @@ static bool temporary_buffer_equals(GumboParser* parser, const char* text) { // TODO(jdtang): See if the extra strlen is a performance problem, and replace // it with an explicit sizeof(literal) if necessary. I don't think it will // be, as this is only used in a couple of rare states. - size_t text_len = strlen(text); + int text_len = strlen(text); return text_len == buffer->length && memcmp(buffer->data, text, text_len) == 0; } @@ -726,7 +726,8 @@ static void copy_over_original_tag_text(GumboParser* parser, original_text->data = tag_state->_original_text; original_text->length = utf8iterator_get_char_pointer(&tokenizer->_input) - tag_state->_original_text; - if (original_text->data[original_text->length - 1] == '\r') { + if (original_text->length > 0 + && original_text->data[original_text->length - 1] == '\r') { // Since \r is skipped by the UTF-8 iterator, it can sometimes end up // appended to the end of original text even when it's really the first part // of the next character. If we detect this situation, shrink the length of @@ -751,7 +752,7 @@ static void finish_tag_name(GumboParser* parser) { GumboTagState* tag_state = &tokenizer->_tag_state; tag_state->_tag = - gumbo_tagn_enum(tag_state->_buffer.data, (unsigned)tag_state->_buffer.length); + gumbo_tagn_enum(tag_state->_buffer.data, tag_state->_buffer.length); reinitialize_tag_buffer(parser); } @@ -839,7 +840,7 @@ static bool is_appropriate_end_tag(GumboParser* parser) { assert(!tag_state->_is_start_tag); return tag_state->_last_start_tag != GUMBO_TAG_LAST && tag_state->_last_start_tag == gumbo_tagn_enum(tag_state->_buffer.data, - (unsigned)tag_state->_buffer.length); + tag_state->_buffer.length); } void gumbo_tokenizer_state_init( diff --git a/src/gumbo/utf8.c b/src/gumbo/utf8.c index ad73cefa6..fdd6f8377 100644 --- a/src/gumbo/utf8.c +++ b/src/gumbo/utf8.c @@ -137,7 +137,7 @@ static void read_char(Utf8Iterator* iter) { for (const char* c = iter->_start; c < iter->_end; ++c) { decode(&state, &code_point, (uint32_t)(unsigned char) (*c)); if (state == UTF8_ACCEPT) { - iter->_width = (int)(c - iter->_start + 1); + iter->_width = c - iter->_start + 1; // This is the special handling for carriage returns that is mandated by // the HTML5 spec. Since we're looking for particular 7-bit literal // characters, we operate in terms of chars and only need a check for iter @@ -181,7 +181,7 @@ static void read_char(Utf8Iterator* iter) { } static void update_position(Utf8Iterator* iter) { - iter->_pos.offset += (int)iter->_width; + iter->_pos.offset += iter->_width; if (iter->_current == '\n') { ++iter->_pos.line; iter->_pos.column = 1; diff --git a/src/gumbo/vector.c b/src/gumbo/vector.c index b9aa474e6..51758dfe0 100644 --- a/src/gumbo/vector.c +++ b/src/gumbo/vector.c @@ -30,7 +30,7 @@ const GumboVector kGumboEmptyVector = {NULL, 0, 0}; void gumbo_vector_init(struct GumboInternalParser* parser, size_t initial_capacity, GumboVector* vector) { vector->length = 0; - vector->capacity = (unsigned)initial_capacity; + vector->capacity = initial_capacity; if (initial_capacity > 0) { vector->data = gumbo_parser_allocate(parser, sizeof(void*) * initial_capacity); diff --git a/src/html.cpp b/src/html.cpp index ee8ddad8d..27f1cfe55 100644 --- a/src/html.cpp +++ b/src/html.cpp @@ -1,8 +1,10 @@ #include "html.h" #include "types.h" -#include "utf8_strings.h" -void litehtml::trim(string &s, const string& chars_to_trim) +namespace litehtml +{ + +string& trim(string& s, const string& chars_to_trim) { string::size_type pos = s.find_first_not_of(chars_to_trim); if(pos != string::npos) @@ -12,24 +14,33 @@ void litehtml::trim(string &s, const string& chars_to_trim) else { s = ""; - return; + return s; } pos = s.find_last_not_of(chars_to_trim); if(pos != string::npos) { s.erase(s.begin() + pos + 1, s.end()); } + return s; +} + +string trim(const string& s, const string& chars_to_trim) +{ + string str = s; + trim(str, chars_to_trim); + return str; } -void litehtml::lcase(string &s) +string& lcase(string& s) { for(char & i : s) { - i = t_tolower(i); + i = (char)t_tolower(i); } + return s; } -litehtml::string::size_type litehtml::find_close_bracket(const string &s, string::size_type off, char open_b, char close_b) +string::size_type find_close_bracket(const string& s, string::size_type off, char open_b, char close_b) { int cnt = 0; for(string::size_type i = off; i < s.length(); i++) @@ -49,20 +60,20 @@ litehtml::string::size_type litehtml::find_close_bracket(const string &s, string return string::npos; } -litehtml::string litehtml::index_value(int index, const string& strings, char delim) +string index_value(int index, const string& strings, char delim) { std::vector<string> vals; string delims; delims.push_back(delim); split_string(strings, vals, delims); - if(index >= 0 && index < vals.size()) + if(index >= 0 && index < (int) vals.size()) { return vals[index]; } return std::to_string(index); } -int litehtml::value_index( const string& val, const string& strings, int defValue, char delim ) +int value_index( const string& val, const string& strings, int defValue, char delim ) { if(val.empty() || strings.empty() || !delim) { @@ -99,7 +110,7 @@ int litehtml::value_index( const string& val, const string& strings, int defValu return defValue; } -bool litehtml::value_in_list( const string& val, const string& strings, char delim ) +bool value_in_list( const string& val, const string& strings, char delim ) { int idx = value_index(val, strings, -1, delim); if(idx >= 0) @@ -109,7 +120,14 @@ bool litehtml::value_in_list( const string& val, const string& strings, char del return false; } -void litehtml::split_string(const string& str, string_vector& tokens, const string& delims, const string& delims_preserve, const string& quote) +string_vector split_string(const string& str, const string& delims, const string& delims_preserve, const string& quote) +{ + string_vector result; + split_string(str, result, delims, delims_preserve, quote); + return result; +} + +void split_string(const string& str, string_vector& tokens, const string& delims, const string& delims_preserve, const string& quote) { if(str.empty() || (delims.empty() && delims_preserve.empty())) { @@ -171,7 +189,7 @@ void litehtml::split_string(const string& str, string_vector& tokens, const stri } } -void litehtml::join_string(string& str, const string_vector& tokens, const string& delims) +void join_string(string& str, const string_vector& tokens, const string& delims) { str = ""; for (size_t i = 0; i < tokens.size(); i++) @@ -184,7 +202,7 @@ void litehtml::join_string(string& str, const string_vector& tokens, const strin } } -int litehtml::t_strcasecmp(const char *s1, const char *s2) +int t_strcasecmp(const char *s1, const char *s2) { int i, d, c; @@ -201,11 +219,11 @@ int litehtml::t_strcasecmp(const char *s1, const char *s2) } } -int litehtml::t_strncasecmp(const char *s1, const char *s2, size_t n) +int t_strncasecmp(const char *s1, const char *s2, size_t n) { int i, d, c; - for (i = 0; i < n; i++) + for (i = 0; i < (int) n; i++) { c = t_tolower((unsigned char)s1[i]); d = c - t_tolower((unsigned char)s2[i]); @@ -220,7 +238,7 @@ int litehtml::t_strncasecmp(const char *s1, const char *s2, size_t n) return 0; } -litehtml::string litehtml::get_escaped_string(const string& in_str) +string get_escaped_string(const string& in_str) { string ret; for (auto ch : in_str) @@ -278,7 +296,7 @@ litehtml::string litehtml::get_escaped_string(const string& in_str) return ret; } -bool litehtml::is_number(const string& string, const bool allow_dot) { +bool is_number(const string& string, const bool allow_dot) { for (auto ch : string) { if (!(t_isdigit(ch) || (allow_dot && ch == '.'))) @@ -288,3 +306,5 @@ bool litehtml::is_number(const string& string, const bool allow_dot) { } return true; } + +} // namespace litehtml \ No newline at end of file diff --git a/src/html_microsyntaxes.cpp b/src/html_microsyntaxes.cpp new file mode 100644 index 000000000..d8ce01471 --- /dev/null +++ b/src/html_microsyntaxes.cpp @@ -0,0 +1,103 @@ +#include "html_microsyntaxes.h" +#include "html.h" + +namespace litehtml +{ + +// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#rules-for-parsing-integers +bool html_parse_integer(const string& str, int& val) +{ + const char* ptr = str.c_str(); + char* end; + // AFAICT strtol does exactly what's required by the standard + int n = strtol(ptr, &end, 10); + if (end == ptr) return false; + val = n; + return true; +} + +// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#rules-for-parsing-non-negative-integers +bool html_parse_non_negative_integer(const string& str, int& val) +{ + int n = 0; + if (!html_parse_integer(str, n) || n < 0) + return false; + val = n; + return true; +} + +// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#rules-for-parsing-dimension-values +bool html_parse_dimension_value(const string& str, float& result, html_dimension_type& type) +{ + // 1. Let input be the string being parsed. + // 2. Let position be a position variable for input, initially pointing at the start of input. + auto position = str.c_str(); + // 3. Skip ASCII whitespace within input given position. + while (is_whitespace(*position)) position++; + // 4. If position is past the end of input or the code point at position within input is not an ASCII digit, then return failure. + if (!is_digit(*position)) return false; + // 5. Collect a sequence of code points that are ASCII digits from input given position, and interpret the resulting sequence as a base-ten integer. Let value be that number. + char* end; + float value = (float)strtol(position, &end, 10); + position = end; + // 6. If position is past the end of input, then return value as a length. + if (!*position) + { + result = value; + type = html_length; + return true; + } + // 7. If the code point at position within input is U+002E (.), then: + if (*position == '.') + { + // 1. Advance position by 1. + position++; + // 2. If position is past the end of input or the code point at position within input is not an ASCII digit, then return the current dimension value with value, input, and position. + if (!is_digit(*position)) + { + result = value; + type = *position == '%' ? html_percentage : html_length; + return true; + } + // 3. Let divisor have the value 1. + float divisor = 1; + // 4. While true: + while (true) + { + // 1. Multiply divisor by ten. + divisor *= 10; + // 2. Add the value of the code point at position within input, interpreted as a base-ten digit (0..9) and divided by divisor, to value. + value += digit_value(*position) / divisor; + // 3. Advance position by 1. + position++; + // 4. If position is past the end of input, then return value as a length. + if (!*position) + { + result = value; + type = html_length;; + return true; + } + // 5. If the code point at position within input is not an ASCII digit, then break. + if (!is_digit(*position)) + break; + } + } + // 8. Return the current dimension value with value, input, and position. + result = value; + type = *position == '%' ? html_percentage : html_length; + return true; +} + +// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#rules-for-parsing-non-zero-dimension-values +bool html_parse_nonzero_dimension_value(const string& str, float& val, html_dimension_type& type) +{ + float x; + html_dimension_type t; + if (!html_parse_dimension_value(str, x, t) || x == 0) + return false; + val = x; + type = t; + return true; +} + +} // namespace litehtml diff --git a/src/html_tag.cpp b/src/html_tag.cpp index 1814e9c3a..08f289df9 100644 --- a/src/html_tag.cpp +++ b/src/html_tag.cpp @@ -1,16 +1,20 @@ +#include <algorithm> + #include "html.h" #include "html_tag.h" #include "document.h" +#include "html_microsyntaxes.h" #include "iterators.h" #include "stylesheet.h" #include "table.h" -#include <algorithm> -#include <locale> -#include "el_before_after.h" #include "num_cvt.h" #include "line_box.h" -#include <stack> #include "render_item.h" +#include "internal.h" +#include "document_container.h" + +namespace litehtml +{ litehtml::html_tag::html_tag(const std::shared_ptr<document>& doc) : element(doc) { @@ -61,57 +65,56 @@ void litehtml::html_tag::clearRecursive() m_children.clear(); } -litehtml::string_id litehtml::html_tag::id() const +string_id html_tag::id() const { return m_id; } -litehtml::string_id litehtml::html_tag::tag() const +string_id html_tag::tag() const { return m_tag; } -const char* litehtml::html_tag::get_tagName() const +const char* html_tag::get_tagName() const { return _s(m_tag).c_str(); } -void litehtml::html_tag::set_tagName( const char* _tag ) +void html_tag::set_tagName( const char* tag ) { - string tag = _tag; - lcase(tag); - m_tag = _id(tag); + m_tag = _id(lowcase(tag)); } -void litehtml::html_tag::set_attr( const char* _name, const char* _val ) +void html_tag::set_attr( const char* _name, const char* _val ) { - if(_name && _val) + if (_name && _val) { - string name = _name; - lcase(name); + // attribute names in attribute selector are matched ASCII case-insensitively regardless of document mode + string name = lowcase(_name); + // m_attrs has all attribute values, including class and id, in their original case + // because in attribute selector values are matched case-sensitively even in quirks mode m_attrs[name] = _val; - if( name == "class" ) + if (name == "class") { string val = _val; - // class names are matched case-insensitively in quirks mode - // we match them case-insensitively in all modes (same for id) - lcase(val); - m_str_classes.resize( 0 ); - split_string( val, m_str_classes, " " ); + // class names in class selector (.xxx) are matched ASCII case-insensitively in quirks mode + if (get_document()->mode() == quirks_mode) lcase(val); + m_str_classes = split_string(val, whitespace, "", ""); m_classes.clear(); - for (auto& cls : m_str_classes) m_classes.push_back(_id(cls)); + for (auto cls : m_str_classes) m_classes.push_back(_id(cls)); } else if (name == "id") { string val = _val; - lcase(val); + // ids in id selector (#xxx) are matched ASCII case-insensitively in quirks mode + if (get_document()->mode() == quirks_mode) lcase(val); m_id = _id(val); } } } -const char* litehtml::html_tag::get_attr( const char* name, const char* def ) const +const char* html_tag::get_attr( const char* name, const char* def ) const { auto attr = m_attrs.find(name); if(attr != m_attrs.end()) @@ -124,8 +127,8 @@ const char* litehtml::html_tag::get_attr( const char* name, const char* def ) co litehtml::elements_list litehtml::html_tag::select_all(const string& selector ) { css_selector sel; - sel.parse(selector); - + sel.parse(selector, get_document()->mode()); + return select_all(sel); } @@ -142,7 +145,7 @@ void litehtml::html_tag::select_all(const css_selector& selector, elements_list& { res.push_back(shared_from_this()); } - + for(auto& el : m_children) { el->select_all(selector, res); @@ -153,7 +156,7 @@ void litehtml::html_tag::select_all(const css_selector& selector, elements_list& litehtml::element::ptr litehtml::html_tag::select_one( const string& selector ) { css_selector sel; - sel.parse(selector); + sel.parse(selector, get_document()->mode()); return select_one(sel); } @@ -178,11 +181,6 @@ litehtml::element::ptr litehtml::html_tag::select_one( const css_selector& selec void litehtml::html_tag::apply_stylesheet( const litehtml::css& stylesheet ) { - if(is_root()) - { - int i = 0; - i++; - } for(const auto& sel : stylesheet.selectors()) { // optimization @@ -194,8 +192,7 @@ void litehtml::html_tag::apply_stylesheet( const litehtml::css& stylesheet ) if (!r.m_attrs.empty()) { const auto& attr = r.m_attrs[0]; - if (attr.type == select_class && - std::find(m_classes.begin(), m_classes.end(), attr.name) == m_classes.end()) + if (attr.type == select_class && !(attr.name in m_classes)) continue; } } @@ -204,14 +201,14 @@ void litehtml::html_tag::apply_stylesheet( const litehtml::css& stylesheet ) if(apply != select_no_match) { - used_selector::ptr us = std::unique_ptr<used_selector>(new used_selector(sel, false)); + used_selector::ptr us = std::make_unique<used_selector>(sel, false); if(sel->is_media_valid()) { auto apply_before_after = [&]() { const auto& content_property = sel->m_style->get_property(_content_); - bool content_none = content_property.m_type == prop_type_string && content_property.m_string == "none"; + bool content_none = content_property.is<string>() && content_property.get<string>() == "none"; bool create = !content_none && (sel->m_right.m_attrs.size() > 1 || sel->m_right.m_tag != star_id); element::ptr el; @@ -300,7 +297,8 @@ void litehtml::html_tag::draw(uint_ptr hdc, int x, int y, const position *clip, draw_background(hdc, x, y, clip, ri); - if(m_css.get_display() == display_list_item && m_css.get_list_style_type() != list_style_type_none) + if(m_css.get_display() == display_list_item && + (m_css.get_list_style_type() != list_style_type_none || m_css.get_list_style_image() != "")) { if(m_css.get_overflow() > overflow_visible) { @@ -325,91 +323,20 @@ void litehtml::html_tag::draw(uint_ptr hdc, int x, int y, const position *clip, } } -litehtml::string litehtml::html_tag::get_custom_property(string_id name, const string& default_value) const +bool html_tag::get_custom_property(string_id name, css_token_vector& result) const { const property_value& value = m_style.get_property(name); - if (value.m_type == prop_type_string) + if (value.is<css_token_vector>()) { - return value.m_string; - } - else if (auto _parent = parent()) - { - return _parent->get_custom_property(name, default_value); - } - return default_value; -} - -template<class Type, litehtml::property_type property_value_type, Type litehtml::property_value::* property_value_member> -const Type& litehtml::html_tag::get_property_impl(string_id name, bool inherited, const Type& default_value, uint_ptr css_properties_member_offset) const -{ - const property_value& value = m_style.get_property(name); - - if (value.m_type == property_value_type) - { - return value.*property_value_member; + result = value.get<css_token_vector>(); + return true; } - else if (inherited || value.m_type == prop_type_inherit) + else if (auto _parent = dynamic_cast<html_tag*>(parent().get())) { - if (auto _parent = parent()) - { - return *(Type*)((byte*)&_parent->css() + css_properties_member_offset); - } - return default_value; + return _parent->get_custom_property(name, result); } - // value must be invalid here - //assert(value.m_type == prop_type_invalid); - return default_value; -} - -int litehtml::html_tag::get_enum_property(string_id name, bool inherited, int default_value, uint_ptr css_properties_member_offset) const -{ - return get_property_impl<int, prop_type_enum_item, &property_value::m_enum_item>(name, inherited, default_value, css_properties_member_offset); -} - -int litehtml::html_tag::get_int_property(string_id name, bool inherited, int default_value, uint_ptr css_properties_member_offset) const -{ - return get_property_impl<int, prop_type_enum_item, &property_value::m_enum_item>(name, inherited, default_value, css_properties_member_offset); -} - -litehtml::css_length litehtml::html_tag::get_length_property(string_id name, bool inherited, css_length default_value, uint_ptr css_properties_member_offset) const -{ - return get_property_impl<css_length, prop_type_length, &property_value::m_length>(name, inherited, default_value, css_properties_member_offset); -} - -litehtml::web_color litehtml::html_tag::get_color_property(string_id name, bool inherited, web_color default_value, uint_ptr css_properties_member_offset) const -{ - return get_property_impl<web_color, prop_type_color, &property_value::m_color>(name, inherited, default_value, css_properties_member_offset); -} - -litehtml::string litehtml::html_tag::get_string_property(string_id name, bool inherited, const string& default_value, uint_ptr css_properties_member_offset) const -{ - return get_property_impl<string, prop_type_string, &property_value::m_string>(name, inherited, default_value, css_properties_member_offset); -} - -float litehtml::html_tag::get_number_property(string_id name, bool inherited, float default_value, uint_ptr css_properties_member_offset) const -{ - return get_property_impl<float, prop_type_number, &property_value::m_number>(name, inherited, default_value, css_properties_member_offset); -} - -litehtml::string_vector litehtml::html_tag::get_string_vector_property(string_id name, bool inherited, const string_vector& default_value, uint_ptr css_properties_member_offset) const -{ - return get_property_impl<string_vector, prop_type_string_vector, &property_value::m_string_vector>(name, inherited, default_value, css_properties_member_offset); -} - -litehtml::int_vector litehtml::html_tag::get_int_vector_property(string_id name, bool inherited, const int_vector& default_value, uint_ptr css_properties_member_offset) const -{ - return get_property_impl<int_vector, prop_type_enum_item_vector, &property_value::m_enum_item_vector>(name, inherited, default_value, css_properties_member_offset); -} - -litehtml::length_vector litehtml::html_tag::get_length_vector_property(string_id name, bool inherited, const length_vector& default_value, uint_ptr css_properties_member_offset) const -{ - return get_property_impl<length_vector, prop_type_length_vector, &property_value::m_length_vector>(name, inherited, default_value, css_properties_member_offset); -} - -litehtml::size_vector litehtml::html_tag::get_size_vector_property(string_id name, bool inherited, const size_vector& default_value, uint_ptr css_properties_member_offset) const -{ - return get_property_impl<size_vector, prop_type_size_vector, &property_value::m_size_vector>(name, inherited, default_value, css_properties_member_offset); + return false; } void litehtml::html_tag::compute_styles(bool recursive) @@ -440,10 +367,20 @@ bool litehtml::html_tag::is_white_space() const return false; } +int html_tag::select(const css_selector::vector& selector_list, bool apply_pseudo) +{ + for (auto sel : selector_list) + { + if (int result = select(*sel, apply_pseudo)) + return result; + } + return select_no_match; +} + int litehtml::html_tag::select(const string& selector) { css_selector sel; - sel.parse(selector); + sel.parse(selector, get_document()->mode()); return select(sel, true); } @@ -547,7 +484,7 @@ int litehtml::html_tag::select(const css_element_selector& selector, bool apply_ switch(attr.type) { case select_class: - if (std::find(m_classes.begin(), m_classes.end(), attr.name) == m_classes.end()) + if (!(attr.name in m_classes)) { return select_no_match; } @@ -600,7 +537,7 @@ int litehtml::html_tag::select(const css_element_selector& selector, bool apply_ return res; } -int litehtml::html_tag::select_pseudoclass(const css_attribute_selector& sel) +int html_tag::select_pseudoclass(const css_attribute_selector& sel) { element::ptr el_parent = parent(); @@ -656,7 +593,7 @@ int litehtml::html_tag::select_pseudoclass(const css_attribute_selector& sel) switch (sel.name) { case _nth_child_: - if (!el_parent->is_nth_child(shared_from_this(), num, off, false)) + if (!el_parent->is_nth_child(shared_from_this(), num, off, false, sel.selector_list)) { return select_no_match; } @@ -668,7 +605,7 @@ int litehtml::html_tag::select_pseudoclass(const css_attribute_selector& sel) } break; case _nth_last_child_: - if (!el_parent->is_nth_last_child(shared_from_this(), num, off, false)) + if (!el_parent->is_nth_last_child(shared_from_this(), num, off, false, sel.selector_list)) { return select_no_match; } @@ -679,24 +616,32 @@ int litehtml::html_tag::select_pseudoclass(const css_attribute_selector& sel) return select_no_match; } break; + default: + break; } } break; + case _is_: + if (!select(sel.selector_list, true)) + { + return select_no_match; + } + break; case _not_: - if (select(*sel.sel, true)) + if (select(sel.selector_list, true)) { return select_no_match; } break; case _lang_: - if (!get_document()->match_lang(sel.val)) + if (!get_document()->match_lang(sel.value)) { return select_no_match; } break; default: - if (std::find(m_pseudo_classes.begin(), m_pseudo_classes.end(), sel.name) == m_pseudo_classes.end()) + if (!(sel.name in m_pseudo_classes)) { return select_no_match; } @@ -705,59 +650,69 @@ int litehtml::html_tag::select_pseudoclass(const css_attribute_selector& sel) return select_match; } -int litehtml::html_tag::select_attribute(const css_attribute_selector& sel) +// https://www.w3.org/TR/selectors-4/#attribute-selectors +int html_tag::select_attribute(const css_attribute_selector& sel) { - const char* attr_value = get_attr(_s(sel.name).c_str()); + const char* sz_attr_value = get_attr(_s(sel.name).c_str()); + + if (!sz_attr_value) return select_no_match; + + string attr_value = sel.caseless_match ? lowcase(sz_attr_value) : sz_attr_value; - switch (sel.type) + switch (sel.matcher) { - case select_exists: - if (!attr_value) + case attribute_exists: + return select_match; + + case attribute_equals: + if (attr_value == sel.value) { - return select_no_match; + return select_match; } break; - case select_equal: - if (!attr_value || strcmp(attr_value, sel.val.c_str())) + + case attribute_contains_string: // *= + if (sel.value != "" && contains(attr_value, sel.value)) { - return select_no_match; + return select_match; } break; - case select_contain_str: - if (!attr_value || !strstr(attr_value, sel.val.c_str())) + + // Attribute value is a whitespace-separated list of words, one of which is exactly sel.value + case attribute_contains_word: // ~= + if (sel.value != "" && contains(split_string(attr_value), sel.value)) { - return select_no_match; + return select_match; } break; - case select_start_str: - if (!attr_value || strncmp(attr_value, sel.val.c_str(), sel.val.length())) + + case attribute_starts_with_string: // ^= + if (sel.value != "" && match(attr_value, 0, sel.value)) { - return select_no_match; + return select_match; } break; - case select_end_str: - if (!attr_value) + + // Attribute value is either equals sel.value or begins with sel.value immediately followed by "-". + case attribute_starts_with_string_hyphen: // |= + // Note: no special treatment for sel.value == "" + if (attr_value == sel.value || match(attr_value, 0, sel.value + '-')) { - return select_no_match; + return select_match; } - else if (strncmp(attr_value, sel.val.c_str(), sel.val.length())) + break; + + case attribute_ends_with_string: // $= + if (sel.value != "" && match(attr_value, -(int)sel.value.size(), sel.value)) { - const char* s = attr_value + strlen(attr_value) - sel.val.length() - 1; - if (s < attr_value) - { - return select_no_match; - } - if (sel.val != s) - { - return select_no_match; - } + return select_match; } break; } - return select_match; + return select_no_match; } -litehtml::element::ptr litehtml::html_tag::find_ancestor(const css_selector& selector, bool apply_pseudo, bool* is_pseudo) +element::ptr html_tag::find_ancestor(const css_selector& selector, bool apply_pseudo, bool* is_pseudo) { element::ptr el_parent = parent(); if (!el_parent) @@ -863,7 +818,7 @@ bool litehtml::html_tag::on_lbutton_down() return ret; } -bool litehtml::html_tag::on_lbutton_up() +bool litehtml::html_tag::on_lbutton_up(bool is_click) { bool ret = false; @@ -877,7 +832,7 @@ bool litehtml::html_tag::on_lbutton_up() el = el->parent(); } - on_click(); + if (is_click) on_click(); return ret; } @@ -886,10 +841,13 @@ void litehtml::html_tag::on_click() { if (!is_root()) { - element::ptr el_parent = parent(); - if (el_parent) + if(!get_document()->container()->on_element_click(shared_from_this())) { - el_parent->on_click(); + element::ptr el_parent = parent(); + if (el_parent) + { + el_parent->on_click(); + } } } } @@ -902,17 +860,17 @@ bool litehtml::html_tag::is_break() const void litehtml::html_tag::draw_background(uint_ptr hdc, int x, int y, const position *clip, const std::shared_ptr<render_item> &ri) { - position pos = ri->pos(); - pos.x += x; - pos.y += y; - - position el_pos = pos; - el_pos += ri->get_paddings(); - el_pos += ri->get_margins(); - if(m_css.get_display() != display_inline && m_css.get_display() != display_table_row) { - if(el_pos.does_intersect(clip) || is_root()) + position pos = ri->pos(); + pos.x += x; + pos.y += y; + + position border_box = pos; + border_box += ri->get_paddings(); + border_box += ri->get_borders(); + + if(border_box.does_intersect(clip) || is_root()) { auto v_offset = ri->get_draw_vertical_offset(); pos.y += v_offset; @@ -921,22 +879,19 @@ void litehtml::html_tag::draw_background(uint_ptr hdc, int x, int y, const posit const background* bg = get_background(); if(bg) { - std::vector<background_paint> bg_paint; - init_background_paint(pos, bg_paint, bg, ri); - if(is_root()) + int num_layers = bg->get_layers_number(); + for(int i = num_layers - 1; i >= 0; i--) { - for(auto& b : bg_paint) + background_layer layer; + if(!bg->get_layer(i, pos, this, ri, layer)) continue; + if(is_root() && (clip != nullptr)) { - b.clip_box = *clip; - b.border_box = *clip; + layer.clip_box = *clip; + layer.border_box = *clip; } + bg->draw_layer(hdc, i, layer, get_document()->container()); } - - get_document()->container()->draw_background(hdc, bg_paint); } - position border_box = pos; - border_box += ri->get_paddings(); - border_box += ri->get_borders(); borders bdr = m_css.get_borders(); if(bdr.is_visible()) @@ -952,7 +907,6 @@ void litehtml::html_tag::draw_background(uint_ptr hdc, int x, int y, const posit position::vector boxes; ri->get_inline_boxes(boxes); - std::vector<background_paint> bg_paint; position content_box; for(auto box = boxes.begin(); box != boxes.end(); box++) @@ -966,11 +920,6 @@ void litehtml::html_tag::draw_background(uint_ptr hdc, int x, int y, const posit content_box -= ri->get_borders(); content_box -= ri->get_paddings(); - if(bg) - { - init_background_paint(content_box, bg_paint, bg, ri); - } - css_borders bdr; // set left borders radius for the first box @@ -991,7 +940,7 @@ void litehtml::html_tag::draw_background(uint_ptr hdc, int x, int y, const posit bdr.radius.top_right_y = m_css.get_borders().radius.top_right_y; } - + bdr.top = m_css.get_borders().top; bdr.bottom = m_css.get_borders().bottom; if(box == boxes.begin()) @@ -1005,11 +954,14 @@ void litehtml::html_tag::draw_background(uint_ptr hdc, int x, int y, const posit if(bg) { - for (auto& bgp : bg_paint) + int num_layers = bg->get_layers_number(); + for(int i = num_layers - 1; i >= 0; i--) { - bgp.border_radius = bdr.radius.calc_percents(bgp.border_box.width, bgp.border_box.width); + background_layer layer; + if(!bg->get_layer(i, content_box, this, ri, layer)) continue; + layer.border_radius = bdr.radius.calc_percents(box->width, box->height); + bg->draw_layer(hdc, i, layer, get_document()->container()); } - get_document()->container()->draw_background(hdc, bg_paint); } if(bdr.is_visible()) { @@ -1095,134 +1047,6 @@ bool litehtml::html_tag::is_replaced() const return false; } -void litehtml::html_tag::init_background_paint(position pos, std::vector<background_paint>& bg_paint, const background* bg, const std::shared_ptr<render_item>& ri) -{ - bg_paint = { background_paint() }; - if (!bg) return; - - int bg_count = std::max((int)bg->m_image.size(), 1); - bg_paint.resize(bg_count); - - for (int i = 0; i < bg_count; i++) - { - init_one_background_paint(i, pos, bg_paint[i], bg, ri); - } - - bg_paint.back().color = bg->m_color; -} - -void litehtml::html_tag::init_one_background_paint(int i, position pos, background_paint& bg_paint, const background* bg, const std::shared_ptr<render_item>& ri) -{ - bg_paint.image = i < bg->m_image.size() ? bg->m_image[i] : ""; - bg_paint.baseurl = bg->m_baseurl; - bg_paint.attachment = i < bg->m_attachment.size() ? (background_attachment)bg->m_attachment[i] : background_attachment_scroll; - bg_paint.repeat = i < bg->m_repeat.size() ? (background_repeat)bg->m_repeat[i] : background_repeat_repeat; - int clip = i < bg->m_clip.size() ? bg->m_clip[i] : background_box_border; - int origin = i < bg->m_origin.size() ? bg->m_origin[i] : background_box_padding; - const css_size auto_auto(css_length::predef_value(background_size_auto), css_length::predef_value(background_size_auto)); - css_size size = i < bg->m_size.size() ? bg->m_size[i] : auto_auto; - css_length position_x = i < bg->m_position_x.size() ? bg->m_position_x[i] : css_length(0, css_units_percentage); - css_length position_y = i < bg->m_position_y.size() ? bg->m_position_y[i] : css_length(0, css_units_percentage); - - position content_box = pos; - position padding_box = pos; - padding_box += ri->get_paddings(); - position border_box = padding_box; - border_box += ri->get_borders(); - - switch(clip) - { - case background_box_padding: - bg_paint.clip_box = padding_box; - break; - case background_box_content: - bg_paint.clip_box = content_box; - break; - default: - bg_paint.clip_box = border_box; - break; - } - - switch(origin) - { - case background_box_border: - bg_paint.origin_box = border_box; - break; - case background_box_content: - bg_paint.origin_box = content_box; - break; - default: - bg_paint.origin_box = padding_box; - break; - } - - if(!bg_paint.image.empty()) - { - get_document()->container()->get_image_size(bg_paint.image.c_str(), bg_paint.baseurl.c_str(), bg_paint.image_size); - if(bg_paint.image_size.width && bg_paint.image_size.height) - { - litehtml::size img_new_sz = bg_paint.image_size; - double img_ar_width = (double) bg_paint.image_size.width / (double) bg_paint.image_size.height; - double img_ar_height = (double) bg_paint.image_size.height / (double) bg_paint.image_size.width; - - - if(size.width.is_predefined()) - { - switch(size.width.predef()) - { - case background_size_contain: - if( (int) ((double) bg_paint.origin_box.width * img_ar_height) <= bg_paint.origin_box.height ) - { - img_new_sz.width = bg_paint.origin_box.width; - img_new_sz.height = (int) ((double) bg_paint.origin_box.width * img_ar_height); - } else - { - img_new_sz.height = bg_paint.origin_box.height; - img_new_sz.width = (int) ((double) bg_paint.origin_box.height * img_ar_width); - } - break; - case background_size_cover: - if( (int) ((double) bg_paint.origin_box.width * img_ar_height) >= bg_paint.origin_box.height ) - { - img_new_sz.width = bg_paint.origin_box.width; - img_new_sz.height = (int) ((double) bg_paint.origin_box.width * img_ar_height); - } else - { - img_new_sz.height = bg_paint.origin_box.height; - img_new_sz.width = (int) ((double) bg_paint.origin_box.height * img_ar_width); - } - break; - break; - case background_size_auto: - if(!size.height.is_predefined()) - { - img_new_sz.height = size.height.calc_percent(bg_paint.origin_box.height); - img_new_sz.width = (int) ((double) img_new_sz.height * img_ar_width); - } - break; - } - } else - { - img_new_sz.width = size.width.calc_percent(bg_paint.origin_box.width); - if(size.height.is_predefined()) - { - img_new_sz.height = (int) ((double) img_new_sz.width * img_ar_height); - } else - { - img_new_sz.height = size.height.calc_percent(bg_paint.origin_box.height); - } - } - - bg_paint.image_size = img_new_sz; - bg_paint.position_x = bg_paint.origin_box.x + (int) position_x.calc_percent(bg_paint.origin_box.width - bg_paint.image_size.width); - bg_paint.position_y = bg_paint.origin_box.y + (int) position_y.calc_percent(bg_paint.origin_box.height - bg_paint.image_size.height); - } - } - bg_paint.border_radius = m_css.get_borders().radius.calc_percents(border_box.width, border_box.height); - bg_paint.border_box = border_box; - bg_paint.is_root = is_root(); -} - void litehtml::html_tag::draw_list_marker( uint_ptr hdc, const position& pos ) { list_marker lm; @@ -1238,7 +1062,7 @@ void litehtml::html_tag::draw_list_marker( uint_ptr hdc, const position& pos ) lm.baseurl = nullptr; } - int ln_height = css().get_line_height(); + int ln_height = css().line_height().computed_value; int sz_font = css().get_font_size(); lm.pos.x = pos.x; lm.pos.width = sz_font - sz_font * 2 / 3; @@ -1366,20 +1190,21 @@ litehtml::string litehtml::html_tag::get_list_marker_text(int index) } } -bool litehtml::html_tag::is_nth_child(const element::ptr& el, int num, int off, bool of_type) const +bool html_tag::is_nth_child(const element::ptr& el, int num, int off, bool of_type, const css_selector::vector& selector_list) const { int idx = 1; for(const auto& child : m_children) { if(child->css().get_display() != display_inline_text) { - if( (!of_type) || (of_type && el->tag() == child->tag()) ) + if( (!of_type && selector_list.empty()) || + (of_type && child->tag() == el->tag()) || child->select(selector_list) ) { if(el == child) { if(num != 0) { - if((idx - off) >= 0 && (idx - off) % num == 0) + if((idx - off) * num >= 0 && (idx - off) % num == 0) { return true; } @@ -1398,20 +1223,21 @@ bool litehtml::html_tag::is_nth_child(const element::ptr& el, int num, int off, return false; } -bool litehtml::html_tag::is_nth_last_child(const element::ptr& el, int num, int off, bool of_type) const +bool html_tag::is_nth_last_child(const element::ptr& el, int num, int off, bool of_type, const css_selector::vector& selector_list) const { int idx = 1; for(auto child = m_children.rbegin(); child != m_children.rend(); child++) { if((*child)->css().get_display() != display_inline_text) { - if( !of_type || (of_type && el->tag() == (*child)->tag()) ) + if( (!of_type && selector_list.empty()) || + (of_type && (*child)->tag() == el->tag()) || (*child)->select(selector_list) ) { if(el == (*child)) { if(num != 0) { - if((idx - off) >= 0 && (idx - off) % num == 0) + if((idx - off) * num >= 0 && (idx - off) % num == 0) { return true; } @@ -1556,21 +1382,21 @@ litehtml::element::ptr litehtml::html_tag::get_element_after(const style& style, void litehtml::html_tag::handle_counter_properties() { - const auto& reset_property = m_style.get_property(string_id::_counter_reset_); - if (reset_property.m_type == prop_type_string_vector) { + const auto& reset_property = m_style.get_property(_counter_reset_); + if (reset_property.is<string_vector>()) { auto reset_function = [&](const string_id&name_id, const int value) { reset_counter(name_id, value); }; - parse_counter_tokens(reset_property.m_string_vector, 0, reset_function); + parse_counter_tokens(reset_property.get<string_vector>(), 0, reset_function); return; } - const auto& inc_property = m_style.get_property(string_id::_counter_increment_); - if (inc_property.m_type == prop_type_string_vector) { + const auto& inc_property = m_style.get_property(_counter_increment_); + if (inc_property.is<string_vector>()) { auto inc_function = [&](const string_id&name_id, const int value) { increment_counter(name_id, value); }; - parse_counter_tokens(inc_property.m_string_vector, 1, inc_function); + parse_counter_tokens(inc_property.get<string_vector>(), 1, inc_function); return; } } @@ -1681,7 +1507,7 @@ const litehtml::background* litehtml::html_tag::get_background(bool own_only) } return nullptr; } - + if(is_body()) { element::ptr el_parent = parent(); @@ -1698,7 +1524,7 @@ const litehtml::background* litehtml::html_tag::get_background(bool own_only) return &m_css.get_bg(); } -litehtml::string litehtml::html_tag::dump_get_name() +string html_tag::dump_get_name() { if(m_tag == empty_id) { @@ -1706,3 +1532,59 @@ litehtml::string litehtml::html_tag::dump_get_name() } return _s(m_tag) + " [html_tag]"; } + +// https://html.spec.whatwg.org/multipage/rendering.html#maps-to-the-pixel-length-property +void html_tag::map_to_pixel_length_property(string_id prop_name, string attr_value) +{ + int n; + if (html_parse_non_negative_integer(attr_value, n)) + { + css_token tok(DIMENSION, (float)n, css_number_integer, "px"); + m_style.add_property(prop_name, {tok}); + } +} + +// https://html.spec.whatwg.org/multipage/rendering.html#tables-2:attr-table-border +void html_tag::map_to_pixel_length_property_with_default_value(string_id prop_name, string attr_value, int default_value) +{ + int n = default_value; + html_parse_non_negative_integer(attr_value, n); + css_token tok(DIMENSION, (float)n, css_number_integer, "px"); + m_style.add_property(prop_name, {tok}); +} + +// https://html.spec.whatwg.org/multipage/rendering.html#maps-to-the-dimension-property +void html_tag::map_to_dimension_property(string_id prop_name, string attr_value) +{ + float x = 0; + html_dimension_type type = html_length; + if (!html_parse_dimension_value(attr_value, x, type)) + return; + + css_token tok; + if (type == html_length) + tok = {DIMENSION, x, css_number_number, "px"}; + else + tok = {PERCENTAGE, x, css_number_number}; + + m_style.add_property(prop_name, {tok}); +} + +// https://html.spec.whatwg.org/multipage/rendering.html#maps-to-the-dimension-property-(ignoring-zero) +void html_tag::map_to_dimension_property_ignoring_zero(string_id prop_name, string attr_value) +{ + float x = 0; + html_dimension_type type = html_length; + if (!html_parse_nonzero_dimension_value(attr_value, x, type)) + return; + + css_token tok; + if (type == html_length) + tok = {DIMENSION, x, css_number_number, "px"}; + else + tok = {PERCENTAGE, x, css_number_number}; + + m_style.add_property(prop_name, {tok}); +} + +} // namespace litehtml diff --git a/src/internal.h b/src/internal.h new file mode 100644 index 000000000..9b53a436d --- /dev/null +++ b/src/internal.h @@ -0,0 +1,26 @@ +#ifndef LH_INTERNAL_H +#define LH_INTERNAL_H +// internal.h should not be included in header files +// internal.h should be included after all other headers in a source file + +namespace litehtml +{ + +template<class T, class TT> +bool operator/(const T& x, const TT& xx) +{ + return contains(xx, x); +} +// a in b if b contains a +#define in / + +/* Limitations of overloaded operators compared to regular function calls: +* 1. at least one operand must be a class, so cannot just write `ch in "abc"` +* (possible solution: ch in "abc"_s) +* 2. operand cannot be initializer list (exception: assignment ops), so cannot just write `ch in {'a','b','c'}` +* (possible solution: ch in ${'a','b','c'}) +*/ + +} // namespace litehtml + +#endif // LH_INTERNAL_H \ No newline at end of file diff --git a/src/iterators.cpp b/src/iterators.cpp index 5b3238ad2..3d48e463d 100644 --- a/src/iterators.cpp +++ b/src/iterators.cpp @@ -1,13 +1,10 @@ -#include "html.h" #include "iterators.h" -#include "html_tag.h" #include "render_item.h" -#include <iterator> litehtml::elements_iterator::elements_iterator(bool return_parents, iterator_selector* go_inside, iterator_selector* select) : - m_return_parent(return_parents), - m_go_inside(go_inside), - m_select(select) + m_go_inside(go_inside), + m_select(select), + m_return_parent(return_parents) { } diff --git a/src/line_box.cpp b/src/line_box.cpp index e739e63c8..20aed39fb 100644 --- a/src/line_box.cpp +++ b/src/line_box.cpp @@ -6,6 +6,8 @@ ////////////////////////////////////////////////////////////////////////////////////////// +litehtml::line_box_item::~line_box_item() = default; + void litehtml::line_box_item::place_to(int x, int y) { m_element->pos().x = x + m_element->content_offset_left(); @@ -43,6 +45,11 @@ int litehtml::line_box_item::left() const return m_element->left(); } +int litehtml::line_box_item::height() const +{ + return m_element->height(); +} + ////////////////////////////////////////////////////////////////////////////////////////// litehtml::lbi_start::lbi_start(const std::shared_ptr<render_item>& element) : line_box_item(element) @@ -51,6 +58,8 @@ litehtml::lbi_start::lbi_start(const std::shared_ptr<render_item>& element) : li m_pos.width = m_element->content_offset_left(); } +litehtml::lbi_start::~lbi_start() = default; + void litehtml::lbi_start::place_to(int x, int y) { m_pos.x = x + m_element->content_offset_left(); @@ -82,6 +91,11 @@ int litehtml::lbi_start::left() const return m_pos.x - m_element->content_offset_left(); } +int litehtml::lbi_start::height() const +{ + return m_pos.height; +} + ////////////////////////////////////////////////////////////////////////////////////////// litehtml::lbi_end::lbi_end(const std::shared_ptr<render_item>& element) : lbi_start(element) @@ -90,6 +104,8 @@ litehtml::lbi_end::lbi_end(const std::shared_ptr<render_item>& element) : lbi_st m_pos.width = m_element->content_offset_right(); } +litehtml::lbi_end::~lbi_end() = default; + void litehtml::lbi_end::place_to(int x, int y) { m_pos.x = x; @@ -114,6 +130,8 @@ litehtml::lbi_continue::lbi_continue(const std::shared_ptr<render_item>& element m_pos.width = 0; } +litehtml::lbi_continue::~lbi_continue() = default; + void litehtml::lbi_continue::place_to(int x, int y) { m_pos.x = x; @@ -172,9 +190,9 @@ int litehtml::line_box::calc_va_baseline(const va_context& current, vertical_ali switch(va) { case va_super: - return current.baseline - current.fm.height / 3; + return current.baseline - current.fm.super_shift; case va_sub: - return current.baseline + current.fm.height / 3; + return current.baseline + current.fm.sub_shift; case va_middle: return current.baseline - current.fm.x_height / 2; case va_text_top: @@ -182,10 +200,10 @@ int litehtml::line_box::calc_va_baseline(const va_context& current, vertical_ali new_font.height - new_font.base_line(); case va_text_bottom: return current.baseline + current.fm.base_line() - new_font.base_line(); + case va_bottom: + return bottom - new_font.base_line(); case va_top: return top + new_font.height - new_font.base_line(); - case va_bottom: - return bottom - new_font.height + new_font.base_line(); default: return current.baseline; } @@ -267,130 +285,236 @@ std::list< std::unique_ptr<litehtml::line_box_item> > litehtml::line_box::finish if( is_empty() || (!is_empty() && last_box && is_break_only()) ) { - m_height = m_default_line_height; + m_height = m_default_line_height.computed_value; m_baseline = m_font_metrics.base_line(); return ret_items; } - int spc_x = 0; + int spacing_x = 0; // Number of pixels to distribute between elements + int shift_x = 0; // Shift elements by X to apply the text-align - int add_x = 0; switch(m_text_align) { case text_align_right: if(m_width < (m_right - m_left)) { - add_x = (m_right - m_left) - m_width; + shift_x = (m_right - m_left) - m_width; } break; case text_align_center: if(m_width < (m_right - m_left)) { - add_x = ((m_right - m_left) - m_width) / 2; + shift_x = ((m_right - m_left) - m_width) / 2; } break; case text_align_justify: if (m_width < (m_right - m_left)) { - add_x = 0; - spc_x = (m_right - m_left) - m_width; - if (spc_x > m_width/4) - spc_x = 0; + shift_x = 0; + spacing_x = (m_right - m_left) - m_width; + // don't justify for small lines + if (spacing_x > m_width / 4) + spacing_x = 0; } break; default: - add_x = 0; + shift_x = 0; } int counter = 0; - float offj = float(spc_x) / std::max(1.f, float(m_items.size())-1.f); + float offj = float(spacing_x) / std::max(1.f, float(m_items.size()) - 1.f); float cixx = 0.0f; - int line_top = 0; - int line_bottom = 0; + std::optional<int> line_height; + + if(!m_default_line_height.css_value.is_predefined()) + { + line_height = m_default_line_height.computed_value; + } va_context current_context; std::list<va_context> contexts; current_context.baseline = 0; current_context.fm = m_font_metrics; + current_context.start_lbi = nullptr; + current_context.line_height = m_default_line_height.computed_value; m_min_width = 0; + struct items_dimensions + { + int top = 0; + int bottom = 0; + int count = 0; + int max_height = 0; + + void add_item(const line_box_item* item) + { + top = std::min(top, item->top()); + bottom = std::max(bottom, item->bottom()); + max_height = std::max(max_height, item->height()); + count++; + } + int height() const { return bottom - top; } + }; + + items_dimensions line_max_height; + items_dimensions top_aligned_max_height; + items_dimensions bottom_aligned_max_height; + items_dimensions inline_boxes_dims; + + // First pass + // 1. Apply text-align-justify + // 1. Align all items by baseline + // 2. top/button aligned items are aligned by baseline + // 3. Calculate top and button of the linebox separately for items in baseline + // and for top and bottom aligned items for (const auto& lbi : m_items) - { + { + // Apply text-align-justify m_min_width += lbi->get_rendered_min_width(); - { // start text_align_justify - if (spc_x && counter) + if (spacing_x && counter) + { + cixx += offj; + if ((counter + 1) == int(m_items.size())) + cixx += 0.99f; + lbi->pos().x += int(cixx); + } + counter++; + if ((m_text_align == text_align_right || spacing_x) && counter == int(m_items.size())) + { + // Forcible justify the last element to the right side for text align right and justify; + lbi->pos().x = m_right - lbi->pos().width; + } else if (shift_x) + { + lbi->pos().x += shift_x; + } + + // Calculate new baseline for inline start/continue + // Inline start/continue elements are inline containers like <span> + if (lbi->get_type() == line_box_item::type_inline_start || lbi->get_type() == line_box_item::type_inline_continue) + { + contexts.push_back(current_context); + if(is_one_of(lbi->get_el()->css().get_vertical_align(), va_top, va_bottom)) { - cixx += offj; - if ((counter + 1) == int(m_items.size())) - cixx += 0.99f; - lbi->pos().x += int(cixx); - } - counter++; - if ((m_text_align == text_align_right || spc_x) && counter == int(m_items.size())) + // top/bottom aligned inline boxes are aligned by baseline == 0 + current_context.baseline = 0; + current_context.start_lbi = lbi.get(); + current_context.start_lbi->reset_items_height(); + } else if(current_context.start_lbi) { - // Forcible justify the last element to the right side for text align right and justify; - lbi->pos().x = m_right - lbi->pos().width; - } else if (add_x) + current_context.baseline = calc_va_baseline(current_context, + lbi->get_el()->css().get_vertical_align(), + lbi->get_el()->css().get_font_metrics(), + current_context.start_lbi->top(), current_context.start_lbi->bottom()); + } else { - lbi->pos().x += add_x; + current_context.start_lbi = nullptr; + current_context.baseline = calc_va_baseline(current_context, + lbi->get_el()->css().get_vertical_align(), + lbi->get_el()->css().get_font_metrics(), + line_max_height.top, line_max_height.bottom); } - } // end text_align_justify + current_context.fm = lbi->get_el()->css().get_font_metrics(); + current_context.line_height = lbi->get_el()->css().line_height().computed_value; + } - if (lbi->get_type() == line_box_item::type_inline_start || lbi->get_type() == line_box_item::type_inline_continue) + int bl = current_context.baseline; + int content_offset = 0; + bool is_top_bottom_box = false; + bool ignore = false; + + // Align element by baseline + if(!is_one_of(lbi->get_el()->src_el()->css().get_display(), display_inline_text, display_inline)) { - contexts.push_back(current_context); - current_context.baseline = calc_va_baseline(current_context, - lbi->get_el()->css().get_vertical_align(), - lbi->get_el()->css().get_font_metrics(), - line_top, line_bottom); - current_context.fm = lbi->get_el()->css().get_font_metrics(); + // Apply margins, paddings and border for inline boxes + content_offset = lbi->get_el()->content_offset_top(); + switch (lbi->get_el()->css().get_vertical_align()) + { + case va_bottom: + case va_top: + // Align by base line 0 all inline boxes with top and bottom vertical aling + bl = 0; + is_top_bottom_box = true; + break; + + case va_text_bottom: + lbi->pos().y = bl + current_context.fm.base_line() - lbi->get_el()->height() + content_offset; + ignore = true; + break; + + case va_text_top: + lbi->pos().y = bl - current_context.fm.ascent + content_offset; + ignore = true; + break; + + case va_middle: + lbi->pos().y = bl - current_context.fm.x_height / 2 - lbi->get_el()->height() / 2 + content_offset; + ignore = true; + break; + + default: + bl = calc_va_baseline(current_context, + lbi->get_el()->css().get_vertical_align(), + lbi->get_el()->css().get_font_metrics(), + line_max_height.top, line_max_height.bottom); + break; + } + } + if(!ignore) + { + lbi->pos().y = bl - lbi->get_el()->get_last_baseline() + content_offset; } - // Align elements vertically by baseline. - if(lbi->get_el()->src_el()->css().get_display() == display_inline_text || lbi->get_el()->src_el()->css().get_display() == display_inline) - { - // inline elements and text are aligned by baseline only - // at this point the baseline for text is properly aligned already - lbi->pos().y = current_context.baseline - lbi->get_el()->css().get_font_metrics().height + lbi->get_el()->css().get_font_metrics().base_line(); - } else - { - switch(lbi->get_el()->css().get_vertical_align()) - { - case va_sub: - case va_super: - { - int bl = calc_va_baseline(current_context, lbi->get_el()->css().get_vertical_align(), current_context.fm, line_top, line_bottom); - lbi->pos().y = bl - lbi->get_el()->get_last_baseline() + - lbi->get_el()->content_offset_top(); - } + if(is_top_bottom_box) + { + switch (lbi->get_el()->css().get_vertical_align()) + { + case va_top: + top_aligned_max_height.add_item(lbi.get()); break; case va_bottom: - lbi->pos().y = line_bottom - lbi->get_el()->height() + lbi->get_el()->content_offset_top(); + bottom_aligned_max_height.add_item(lbi.get()); + break; + default: break; + } + } else if(current_context.start_lbi) + { + current_context.start_lbi->add_item_height(lbi->top(), lbi->bottom()); + switch (current_context.start_lbi->get_el()->css().get_vertical_align()) + { case va_top: - lbi->pos().y = line_top + lbi->get_el()->content_offset_top(); + top_aligned_max_height.add_item(lbi.get()); break; - case va_baseline: - lbi->pos().y = current_context.baseline - lbi->get_el()->get_last_baseline() + - lbi->get_el()->content_offset_top(); - break; - case va_text_top: - lbi->pos().y = current_context.baseline - current_context.fm.height + current_context.fm.base_line() + - lbi->get_el()->content_offset_top(); - break; - case va_text_bottom: - lbi->pos().y = current_context.baseline + current_context.fm.base_line() - lbi->get_el()->height() + - lbi->get_el()->content_offset_top(); + case va_bottom: + bottom_aligned_max_height.add_item(lbi.get()); break; - case va_middle: - lbi->pos().y = current_context.baseline - current_context.fm.x_height / 2 - lbi->get_el()->height() / 2 + - lbi->get_el()->content_offset_top(); - break; - } - } + default: + break; + } + } else + { + if(!lbi->get_el()->src_el()->is_inline_box()) + { + line_max_height.add_item(lbi.get()); + } else + { + inline_boxes_dims.add_item(lbi.get()); + } + } + + if(!lbi->get_el()->src_el()->is_inline_box() && !lbi->get_el()->css().line_height().css_value.is_predefined()) + { + if(line_height.has_value()) + { + line_height = std::max(line_height.value(), lbi->get_el()->css().line_height().computed_value); + } else + { + line_height = lbi->get_el()->css().line_height().computed_value; + } + } if (lbi->get_type() == line_box_item::type_inline_end) { @@ -400,25 +524,68 @@ std::list< std::unique_ptr<litehtml::line_box_item> > litehtml::line_box::finish contexts.pop_back(); } } + } - // calculate line height - line_top = std::min(line_top, lbi->top()); - line_bottom = std::max(line_bottom, lbi->bottom()); - - if(lbi->get_el()->src_el()->css().get_display() == display_inline_text) + int top_shift = 0; + if(line_height.has_value()) + { + m_height = line_height.value(); + if(line_max_height.count != 0) + { + // We have inline items + top_shift = std::abs(line_max_height.top); + const int top_shift_correction = (line_height.value() - line_max_height.height()) / 2; + m_baseline = line_max_height.bottom + top_shift_correction; + top_shift += top_shift_correction; + if(inline_boxes_dims.count) + { + const int diff2 = std::abs(inline_boxes_dims.top) - std::abs(top_shift); + if(diff2 > 0) + { + m_height += diff2; + top_shift += diff2; + m_baseline += diff2; + } + const int diff1 = inline_boxes_dims.bottom - (line_max_height.bottom + top_shift_correction); + if(diff1 > 0) + { + m_height += diff1; + } + } + } else if(inline_boxes_dims.count != 0) { - m_line_height = std::max(m_line_height, lbi->get_el()->css().get_line_height()); + // We have inline boxes only + m_height = inline_boxes_dims.height(); + top_shift = std::abs(inline_boxes_dims.top); + m_baseline = inline_boxes_dims.bottom; + } else + { + // We don't have inline items and inline boxes + top_shift = 0; } - } - m_height = line_bottom - line_top; - int top_shift = line_top; - if(m_height < m_line_height) + + const int top_down_height = std::max(top_aligned_max_height.max_height, bottom_aligned_max_height.max_height); + if(top_down_height > m_height) + { + if(bottom_aligned_max_height.count) + { + top_shift += bottom_aligned_max_height.height() - m_height; + } + m_height = top_down_height; + } + } else { - top_shift -= (m_line_height - m_height) / 2; - m_height = m_line_height; + // Add inline boxes dimentions + line_max_height.top = std::min(line_max_height.top, inline_boxes_dims.top); + line_max_height.bottom = std::max(line_max_height.bottom, inline_boxes_dims.bottom); + + // Height is maximum from inline elements height and top/bottom aligned elements height + m_height = std::max({line_max_height.height(), top_aligned_max_height.height(), bottom_aligned_max_height.height()}); + + top_shift = -std::min(line_max_height.top, line_max_height.bottom - bottom_aligned_max_height.height()); + m_baseline = line_max_height.bottom; } - m_baseline = line_bottom; struct inline_item_box { @@ -435,37 +602,30 @@ std::list< std::unique_ptr<litehtml::line_box_item> > litehtml::line_box::finish current_context.baseline = 0; current_context.fm = m_font_metrics; - bool va_top_bottom = false; + current_context.start_lbi = nullptr; + //int va_top_bottom = 0; + // Second pass: + // 1. Vertical align top/bottom + // 2. Apply relative shift + // 3. Calculate inline boxes for (const auto& lbi : m_items) { - // Calculate baseline. Now we calculate baseline for vertical alignment top and bottom - if (lbi->get_type() == line_box_item::type_inline_start || lbi->get_type() == line_box_item::type_inline_continue) + if(is_one_of(lbi->get_type(), line_box_item::type_inline_start, line_box_item::type_inline_continue)) { contexts.push_back(current_context); - va_top_bottom = lbi->get_el()->css().get_vertical_align() == va_bottom || lbi->get_el()->css().get_vertical_align() == va_top; - current_context.baseline = calc_va_baseline(current_context, - lbi->get_el()->css().get_vertical_align(), - lbi->get_el()->css().get_font_metrics(), - top_shift, top_shift + m_height); current_context.fm = lbi->get_el()->css().get_font_metrics(); - } - // Align inlines and text by baseline if current vertical alignment is top or bottom - if(va_top_bottom) - { - if (lbi->get_el()->src_el()->css().get_display() == display_inline_text || - lbi->get_el()->src_el()->css().get_display() == display_inline) + if(lbi->get_el()->css().get_vertical_align() == va_top) + { + current_context.baseline = m_top - lbi->get_items_top(); + current_context.start_lbi = lbi.get(); + } else if(lbi->get_el()->css().get_vertical_align() == va_bottom) { - // inline elements and text are aligned by baseline only - // at this point the baseline for text is properly aligned already - lbi->pos().y = current_context.baseline - lbi->get_el()->css().get_font_metrics().height + - lbi->get_el()->css().get_font_metrics().base_line(); + current_context.baseline = m_top + m_height - lbi->get_items_bottom(); + current_context.start_lbi = lbi.get(); } - } - - // Pop the prev context - if (lbi->get_type() == line_box_item::type_inline_end) + } else if(lbi->get_type() == line_box_item::type_inline_end) { if(!contexts.empty()) { @@ -474,20 +634,25 @@ std::list< std::unique_ptr<litehtml::line_box_item> > litehtml::line_box::finish } } - // move element to the correct position - lbi->pos().y += m_top - top_shift; - - // Perform vertical align top and bottom for inline boxes - if(lbi->get_el()->css().get_display() != display_inline_text && lbi->get_el()->css().get_display() != display_inline) - { - if(lbi->get_el()->css().get_vertical_align() == va_top) + if(current_context.start_lbi) + { + lbi->pos().y = current_context.baseline - lbi->get_el()->get_last_baseline() + + lbi->get_el()->content_offset_top(); + } else if(is_one_of(lbi->get_el()->css().get_vertical_align(), va_top, va_bottom) && lbi->get_type() == line_box_item::type_text_part) + { + if(lbi->get_el()->css().get_vertical_align() == va_top) { lbi->pos().y = m_top + lbi->get_el()->content_offset_top(); - } else if(lbi->get_el()->css().get_vertical_align() == va_bottom) + } else { - lbi->pos().y = m_top + m_height - lbi->get_el()->height() + lbi->get_el()->content_offset_top(); + lbi->pos().y = m_top + m_height - (lbi->bottom() - lbi->top()) + lbi->get_el()->content_offset_bottom(); } - } + } else + { + // move element to the correct position + lbi->pos().y += m_top + top_shift; + } + lbi->get_el()->apply_relative_shift(containing_block_size); // Calculate and push inline box into the render item element @@ -520,7 +685,7 @@ std::list< std::unique_ptr<litehtml::line_box_item> > litehtml::line_box::finish ret_items.emplace_front(std::unique_ptr<line_box_item>(new lbi_continue(iter->element))); } - return std::move(ret_items); + return ret_items; } std::shared_ptr<litehtml::render_item> litehtml::line_box::get_first_text_part() const @@ -556,7 +721,7 @@ bool litehtml::line_box::can_hold(const std::unique_ptr<line_box_item>& item, wh if(item->get_type() == line_box_item::type_text_part) { // force new line on floats clearing - if (item->get_el()->src_el()->is_break() && item->get_el()->src_el()->css().get_clear() != clear_none) + if (item->get_el()->src_el()->is_break() && item->get_el()->css().get_clear() != clear_none) { return false; } @@ -570,8 +735,8 @@ bool litehtml::line_box::can_hold(const std::unique_ptr<line_box_item>& item, wh } // force new line if the last placed element was line break - // Skip If there are the only break item - this is float clearing - if (last_el && last_el->src_el()->is_break() && m_items.size() > 1) + // Skip If the break item is float clearing + if (last_el && last_el->src_el()->is_break() && last_el->css().get_clear() == clear_none) { return false; } @@ -695,11 +860,9 @@ std::list< std::unique_ptr<litehtml::line_box_item> > litehtml::line_box::new_wi { remove_begin = i; break; - } else - { - (*i)->pos().x += add; - m_width += (*i)->get_el()->width(); } + (*i)->pos().x += add; + m_width += (*i)->get_el()->width(); } i++; } @@ -714,4 +877,3 @@ std::list< std::unique_ptr<litehtml::line_box_item> > litehtml::line_box::new_wi } return ret_items; } - diff --git a/src/media_query.cpp b/src/media_query.cpp index 8c2afff27..5165bd9ff 100644 --- a/src/media_query.cpp +++ b/src/media_query.cpp @@ -1,430 +1,682 @@ #include "html.h" #include "media_query.h" +#include "css_parser.h" +#include <cassert> #include "document.h" +#include "document_container.h" -litehtml::media_query::media_query() +namespace litehtml { - m_media_type = media_type_all; - m_not = false; + +bool eval_op(float x, short op, float value) +{ + const float epsilon = 0.00001f; + if (abs(x - value) < epsilon) + { + if (is_one_of(op, '=', u'⩾', u'⩽')) return true; + if (op == u'≠') return false; + } + + switch (op) + { + case '<': return x < value; + case u'⩽': return x <= value; + case '>': return x > value; + case u'⩾': return x >= value; + case '=': return x == value; + case u'≠': return x != value; + default: return false; + } } -litehtml::media_query::media_query( const media_query& val ) +bool media_feature::compare(float x) const { - m_not = val.m_not; - m_expressions = val.m_expressions; - m_media_type = val.m_media_type; + if (!op2) return eval_op(x, op, value); + return eval_op(value, op, x) && eval_op(x, op2, value2); } -litehtml::media_query::ptr litehtml::media_query::create_from_string(const string& str, const std::shared_ptr<document>& doc) +bool media_feature::check(const media_features& feat) const { - media_query::ptr query = std::make_shared<media_query>(); + switch (_id(name)) + { + case _width_: + return compare(feat.width); + case _height_: + return compare(feat.height); + case _device_width_: + return compare(feat.device_width); + case _device_height_: + return compare(feat.device_height); + case _orientation_: + return compare(feat.height >= feat.width ? _portrait_ : _landscape_); + case _aspect_ratio_: + // The standard calls 1/0 "a degenerate ratio" https://drafts.csswg.org/css-values-4/#ratio-value, + // but it doesn't specify exactly how it behaves in this context: https://drafts.csswg.org/mediaqueries-5/#aspect-ratio. + // Chrome/Firefox work as expected with 1/0, for example when both width and height are nonzero + // (aspect-ratio < 1/0) evaluates to true. But they behave the same for 0/0, which is unexpected + // (0/0 is NaN, so any comparisons should evaluate to false). + // 0/1 is also degenerate according to the standard. + return feat.height ? compare(float(feat.width) / feat.height) : false; + case _device_aspect_ratio_: + return feat.device_height ? compare(float(feat.device_width) / feat.device_height) : false; + case _color_: + return compare(feat.color); + case _color_index_: + return compare(feat.color_index); + case _monochrome_: + return compare(feat.monochrome); + case _resolution_: + return compare(feat.resolution); + default: + assert(0); // must never happen, unknown media features are handled in parse_media_feature + return false; + } +} - string_vector tokens; - split_string(str, tokens, " \t\r\n", "", "("); +trilean media_condition::check(const media_features& features) const +{ + if (op == _not_) + return not m_conditions[0].check(features); - for(auto & token : tokens) + if (op == _and_) { - if(token == "not") - { - query->m_not = true; - } else if(token.at(0) == '(') + // https://drafts.csswg.org/mediaqueries/#evaluating + // The result is true if the <media-in-parens> child term and all of the <media-in-parens> + // children of the <media-and> child terms are true, false if at least one of these + // <media-in-parens> terms are false, and unknown otherwise. + trilean result = True; + for (const auto& condition : m_conditions) { - token.erase(0, 1); - if(!token.empty() && token.at(token.length() - 1) == ')') - { - token.erase(token.length() - 1, 1); - } - media_query_expression expr; - string_vector expr_tokens; - split_string(token, expr_tokens, ":"); - if(!expr_tokens.empty()) - { - trim(expr_tokens[0]); - expr.feature = (media_feature) value_index(expr_tokens[0], media_feature_strings, media_feature_none); - if(expr.feature != media_feature_none) - { - if(expr_tokens.size() == 1) - { - expr.check_as_bool = true; - } else - { - trim(expr_tokens[1]); - expr.check_as_bool = false; - if(expr.feature == media_feature_orientation) - { - expr.val = value_index(expr_tokens[1], media_orientation_strings, media_orientation_landscape); - } else - { - string::size_type slash_pos = expr_tokens[1].find('/'); - if( slash_pos != string::npos ) - { - string val1 = expr_tokens[1].substr(0, slash_pos); - string val2 = expr_tokens[1].substr(slash_pos + 1); - trim(val1); - trim(val2); - expr.val = atoi(val1.c_str()); - expr.val2 = atoi(val2.c_str()); - } else - { - css_length length; - length.fromString(expr_tokens[1]); - if(length.units() == css_units_dpcm || length.units() == css_units_dpi) - { - expr.val = (int) (length.val() * 2.54); - } else - { - if(doc) - { - doc->cvt_units(length, doc->container()->get_default_font_size()); - } - expr.val = (int) length.val(); - } - } - } - } - query->m_expressions.push_back(expr); - } - } - } else + result = result && condition.check(features); + if (result == False) return result; // no need to check further + } + return result; + } + if (op == _or_) + { + // The result is false if the <media-in-parens> child term and all of the <media-in-parens> + // children of the <media-or> child terms are false, true if at least one of these + // <media-in-parens> terms are true, and unknown otherwise. + trilean result = False; + for (const auto& condition : m_conditions) { - query->m_media_type = (media_type) value_index(token, media_type_strings, media_type_none); + result = result || condition.check(features); + if (result == True) return result; // no need to check further } + return result; } + return False; +} - return query; +trilean media_in_parens::check(const media_features& features) const +{ + if (is<media_condition>()) return get<media_condition>().check(features); + if (is<media_feature>()) return (trilean)get<media_feature>().check(features); + // <general-enclosed> https://drafts.csswg.org/mediaqueries/#evaluating + return Unknown; } -bool litehtml::media_query::check( const media_features& features ) const +trilean media_query::check(const media_features& features) const { - bool res = false; - if(m_media_type == media_type_all || m_media_type == features.type) + trilean result; + // https://drafts.csswg.org/mediaqueries/#media-types + // User agents must recognize the following media types as valid, but must make them match nothing. + if (m_media_type >= media_type_first_deprecated) + result = False; + else if (m_media_type == media_type_unknown) + result = False; + else if (m_media_type == media_type_all) + result = True; + else + result = (trilean)(m_media_type == features.type); + + if (result == True) { - res = true; - for(auto expression : m_expressions) + for (const auto& condition : m_conditions) { - if(!expression.check(features)) - { - res = false; - break; - } + result = result && condition.check(features); + if (result == False) break; // no need to check further } } - if(m_not) + if (m_not) result = not result; + + return result; +} + +// https://drafts.csswg.org/mediaqueries/#mq-list +bool media_query_list::check(const media_features& features) const +{ + if (empty()) return true; // An empty media query list evaluates to true. + + trilean result = False; + for (const auto& query : m_queries) { - res = !res; + result = result || query.check(features); + if (result == True) break; // no need to check further } - return res; + // https://drafts.csswg.org/mediaqueries/#evaluating + // If the result ... is used in any context that expects a two-valued boolean, “unknown” must be converted to “false”. + return result == True; } -////////////////////////////////////////////////////////////////////////// +// nested @media rules: https://drafts.csswg.org/css-conditional-3/#processing +// all of them must be true for style rules to apply +bool media_query_list_list::apply_media_features(const media_features& features) +{ + bool apply = true; -litehtml::media_query_list::ptr litehtml::media_query_list::create_from_string(const string& str, const std::shared_ptr<document>& doc) + for (const auto& mq_list: m_media_query_lists) + { + if (!mq_list.check(features)) + { + apply = false; + break; + } + } + + bool ret = (apply != m_is_used); + m_is_used = apply; + return ret; +} + + +bool parse_media_query(const css_token_vector& tokens, media_query& mquery, document::ptr doc); + +// https://drafts.csswg.org/mediaqueries-5/#typedef-media-query-list +media_query_list parse_media_query_list(const css_token_vector& _tokens, document::ptr doc) +{ + auto keep_whitespace = [](auto left_token, auto right_token) + { + return is_one_of(left_token.ch, '<', '>') && right_token.ch == '='; + }; + css_token_vector tokens = normalize(_tokens, f_componentize | f_remove_whitespace, keep_whitespace); + // this needs special treatment because empty media query list evaluates to true + if (tokens.empty()) return {}; + + media_query_list result; + auto list_of_lists = parse_comma_separated_list(tokens); + for (const auto& list : list_of_lists) + { + media_query query; + parse_media_query(list, query, doc); + // Note: appending even if media query failed to parse, as per standard. + result.m_queries.push_back(query); + } + return result; +} + +media_query_list parse_media_query_list(const string& str, shared_ptr<document> doc) { - media_query_list::ptr list = std::make_shared<media_query_list>(); + auto tokens = normalize(str); + return parse_media_query_list(tokens, doc); +} - string_vector tokens; - split_string(str, tokens, ","); +bool parse_media_condition(const css_token_vector& tokens, int& index, bool or_allowed, media_condition& condition, document::ptr doc); - for(auto & token : tokens) +// <media-query> = <media-condition> | [ not | only ]? <media-type> [ and <media-condition-without-or> ]? +bool parse_media_query(const css_token_vector& tokens, media_query& mquery, document::ptr doc) +{ + if (tokens.empty()) return false; + int index = 0; + auto end = [&]() { return index == (int)tokens.size(); }; + + media_condition condition; + if (parse_media_condition(tokens, index, true, condition, doc) && end()) { - trim(token); - lcase(token); + mquery.m_not = false; + mquery.m_media_type = media_type_all; + mquery.m_conditions = {condition}; + return true; + } - litehtml::media_query::ptr query = media_query::create_from_string(token, doc); - if(query) - { - list->m_queries.push_back(query); - } + string ident = tokens[0].ident(); + bool _not = false; + if (ident == "not") index++, _not = true; + else if (ident == "only") index++; // ignored https://drafts.csswg.org/mediaqueries-5/#mq-only + + // <media-type> = <ident> except only, not, and, or, and layer + ident = at(tokens, index).ident(); + if (is_one_of(ident, "", "only", "not", "and", "or", "layer")) + return false; + int idx = value_index(ident, media_type_strings); + int media_type = idx == -1 ? media_type_unknown : idx + 1; + index++; + + if (at(tokens, index).ident() == "and") + { + index++; + if (!parse_media_condition(tokens, index, false, condition, doc) || !end()) + return false; + mquery.m_conditions = {condition}; } - if(list->m_queries.empty()) + if (!end()) return false; + mquery.m_not = _not; + mquery.m_media_type = (litehtml::media_type) media_type; + return true; +} + +bool parse_media_in_parens(const css_token& token, media_in_parens& media_in_parens, document::ptr doc); + +// <media-condition> = <media-not> | <media-in-parens> [ <media-and>* | <media-or>* ] +// <media-condition-without-or> = <media-not> | <media-in-parens> <media-and>* +// <media-not> = not <media-in-parens> +bool parse_media_condition(const css_token_vector& tokens, int& index, bool _or_allowed, media_condition& condition, document::ptr doc) +{ + media_in_parens media_in_parens; + if (at(tokens, index).ident() == "not") { - list = nullptr; + if (!parse_media_in_parens(at(tokens, index + 1), media_in_parens, doc)) return false; + condition.op = _not_; + condition.m_conditions = {media_in_parens}; + index += 2; + return true; } - return list; + if (!parse_media_in_parens(at(tokens, index), media_in_parens, doc)) return false; + condition.m_conditions = {media_in_parens}; + index++; + + bool or_allowed = _or_allowed; + bool and_allowed = true; + while (true) + { + string ident = at(tokens, index).ident(); + if (ident == "and" && and_allowed) condition.op = _and_, or_allowed = false; + else if (ident == "or" && or_allowed) condition.op = _or_, and_allowed = false; + else return true; + index++; + + if (!parse_media_in_parens(at(tokens, index), media_in_parens, doc)) return false; + condition.m_conditions.push_back(media_in_parens); + index++; + } } -bool litehtml::media_query_list::apply_media_features( const media_features& features ) +bool parse_media_feature(const css_token& token, media_feature& media_feature, document::ptr doc); + +// https://drafts.csswg.org/mediaqueries-5/#typedef-media-in-parens +// <media-in-parens> = ( <media-condition> ) | <media-feature> | <general-enclosed> +// <general-enclosed> = [ <function-token> <any-value>? ) ] | [ ( <any-value>? ) ] +bool parse_media_in_parens(const css_token& token, media_in_parens& media_in_parens, document::ptr doc) { - bool apply = false; - - for(auto & query : m_queries) + if (token.type == CV_FUNCTION) { - if(query->check(features)) - { - apply = true; - break; - } + if (!token.value.empty() && !is_any_value(token.value)) + return false; + media_in_parens = unknown(); + return true; } - bool ret = (apply != m_is_used); - m_is_used = apply; - return ret; + if (token.type != ROUND_BLOCK) return false; + const css_token_vector& tokens = token.value; + + int index = 0; + media_condition condition; + media_feature media_feature; + if (parse_media_condition(tokens, index, true, condition, doc) && index == (int)tokens.size()) + media_in_parens = condition; + else if (parse_media_feature(token, media_feature, doc)) + media_in_parens = media_feature; + else if (!tokens.empty() && !is_any_value(tokens)) + return false; + else + media_in_parens = unknown(); + return true; } -bool litehtml::media_query_expression::check( const media_features& features ) const +bool parse_mf_value(const css_token_vector& tokens, int& index, css_token val[2]); +bool parse_mf_range(const css_token_vector& tokens, media_feature& media_feature, document::ptr doc); + +// https://drafts.csswg.org/mediaqueries/#mq-ranges +// Every media feature defines its “type” as either “range” or “discrete” in its definition table. +// The only significant difference between the two types is that “range” media features can be evaluated in a range context and accept “min-” and “max-” prefixes on their name. + +// https://drafts.csswg.org/mediaqueries/#mq-min-max +// “Discrete” type properties do not accept “min-” or “max-” prefixes. Adding such a prefix to a “discrete” type media feature simply results in an unknown feature name. +// For example, (min-grid: 1) is invalid, because grid is a “discrete” media feature, and so doesn’t accept the prefixes. (Even though the grid media feature appears to be numeric, as it accepts the values 0 and 1.) + +struct mf_info +{ + string_id type = empty_id; // range, discrete + string_id value_type = empty_id; // length, ratio, resolution, integer, keyword + vector<string_id> keywords = {}; // default value is specified here to get rid of gcc warning "missing initializer for member" + + operator bool() { return type != empty_id; } +}; + +std::map<string, mf_info> supported_media_features = +{ + //////////////////////////////////////////////// + // 4. Viewport/Page Dimensions Media Features + //////////////////////////////////////////////// + + // https://drafts.csswg.org/mediaqueries/#width + // For continuous media, this is the width of the viewport. + {"width", {_range_, _length_}}, + {"height", {_range_, _length_}}, + + // https://drafts.csswg.org/mediaqueries/#aspect-ratio + // width/height + {"aspect-ratio", {_range_, _ratio_}}, + + // https://drafts.csswg.org/mediaqueries/#orientation + {"orientation", {_discrete_, _keyword_, {_portrait_, _landscape_}}}, + + //////////////////////////////////////////////// + // 5. Display Quality Media Features + //////////////////////////////////////////////// + + // https://drafts.csswg.org/mediaqueries/#resolution + // https://developer.mozilla.org/en-US/docs/Web/CSS/@media/resolution + {"resolution", {_range_, _resolution_}}, + + //////////////////////////////////////////////// + // 6. Color Media Features + //////////////////////////////////////////////// + + // https://drafts.csswg.org/mediaqueries/#color + {"color", {_range_, _integer_}}, + + // https://drafts.csswg.org/mediaqueries/#color-index + {"color-index", {_range_, _integer_}}, + + // https://drafts.csswg.org/mediaqueries/#monochrome + {"monochrome", {_range_, _integer_}}, + + + //////////////////////////////////////////////// + // Deprecated Media Features + //////////////////////////////////////////////// + + // https://drafts.csswg.org/mediaqueries/#device-width + {"device-width", {_range_, _length_}}, + {"device-height", {_range_, _length_}}, + + // https://drafts.csswg.org/mediaqueries/#device-aspect-ratio + {"device-aspect-ratio", {_range_, _ratio_}}, +}; + +bool convert_units(mf_info mfi, css_token val[2], document::ptr doc) { - switch(feature) + switch (mfi.value_type) { - case media_feature_width: - if(check_as_bool) - { - return (features.width != 0); - } else if(features.width == val) - { - return true; - } - break; - case media_feature_min_width: - if(features.width >= val) - { - return true; - } - break; - case media_feature_max_width: - if(features.width <= val) - { - return true; - } - break; - case media_feature_height: - if(check_as_bool) - { - return (features.height != 0); - } else if(features.height == val) - { - return true; - } - break; - case media_feature_min_height: - if(features.height >= val) - { + case _integer_: + // nothing to convert, just verify + return val[0].type == NUMBER && val[0].n.number_type == css_number_integer && val[1].type == 0; + + case _length_: + { + if (val[1].type != 0) return false; + css_length length; + if (!length.from_token(val[0], f_length)) return false; + font_metrics fm; + fm.x_height = fm.font_size = doc->container()->get_default_font_size(); + doc->cvt_units(length, fm, 0); + val[0].n.number = length.val(); + return true; + } + + case _resolution_: // https://drafts.csswg.org/css-values-4/#resolution + if (val[1].type != 0) return false; + if (val[0].type == DIMENSION) + { + string unit = lowcase(val[0].unit); + int idx = value_index(unit, "dpi;dpcm;dppx;x"); // x == dppx + // The allowed range of <resolution> values always excludes negative values + if (idx < 0 || val[0].n.number < 0) return false; + // dppx is the canonical unit, but we convert to dpi instead to match document_container::get_media_features + // "Note that due to the 1:96 fixed ratio of CSS in to CSS px, 1dppx is equivalent to 96dpi." + if (unit == "dppx" || unit == "x") + val[0].n.number *= 96; + else if (unit == "dpcm") + val[0].n.number *= 2.54f; // 1in = 2.54cm return true; } - break; - case media_feature_max_height: - if(features.height <= val) + // https://drafts.csswg.org/mediaqueries/#resolution + else if (val[0].ident() == "infinite") { + val[0] = css_token(NUMBER, INFINITY, css_number_number); return true; } - break; + // Note: <resolution> doesn't allow unitless zero + return false; - case media_feature_device_width: - if(check_as_bool) - { - return (features.device_width != 0); - } else if(features.device_width == val) + case _ratio_: // https://drafts.csswg.org/css-values-4/#ratio <ratio> = <number [0,∞]> [ / <number [0,∞]> ]? + if (val[0].type == NUMBER && val[0].n.number >= 0 && + ((val[1].type == NUMBER && val[1].n.number >= 0) || val[1].type == 0)) { + if (val[1].type == NUMBER) + val[0].n.number /= val[1].n.number; // Note: val[1].n.number may be 0, so result may be inf return true; } - break; - case media_feature_min_device_width: - if(features.device_width >= val) - { - return true; - } - break; - case media_feature_max_device_width: - if(features.device_width <= val) - { + return false; + + case _keyword_: + { + if (val[1].type != 0) return false; + string_id ident = _id(val[0].ident()); + if (!contains(mfi.keywords, ident)) return false; + val[0] = css_token(NUMBER, (float)ident); + return true; + } + + default: + return false; + } +} + +bool media_feature::verify_and_convert_units(string_id syntax, + css_token val[2], css_token val2[2], document::ptr doc) +{ + // https://drafts.csswg.org/mediaqueries/#mq-boolean-context + if (syntax == _boolean_) // (name) + { + // Attempting to evaluate a min/max prefixed media feature in a boolean context is invalid and a syntax error. + auto mf_info = at(supported_media_features, name); + if (!mf_info) return false; + value = mf_info.value_type == _keyword_ ? (float)_none_ : 0; + op = u'≠'; + return true; + } + else if (syntax == _plain_) // ({min-,max-,}name: value) + { + if (is_one_of(name.substr(0, 4), "min-", "max-")) + { + string real_name = name.substr(4); + auto mf_info = at(supported_media_features, real_name); + if (!mf_info || mf_info.type == _discrete_) + return false; + if (!convert_units(mf_info, val, doc)) + return false; + value = val[0].n.number; + op = name.substr(0, 4) == "min-" ? u'⩾' : u'⩽'; + name = real_name; return true; } - break; - case media_feature_device_height: - if(check_as_bool) - { - return (features.device_height != 0); - } else if(features.device_height == val) + else { + auto mf_info = at(supported_media_features, name); + if (!mf_info) return false; + if (!convert_units(mf_info, val, doc)) + return false; + value = val[0].n.number; + op = '='; return true; } - break; - case media_feature_min_device_height: - if(features.device_height >= val) + } + else // range syntax + { + auto mf_info = at(supported_media_features, name); + if (!mf_info || mf_info.type == _discrete_) + return false; + //if (val) { - return true; + if (!convert_units(mf_info, val, doc)) + return false; + value = val[0].n.number; } - break; - case media_feature_max_device_height: - if(features.device_height <= val) + if (val2) { - return true; + if (!convert_units(mf_info, val2, doc)) + return false; + value2 = val2[0].n.number; } - break; + return true; + } +} - case media_feature_orientation: - if(features.height >= features.width) - { - if(val == media_orientation_portrait) - { - return true; - } - } else - { - if(val == media_orientation_landscape) - { - return true; - } - } - break; - case media_feature_aspect_ratio: - if(features.height && val2) - { - int ratio_this = round_d( (double) val / (double) val2 * 100 ); - int ratio_feat = round_d( (double) features.width / (double) features.height * 100.0 ); - if(ratio_this == ratio_feat) - { - return true; - } - } - break; - case media_feature_min_aspect_ratio: - if(features.height && val2) - { - int ratio_this = round_d( (double) val / (double) val2 * 100 ); - int ratio_feat = round_d( (double) features.width / (double) features.height * 100.0 ); - if(ratio_feat >= ratio_this) - { - return true; - } - } - break; - case media_feature_max_aspect_ratio: - if(features.height && val2) - { - int ratio_this = round_d( (double) val / (double) val2 * 100 ); - int ratio_feat = round_d( (double) features.width / (double) features.height * 100.0 ); - if(ratio_feat <= ratio_this) - { - return true; - } - } - break; +// <media-feature> = ( [ <mf-plain> | <mf-boolean> | <mf-range> ] ) +bool parse_media_feature(const css_token& token, media_feature& result, document::ptr doc) +{ + if (token.type != ROUND_BLOCK || token.value.empty()) return false; + const css_token_vector& tokens = token.value; - case media_feature_device_aspect_ratio: - if(features.device_height && val2) - { - int ratio_this = round_d( (double) val / (double) val2 * 100 ); - int ratio_feat = round_d( (double) features.device_width / (double) features.device_height * 100.0 ); - if(ratio_feat == ratio_this) - { - return true; - } - } - break; - case media_feature_min_device_aspect_ratio: - if(features.device_height && val2) - { - int ratio_this = round_d( (double) val / (double) val2 * 100 ); - int ratio_feat = round_d( (double) features.device_width / (double) features.device_height * 100.0 ); - if(ratio_feat >= ratio_this) - { - return true; - } - } - break; - case media_feature_max_device_aspect_ratio: - if(features.device_height && val2) - { - int ratio_this = round_d( (double) val / (double) val2 * 100 ); - int ratio_feat = round_d( (double) features.device_width / (double) features.device_height * 100.0 ); - if(ratio_feat <= ratio_this) - { - return true; - } - } - break; + if (tokens.size() == 1) + { + media_feature mf = {tokens[0].ident()}; + if (!mf.verify_and_convert_units(_boolean_)) return false; + result = mf; + return true; + } - case media_feature_color: - if(check_as_bool) - { - return (features.color != 0); - } else if(features.color == val) - { - return true; - } - break; - case media_feature_min_color: - if(features.color >= val) - { - return true; - } - break; - case media_feature_max_color: - if(features.color <= val) - { - return true; - } - break; + if (tokens[0].type == IDENT && tokens[1].ch == ':') + { + css_token val[2]; + int index = 2; + if (!parse_mf_value(tokens, index, val) || index != (int)tokens.size()) + return false; - case media_feature_color_index: - if(check_as_bool) - { - return (features.color_index != 0); - } else if(features.color_index == val) - { - return true; - } - break; - case media_feature_min_color_index: - if(features.color_index >= val) - { - return true; - } - break; - case media_feature_max_color_index: - if(features.color_index <= val) - { - return true; - } - break; + media_feature mf = {tokens[0].ident()}; + if (!mf.verify_and_convert_units(_plain_, val, nullptr, doc)) return false; + result = mf; + return true; + } - case media_feature_monochrome: - if(check_as_bool) - { - return (features.monochrome != 0); - } else if(features.monochrome == val) - { - return true; - } - break; - case media_feature_min_monochrome: - if(features.monochrome >= val) - { - return true; - } - break; - case media_feature_max_monochrome: - if(features.monochrome <= val) - { - return true; - } - break; + return parse_mf_range(tokens, result, doc); +} - case media_feature_resolution: - if(features.resolution == val) - { - return true; - } - break; - case media_feature_min_resolution: - if(features.resolution >= val) - { - return true; - } - break; - case media_feature_max_resolution: - if(features.resolution <= val) +// <mf-value> = <number> | <dimension> | <ident> | <ratio> +// <ratio> = <number [0,∞]> [ / <number [0,∞]> ]? +bool parse_mf_value(const css_token_vector& tokens, int& index, css_token val[2]) +{ + const css_token& a = at(tokens, index); + const css_token& b = at(tokens, index + 1); + const css_token& c = at(tokens, index + 2); + + if (!is_one_of(a.type, NUMBER, DIMENSION, IDENT)) return false; + + if (a.type == NUMBER && a.n.number >= 0 && b.ch == '/' && + c.type == NUMBER && c.n.number >= 0) + { + val[0] = a; + val[1] = c; + index += 3; + } + else + { + val[0] = a; + index++; + } + return true; +} + +short mirror(short op) +{ + if (op == '<') return '>'; + if (op == '>') return '<'; + if (op == u'⩽') return u'⩾'; + if (op == u'⩾') return u'⩽'; + return op; +} + +// <mf-range> = <mf-name> <mf-comparison> <mf-value> +// | <mf-value> <mf-comparison> <mf-name> +// | <mf-value> <mf-lt> <mf-name> <mf-lt> <mf-value> +// | <mf-value> <mf-gt> <mf-name> <mf-gt> <mf-value> +// <mf-lt> = '<' '='? +// <mf-gt> = '>' '='? +// <mf-eq> = '=' +// <mf-comparison> = <mf-lt> | <mf-gt> | <mf-eq> +bool parse_mf_range(const css_token_vector& tokens, media_feature& result, document::ptr doc) +{ + if (tokens.size() < 3) return false; + + int index; + string name; + auto mf_name = [&]() + { + if (at(tokens, index).type != IDENT) return false; + name = at(tokens, index).ident(); + // special case for (infinite = resolution) + // resolution is the only range media feature that can accept a keyword + if (name == "infinite") return false; + index++; + return true; + }; + auto mf_value = [&](css_token _val[2]) + { + return parse_mf_value(tokens, index, _val); + }; + auto mf_lt_gt = [&](char lg, short& _op) + { + const css_token& tok = at(tokens, index); + const css_token& tok1 = at(tokens, index + 1); + + if (tok.ch != lg) return false; + + if (tok1.ch == '=') + index+=2, _op = lg == '<' ? u'⩽' : u'⩾'; + else + index++, _op = lg; + return true; + }; + auto mf_lt = [&](short& _op) { return mf_lt_gt('<', _op); }; + auto mf_gt = [&](short& _op) { return mf_lt_gt('>', _op); }; + auto mf_comparison = [&](short& _op) + { + const css_token& tok = at(tokens, index); + + if (tok.ch == '=') { + index++; + _op = '='; return true; } - break; - default: - return false; - } + return mf_lt(_op) || mf_gt(_op); + }; + auto start = [&]() { index = 0; return true; }; + auto end = [&]() { return index == (int)tokens.size(); }; + + short op; + css_token val[2]; + // using lambda to avoid warning "assignment within conditional expression" + auto reverse = [](short& _op) { _op = mirror(_op); return true; }; + if ((start() && mf_name() && mf_comparison(op) && mf_value(val) && end()) || + (start() && mf_value(val) && mf_comparison(op) && mf_name() && end() && reverse(op))) + { + media_feature mf = {name}; + mf.op = op; + if (!mf.verify_and_convert_units(_range_, val, nullptr, doc)) return false; + result = mf; + return true; + } + short op2; + css_token val2[2]; + if ((start() && mf_value(val) && mf_lt(op) && mf_name() && mf_lt(op2) && mf_value(val2) && end()) || + (start() && mf_value(val) && mf_gt(op) && mf_name() && mf_gt(op2) && mf_value(val2) && end())) + { + media_feature mf = {name}; + mf.op = op; + mf.op2 = op2; + if (!mf.verify_and_convert_units(_range_, val, val2, doc)) return false; + result = mf; + return true; + } return false; } + +} // namespace litehtml diff --git a/src/num_cvt.cpp b/src/num_cvt.cpp index 23d594b5c..b1d0f3b75 100644 --- a/src/num_cvt.cpp +++ b/src/num_cvt.cpp @@ -4,7 +4,7 @@ static std::vector<char> latin_lower = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' }; static std::vector<char> latin_upper = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' }; -static std::vector<std::wstring> greek_lower = { L"α", L"β", L"γ", L"δ", L"ε", L"ζ", L"η", L"θ", L"ι", L"κ", L"λ", L"μ", L"ν", L"ξ", L"ο", L"π", L"ρ", L"σ", L"τ", L"υ", L"φ", L"χ", L"ψ", L"ω" }; +static std::vector<std::u32string> greek_lower = { U"α", U"β", U"γ", U"δ", U"ε", U"ζ", U"η", U"θ", U"ι", U"κ", U"λ", U"μ", U"ν", U"ξ", U"ο", U"π", U"ρ", U"σ", U"τ", U"υ", U"φ", U"χ", U"ψ", U"ω" }; static litehtml::string to_mapped_alpha(int num, const std::vector<char>& map) { @@ -22,7 +22,7 @@ static litehtml::string to_mapped_alpha(int num, const std::vector<char>& map) return out; } -static litehtml::string to_mapped_alpha(int num, const std::vector<std::wstring>& map) +static litehtml::string to_mapped_alpha(int num, const std::vector<std::u32string>& map) { int dividend = num; litehtml::string out; @@ -31,7 +31,7 @@ static litehtml::string to_mapped_alpha(int num, const std::vector<std::wstring> while (dividend > 0) { modulo = (dividend - 1) % map.size(); - out = litehtml_from_wchar(map[modulo]).c_str() + out; + out = litehtml_from_utf32(map[modulo]).c_str() + out; dividend = (int)((dividend - modulo) / map.size()); } diff --git a/src/render_block.cpp b/src/render_block.cpp index c170c341f..6fba641b0 100644 --- a/src/render_block.cpp +++ b/src/render_block.cpp @@ -1,8 +1,9 @@ -#include "html.h" #include "render_block.h" #include "render_inline_context.h" #include "render_block_context.h" #include "document.h" +#include "document_container.h" +#include "html_tag.h" int litehtml::render_item_block::place_float(const std::shared_ptr<render_item> &el, int top, const containing_block_context &self_size, formatting_context* fmt_ctx) { @@ -52,15 +53,6 @@ int litehtml::render_item_block::place_float(const std::shared_ptr<render_item> std::shared_ptr<litehtml::render_item> litehtml::render_item_block::init() { - { - css_selector sel; - sel.parse(".inline_rating"); - if(src_el()->select(sel)) - { - int i = 0; - i++; - } - } std::shared_ptr<render_item> ret; // Initialize indexes for list items @@ -107,7 +99,6 @@ std::shared_ptr<litehtml::render_item> litehtml::render_item_block::init() bool has_block_level = false; bool has_inlines = false; - bool has_floats = false; for (const auto& el : m_children) { if(!el->src_el()->is_float()) @@ -205,6 +196,14 @@ int litehtml::render_item_block::_render(int x, int y, const containing_block_co int ret_width = _render_content(x, y, second_pass, self_size, fmt_ctx); //***************************************** + if (src_el()->css().get_display() == display_list_item) + { + if(m_pos.height == 0) + { + m_pos.height = css().line_height().computed_value; + } + } + bool requires_rerender = false; // when true, the second pass for content rendering is required // Set block width @@ -342,7 +341,6 @@ int litehtml::render_item_block::_render(int x, int y, const containing_block_co m_pos.height = sz.height; } } - } return ret_width + content_offset_width(); diff --git a/src/render_block_context.cpp b/src/render_block_context.cpp index bf6b09277..d03998500 100644 --- a/src/render_block_context.cpp +++ b/src/render_block_context.cpp @@ -1,8 +1,7 @@ -#include "html.h" #include "render_block_context.h" #include "document.h" -int litehtml::render_item_block_context::_render_content(int x, int y, bool second_pass, const containing_block_context &self_size, formatting_context* fmt_ctx) +int litehtml::render_item_block_context::_render_content(int /*x*/, int /*y*/, bool second_pass, const containing_block_context &self_size, formatting_context* fmt_ctx) { element_position el_position; diff --git a/src/render_flex.cpp b/src/render_flex.cpp index d0170e650..ad8ff3a2f 100644 --- a/src/render_flex.cpp +++ b/src/render_flex.cpp @@ -1,8 +1,8 @@ -#include "html.h" #include "types.h" #include "render_flex.h" +#include "html_tag.h" -int litehtml::render_item_flex::_render_content(int x, int y, bool second_pass, const containing_block_context &self_size, formatting_context* fmt_ctx) +int litehtml::render_item_flex::_render_content(int x, int y, bool /*second_pass*/, const containing_block_context &self_size, formatting_context* fmt_ctx) { bool is_row_direction = true; bool reverse = false; @@ -63,8 +63,6 @@ int litehtml::render_item_flex::_render_content(int x, int y, bool second_pass, ///////////////////////////////////////////////////////////////// m_lines = get_lines(self_size, fmt_ctx, is_row_direction, container_main_size, single_line); - int el_y = 0; - int el_x = 0; int sum_cross_size = 0; int sum_main_size = 0; int ret_width = 0; @@ -89,7 +87,6 @@ int litehtml::render_item_flex::_render_content(int x, int y, bool second_pass, } int free_cross_size = 0; - int cross_start = 0; bool is_wrap_reverse = css().get_flex_wrap() == flex_wrap_wrap_reverse; if(container_main_size == 0) { @@ -101,7 +98,6 @@ int litehtml::render_item_flex::_render_content(int x, int y, bool second_pass, ///////////////////////////////////////////////////////////////// if (is_row_direction) { - cross_start = content_offset_top(); if (self_size.height.type != containing_block_context::cbc_value_type_auto) { int height = self_size.height; @@ -113,7 +109,6 @@ int litehtml::render_item_flex::_render_content(int x, int y, bool second_pass, } } else { - cross_start = content_offset_left(); free_cross_size = self_size.render_width - sum_cross_size; ret_width = sum_cross_size; } @@ -224,7 +219,6 @@ int litehtml::render_item_flex::_render_content(int x, int y, bool second_pass, ///////////////////////////////////////////////////////////////// /// Align flex items in flex lines ///////////////////////////////////////////////////////////////// - int line_num = 0; for(auto &ln : m_lines) { int height = ln.calculate_items_position(container_main_size, @@ -232,7 +226,6 @@ int litehtml::render_item_flex::_render_content(int x, int y, bool second_pass, is_row_direction, self_size, fmt_ctx); - line_num++; m_pos.height = std::max(m_pos.height, height); } diff --git a/src/render_image.cpp b/src/render_image.cpp index 1ff4574bb..0f1321607 100644 --- a/src/render_image.cpp +++ b/src/render_image.cpp @@ -1,8 +1,7 @@ -#include "html.h" #include "render_image.h" #include "document.h" -int litehtml::render_item_image::_render(int x, int y, const containing_block_context &containing_block_size, formatting_context* fmt_ctx, bool second_pass) +int litehtml::render_item_image::_render(int x, int y, const containing_block_context &containing_block_size, formatting_context* /*fmt_ctx*/, bool /*second_pass*/) { int parent_width = containing_block_size.width; containing_block_context self_size = calculate_containing_block_context(containing_block_size); @@ -19,7 +18,7 @@ int litehtml::render_item_image::_render(int x, int y, const containing_block_co m_pos.width = sz.width; m_pos.height = sz.height; - src_el()->css_w().set_line_height(height()); + src_el()->css_w().line_height_w().computed_value = height(); if(src_el()->css().get_height().is_predefined() && src_el()->css().get_width().is_predefined()) { @@ -29,7 +28,7 @@ int litehtml::render_item_image::_render(int x, int y, const containing_block_co // check for max-width if(!src_el()->css().get_max_width().is_predefined()) { - int max_width = doc->to_pixels(src_el()->css().get_max_width(), src_el()->css().get_font_size(), parent_width); + int max_width = doc->to_pixels(css().get_max_width(), css().get_font_metrics(), parent_width); if(m_pos.width > max_width) { m_pos.width = max_width; @@ -90,7 +89,7 @@ int litehtml::render_item_image::_render(int x, int y, const containing_block_co // check for max-width if(!src_el()->css().get_max_width().is_predefined()) { - int max_width = doc->to_pixels(src_el()->css().get_max_width(), src_el()->css().get_font_size(), parent_width); + int max_width = doc->to_pixels(css().get_max_width(), css().get_font_metrics(), parent_width); if(m_pos.width > max_width) { m_pos.width = max_width; @@ -126,7 +125,7 @@ int litehtml::render_item_image::_render(int x, int y, const containing_block_co // check for max-height if(!src_el()->css().get_max_width().is_predefined()) { - int max_width = doc->to_pixels(src_el()->css().get_max_width(), src_el()->css().get_font_size(), parent_width); + int max_width = doc->to_pixels(css().get_max_width(), css().get_font_metrics(), parent_width); if(m_pos.width > max_width) { m_pos.width = max_width; @@ -143,6 +142,6 @@ int litehtml::render_item_image::_render(int x, int y, const containing_block_co int litehtml::render_item_image::calc_max_height(int image_height, int containing_block_height) { document::ptr doc = src_el()->get_document(); - return doc->to_pixels(src_el()->css().get_max_height(), src_el()->css().get_font_size(), + return doc->to_pixels(css().get_max_height(), css().get_font_metrics(), containing_block_height == 0 ? image_height : containing_block_height); } diff --git a/src/render_inline_context.cpp b/src/render_inline_context.cpp index 4f5d77015..4c4d97480 100644 --- a/src/render_inline_context.cpp +++ b/src/render_inline_context.cpp @@ -1,9 +1,8 @@ -#include "html.h" #include "render_inline_context.h" #include "document.h" #include "iterators.h" -int litehtml::render_item_inline_context::_render_content(int x, int y, bool second_pass, const containing_block_context &self_size, formatting_context* fmt_ctx) +int litehtml::render_item_inline_context::_render_content(int /*x*/, int /*y*/, bool /*second_pass*/, const containing_block_context &self_size, formatting_context* fmt_ctx) { m_line_boxes.clear(); m_max_line_width = 0; @@ -49,20 +48,20 @@ int litehtml::render_item_inline_context::_render_content(int x, int y, bool sec } } // place element into rendering flow - place_inline(std::unique_ptr<line_box_item>(new line_box_item(el)), self_size, fmt_ctx); + place_inline(std::make_unique<line_box_item>(el), self_size, fmt_ctx); } break; case iterator_item_type_start_parent: { el->clear_inline_boxes(); - place_inline(std::unique_ptr<lbi_start>(new lbi_start(el)), self_size, fmt_ctx); + place_inline(std::make_unique<lbi_start>(el), self_size, fmt_ctx); } break; case iterator_item_type_end_parent: { - place_inline(std::unique_ptr<lbi_end>(new lbi_end(el)), self_size, fmt_ctx); + place_inline(std::make_unique<lbi_end>(el), self_size, fmt_ctx); } break; } @@ -150,7 +149,7 @@ void litehtml::render_item_inline_context::fix_line_width(element_float flt, { line_left += src_el()->css().get_text_indent().calc_percent(self_size.width); } - + } auto items = m_line_boxes.back()->new_width(line_left, line_right); @@ -224,12 +223,12 @@ int litehtml::render_item_inline_context::new_box(const std::unique_ptr<line_box } } - m_line_boxes.emplace_back(std::unique_ptr<line_box>(new line_box( + m_line_boxes.emplace_back(std::make_unique<line_box>( line_ctx.top, line_ctx.left + first_line_margin + text_indent, line_ctx.right, - css().get_line_height(), + css().line_height(), css().get_font_metrics(), - css().get_text_align()))); + css().get_text_align())); // Add items returned by finish_last_box function into the new line for(auto& it : items) @@ -259,13 +258,11 @@ void litehtml::render_item_inline_context::place_inline(std::unique_ptr<line_box return; } - line_context line_ctx = {0}; - line_ctx.top = 0; + line_context line_ctx; if (!m_line_boxes.empty()) { line_ctx.top = m_line_boxes.back().get()->top(); } - line_ctx.left = 0; line_ctx.right = self_size.render_width; line_ctx.fix_top(); fmt_ctx->get_line_left_right(line_ctx.top, self_size.render_width, line_ctx.left, line_ctx.right); @@ -405,7 +402,7 @@ int litehtml::render_item_inline_context::get_last_baseline() bl = line->bottom() - line->baseline() + content_offset_top(); } else { - bl = height() - margin_bottom(); + bl = height(); } return bl; } diff --git a/src/render_item.cpp b/src/render_item.cpp index 0af172807..3794807a4 100644 --- a/src/render_item.cpp +++ b/src/render_item.cpp @@ -1,30 +1,30 @@ -#include "html.h" #include "render_item.h" #include "document.h" #include <typeinfo> -#include <utf8_strings.h> +#include "document_container.h" +#include "types.h" litehtml::render_item::render_item(std::shared_ptr<element> _src_el) : m_element(std::move(_src_el)), m_skip(false) { document::ptr doc = src_el()->get_document(); - auto fnt_size = src_el()->css().get_font_size(); - - m_margins.left = doc->to_pixels(src_el()->css().get_margins().left, fnt_size); - m_margins.right = doc->to_pixels(src_el()->css().get_margins().right, fnt_size); - m_margins.top = doc->to_pixels(src_el()->css().get_margins().top, fnt_size); - m_margins.bottom = doc->to_pixels(src_el()->css().get_margins().bottom, fnt_size); - - m_padding.left = doc->to_pixels(src_el()->css().get_padding().left, fnt_size); - m_padding.right = doc->to_pixels(src_el()->css().get_padding().right, fnt_size); - m_padding.top = doc->to_pixels(src_el()->css().get_padding().top, fnt_size); - m_padding.bottom = doc->to_pixels(src_el()->css().get_padding().bottom, fnt_size); - - m_borders.left = doc->to_pixels(src_el()->css().get_borders().left.width, fnt_size); - m_borders.right = doc->to_pixels(src_el()->css().get_borders().right.width, fnt_size); - m_borders.top = doc->to_pixels(src_el()->css().get_borders().top.width, fnt_size); - m_borders.bottom = doc->to_pixels(src_el()->css().get_borders().bottom.width, fnt_size); + auto fm = css().get_font_metrics(); + + m_margins.left = doc->to_pixels(src_el()->css().get_margins().left, fm, 0); + m_margins.right = doc->to_pixels(src_el()->css().get_margins().right, fm, 0); + m_margins.top = doc->to_pixels(src_el()->css().get_margins().top, fm, 0); + m_margins.bottom = doc->to_pixels(src_el()->css().get_margins().bottom, fm, 0); + + m_padding.left = doc->to_pixels(src_el()->css().get_padding().left, fm, 0); + m_padding.right = doc->to_pixels(src_el()->css().get_padding().right, fm, 0); + m_padding.top = doc->to_pixels(src_el()->css().get_padding().top, fm, 0); + m_padding.bottom = doc->to_pixels(src_el()->css().get_padding().bottom, fm, 0); + + m_borders.left = doc->to_pixels(src_el()->css().get_borders().left.width, fm, 0); + m_borders.right = doc->to_pixels(src_el()->css().get_borders().right.width, fm, 0); + m_borders.top = doc->to_pixels(src_el()->css().get_borders().top.width, fm, 0); + m_borders.bottom = doc->to_pixels(src_el()->css().get_borders().bottom.width, fm, 0); } int litehtml::render_item::render(int x, int y, const containing_block_context& containing_block_size, formatting_context* fmt_ctx, bool second_pass) @@ -46,7 +46,6 @@ int litehtml::render_item::render(int x, int y, const containing_block_context& if(src_el()->is_block_formatting_context() || ! fmt_ctx) { formatting_context fmt; - fmt.push_position(content_left, content_top); ret = _render(x, y, containing_block_size, &fmt, second_pass); fmt.apply_relative_shift(containing_block_size); } else @@ -243,8 +242,8 @@ bool litehtml::render_item::fetch_positioned() void litehtml::render_item::render_positioned(render_type rt) { - position wnd_position; - src_el()->get_document()->container()->get_client_rect(wnd_position); + position view_port; + src_el()->get_document()->container()->get_viewport(view_port); element_position el_position; bool process; @@ -273,18 +272,14 @@ void litehtml::render_item::render_positioned(render_type rt) if(process) { containing_block_context containing_block_size; - int client_x = 0; - int client_y = 0; - if(el_position == element_position_fixed) + if(el_position == element_position_fixed || (is_root() && !src_el()->is_positioned())) { - containing_block_size.height = wnd_position.height; - containing_block_size.width = wnd_position.width; - client_x = wnd_position.left(); - client_y = wnd_position.top(); + containing_block_size.height = view_port.height; + containing_block_size.width = view_port.width; } else { - containing_block_size.height = m_pos.height; - containing_block_size.width = m_pos.width; + containing_block_size.height = m_pos.height + m_padding.height(); + containing_block_size.width = m_pos.width + m_padding.width(); } css_length css_left = el->src_el()->css().get_offsets().left; @@ -294,144 +289,353 @@ void litehtml::render_item::render_positioned(render_type rt) bool need_render = false; - css_length el_w = el->src_el()->css().get_width(); - css_length el_h = el->src_el()->css().get_height(); - - int new_width = -1; - int new_height = -1; - if(el_w.units() == css_units_percentage && containing_block_size.width) - { - new_width = el_w.calc_percent(containing_block_size.width); - if(el->m_pos.width != new_width) - { - need_render = true; - el->m_pos.width = new_width; - } - } + css_length el_width = el->src_el()->css().get_width(); + css_length el_height = el->src_el()->css().get_height(); - if(el_h.units() == css_units_percentage && containing_block_size.height) - { - new_height = el_h.calc_percent(containing_block_size.height); - if(el->m_pos.height != new_height) - { - need_render = true; - el->m_pos.height = new_height; - } - } + auto fix_height_min_max = [&] (int height) + { + auto max_height = el->css().get_max_height(); + auto min_height = el->css().get_max_height(); + if(!max_height.is_predefined()) + { + int max_height_value = max_height.calc_percent(containing_block_size.height); + if(height > max_height_value) + { + height = max_height_value; + } + } + if(!min_height.is_predefined()) + { + int min_height_value = min_height.calc_percent(containing_block_size.height); + if(height < min_height_value) + { + height = min_height_value; + } + } + height += el->content_offset_height(); + return height; + }; - bool cvt_x = false; - bool cvt_y = false; + auto fix_width_min_max = [&] (int width) + { + auto max_width = el->css().get_max_width(); + auto min_width = el->css().get_min_width(); + if(!max_width.is_predefined()) + { + int max_width_value = max_width.calc_percent(containing_block_size.width); + if(width > max_width_value) + { + width = max_width_value; + } + } + if(!min_width.is_predefined()) + { + int min_width_value = min_width.calc_percent(containing_block_size.width); + if(width < min_width_value) + { + width = min_width_value; + } + } + width += el->content_offset_width(); + return width; + }; + + int bottom = 0; + int top = 0; + int height = 0; + auto [el_static_offset_x, el_static_offset_y] = element_static_offset(el); + int el_static_x = el->m_pos.x + el_static_offset_x; + int el_static_y = el->m_pos.y + el_static_offset_y; + // Calculate vertical position + // https://www.w3.org/TR/CSS22/visudet.html#abs-non-replaced-height + // 10.6.4 Absolutely positioned, non-replaced elements + if(css_top.is_predefined() && !css_bottom.is_predefined() && el_height.is_predefined()) + { + // 1. 'top' and 'height' are 'auto' and 'bottom' is not 'auto', then the height is based on the + // content per 10.6.7, set 'auto' values for 'margin-top' and 'margin-bottom' to 0, and solve for 'top' + if(el->css().get_margins().top.is_predefined()) el->m_margins.top = 0; + if(el->css().get_margins().bottom.is_predefined()) el->m_margins.bottom = 0; + height = el->height(); + bottom = css_bottom.calc_percent(containing_block_size.height); + top = containing_block_size.height - height - bottom; + } else if(css_top.is_predefined() && css_bottom.is_predefined() && !el_height.is_predefined()) + { + // 2. 'top' and 'bottom' are 'auto' and 'height' is not 'auto', then set 'top' to the static position, + // set 'auto' values for 'margin-top' and 'margin-bottom' to 0, and solve for 'bottom' + if(el->css().get_margins().top.is_predefined()) el->m_margins.top = 0; + if(el->css().get_margins().bottom.is_predefined()) el->m_margins.bottom = 0; + top = el_static_y - el->content_offset_top(); + height = fix_height_min_max(el_height.calc_percent(containing_block_size.height)); + } else if(!css_top.is_predefined() && css_bottom.is_predefined() && el_height.is_predefined()) + { + // 3. 'height' and 'bottom' are 'auto' and 'top' is not 'auto', then the height is based on the + // content per 10.6.7, set 'auto' values for 'margin-top' and 'margin-bottom' to 0, + // and solve for 'bottom' + if(el->css().get_margins().top.is_predefined()) el->m_margins.top = 0; + if(el->css().get_margins().bottom.is_predefined()) el->m_margins.bottom = 0; + height = el->height(); + top = css_top.calc_percent(containing_block_size.height); + } else if(css_top.is_predefined() && !css_bottom.is_predefined() && !el_height.is_predefined()) + { + // 4. 'top' is 'auto', 'height' and 'bottom' are not 'auto', then set 'auto' values for 'margin-top' + // and 'margin-bottom' to 0, and solve for 'top' + if(el->css().get_margins().top.is_predefined()) el->m_margins.top = 0; + if(el->css().get_margins().bottom.is_predefined()) el->m_margins.bottom = 0; + height = fix_height_min_max(el_height.calc_percent(containing_block_size.height)); + bottom = css_bottom.calc_percent(containing_block_size.height); + top = containing_block_size.height - height - bottom; + } else if(!css_top.is_predefined() && !css_bottom.is_predefined() && el_height.is_predefined()) + { + // 5. 'height' is 'auto', 'top' and 'bottom' are not 'auto', then 'auto' values for 'margin-top' and + // 'margin-bottom' are set to 0 and solve for 'height' + if(el->css().get_margins().top.is_predefined()) el->m_margins.top = 0; + if(el->css().get_margins().bottom.is_predefined()) el->m_margins.bottom = 0; + bottom = css_bottom.calc_percent(containing_block_size.height); + top = css_top.calc_percent(containing_block_size.height); + if(el->src_el()->is_replaced()) + { + height = el->height() - el->content_offset_height(); + int reminded = (containing_block_size.height - top - bottom) - height - el->content_offset_height(); + if(reminded > 0) + { + int divider = 0; + if (el->css().get_margins().top.is_predefined()) divider++; + if (el->css().get_margins().bottom.is_predefined()) divider++; + if (divider != 0) + { + if (el->css().get_margins().top.is_predefined()) el->m_margins.top = reminded / divider; + if (el->css().get_margins().bottom.is_predefined()) el->m_margins.bottom = reminded / divider; + } + } + height += el->content_offset_height(); + } else + { + height = containing_block_size.height - top - bottom; + } + if(!el->css().get_max_height().is_predefined()) + { + int max_height = el->css().get_max_height().calc_percent(containing_block_size.height); + if(height - el->content_offset_height() > max_height) + { + int reminded = height - el->content_offset_height() - max_height; + height = max_height; + int divider = 0; + if(el->css().get_margins().top.is_predefined()) divider++; + if(el->css().get_margins().bottom.is_predefined()) divider++; + if(divider != 0) + { + if(el->css().get_margins().top.is_predefined()) el->m_margins.top = reminded / divider; + if(el->css().get_margins().bottom.is_predefined()) el->m_margins.bottom = reminded / divider; + } + height += el->content_offset_height(); + } + } + } else if(!css_top.is_predefined() && css_bottom.is_predefined() && !el_height.is_predefined()) + { + // 6. 'bottom' is 'auto', 'top' and 'height' are not 'auto', then set 'auto' values for 'margin-top' + // and 'margin-bottom' to 0 and solve for 'bottom' + if(el->css().get_margins().top.is_predefined()) el->m_margins.top = 0; + if(el->css().get_margins().bottom.is_predefined()) el->m_margins.bottom = 0; + height = fix_height_min_max(el_height.calc_percent(containing_block_size.height)); + top = css_top.calc_percent(containing_block_size.height); + } else if(css_top.is_predefined() && css_bottom.is_predefined() && el_height.is_predefined()) + { + // If all three of 'top', 'height', and 'bottom' are auto, set 'top' to the static position and + // apply rule number three. + if(el->css().get_margins().top.is_predefined()) el->m_margins.top = 0; + if(el->css().get_margins().bottom.is_predefined()) el->m_margins.bottom = 0; + height = el->height(); + top = el_static_y - el->content_offset_top(); + } else + { + // If none of the three are 'auto': + height = fix_height_min_max(el_height.calc_percent(containing_block_size.height)); + top = css_top.calc_percent(containing_block_size.height); + bottom = css_bottom.calc_percent(containing_block_size.height); + int remained = containing_block_size.height - height - top - bottom; - if(el_position == element_position_fixed) - { - if(!css_left.is_predefined() || !css_right.is_predefined()) - { - if(!css_left.is_predefined() && css_right.is_predefined()) - { - el->m_pos.x = css_left.calc_percent(containing_block_size.width) + el->content_offset_left(); - } else if(css_left.is_predefined() && !css_right.is_predefined()) - { - el->m_pos.x = containing_block_size.width - css_right.calc_percent(containing_block_size.width) - el->m_pos.width - - el->content_offset_right(); - } else - { - el->m_pos.x = css_left.calc_percent(containing_block_size.width) + el->content_offset_left(); - el->m_pos.width = containing_block_size.width - - css_left.calc_percent(containing_block_size.width) - - css_right.calc_percent(containing_block_size.width) - - (el->content_offset_left() + el->content_offset_right()); - need_render = true; - } - } + if(el->css().get_margins().top.is_predefined() && el->css().get_margins().bottom.is_predefined()) + { + // If both 'margin-top' and 'margin-bottom' are 'auto', solve the equation under the extra + // constraint that the two margins get equal values. + el->m_margins.top = el->m_margins.bottom = remained / 2; + height += el->m_margins.top + el->m_margins.bottom; + } else + { + // If one of 'margin-top' or 'margin-bottom' is 'auto', solve the equation for that value. + if(el->css().get_margins().top.is_predefined()) + { + el->m_margins.top = remained; + height += el->m_margins.top; + } + if(el->css().get_margins().bottom.is_predefined()) + { + el->m_margins.bottom = remained; + height += el->m_margins.bottom; + } + } + } + el->m_pos.y = top + el->content_offset_top(); + if(el->m_pos.height != height - el->content_offset_height()) + { + el->m_pos.height = height - el->content_offset_height(); + need_render = true; + } - if(!css_top.is_predefined() || !css_bottom.is_predefined()) - { - if(!css_top.is_predefined() && css_bottom.is_predefined()) - { - el->m_pos.y = css_top.calc_percent(containing_block_size.height) + el->content_offset_top(); - } else if(css_top.is_predefined() && !css_bottom.is_predefined()) - { - el->m_pos.y = containing_block_size.height - css_bottom.calc_percent(containing_block_size.height) - el->m_pos.height - - el->content_offset_bottom(); - } else - { - el->m_pos.y = css_top.calc_percent(containing_block_size.height) + el->content_offset_top(); - el->m_pos.height = containing_block_size.height - - css_top.calc_percent(containing_block_size.height) - - css_bottom.calc_percent(containing_block_size.height) - - (el->content_offset_top() + el->content_offset_bottom()); - need_render = true; - } - } - } else - { - if(!css_left.is_predefined() || !css_right.is_predefined()) - { - if(!css_left.is_predefined() && css_right.is_predefined()) - { - el->m_pos.x = css_left.calc_percent(containing_block_size.height) + el->content_offset_left() - m_padding.left; - } else if(css_left.is_predefined() && !css_right.is_predefined()) - { - el->m_pos.x = m_pos.width + m_padding.right - css_right.calc_percent(containing_block_size.height) - el->m_pos.width - - el->content_offset_right(); - } else - { - el->m_pos.x = css_left.calc_percent(containing_block_size.height) + el->content_offset_left() - m_padding.left; - el->m_pos.width = m_pos.width + m_padding.left + m_padding.right - - css_left.calc_percent(containing_block_size.height) - - css_right.calc_percent(containing_block_size.height) - - (el->content_offset_left() + el->content_offset_right()); - if (new_width != -1) - { - el->m_pos.x += (el->m_pos.width - new_width) / 2; - el->m_pos.width = new_width; - } - need_render = true; - } - cvt_x = true; - } + // Calculate horizontal position + int right = 0; + int left = 0; + int width = 0; + // https://www.w3.org/TR/CSS22/visudet.html#abs-non-replaced-width + // 10.3.7 Absolutely positioned, non-replaced elements + if(css_left.is_predefined() && !css_right.is_predefined() && el_width.is_predefined()) + { + // 1. 'left' and 'width' are 'auto' and 'right' is not 'auto', then the width is shrink-to-fit. + // Then solve for 'left' + if(el->css().get_margins().left.is_predefined()) el->m_margins.left = 0; + if(el->css().get_margins().right.is_predefined()) el->m_margins.right = 0; + width = el->width(); + right = css_right.calc_percent(containing_block_size.width); + left = containing_block_size.width - width - right; + } else if(css_left.is_predefined() && css_right.is_predefined() && !el_width.is_predefined()) + { + // 2. 'left' and 'right' are 'auto' and 'width' is not 'auto', then if the 'direction' property of + // the element establishing the static-position containing block is 'ltr' set 'left' to the + // static position, otherwise set 'right' to the static position. Then solve for 'left' + // (if 'direction is 'rtl') or 'right' (if 'direction' is 'ltr'). + if(el->css().get_margins().left.is_predefined()) el->m_margins.left = 0; + if(el->css().get_margins().right.is_predefined()) el->m_margins.right = 0; + left = el_static_x - el->content_offset_left(); + width = fix_width_min_max(el_width.calc_percent(containing_block_size.width)); + } else if(!css_left.is_predefined() && css_right.is_predefined() && el_width.is_predefined()) + { + // 3. 'width' and 'right' are 'auto' and 'left' is not 'auto', then the width is shrink-to-fit . + // Then solve for 'right' + if(el->css().get_margins().left.is_predefined()) el->m_margins.left = 0; + if(el->css().get_margins().right.is_predefined()) el->m_margins.right = 0; + width = el->width(); + left = css_left.calc_percent(containing_block_size.width); + } else if(css_left.is_predefined() && !css_right.is_predefined() && !el_width.is_predefined()) + { + // 4. 'left' is 'auto', 'width' and 'right' are not 'auto', then solve for 'left' + if(el->css().get_margins().left.is_predefined()) el->m_margins.left = 0; + if(el->css().get_margins().right.is_predefined()) el->m_margins.right = 0; + right = css_right.calc_percent(containing_block_size.width); + width = fix_width_min_max(el_width.calc_percent(containing_block_size.width)); + left = containing_block_size.width - right - width; + } else if(!css_left.is_predefined() && !css_right.is_predefined() && el_width.is_predefined()) + { + // 5. 'width' is 'auto', 'left' and 'right' are not 'auto', then solve for 'width' + if(el->css().get_margins().left.is_predefined()) el->m_margins.left = 0; + if(el->css().get_margins().right.is_predefined()) el->m_margins.right = 0; + left = css_left.calc_percent(containing_block_size.width); + right = css_right.calc_percent(containing_block_size.width); + if(el->src_el()->is_replaced()) + { + width = el->width() - el->content_offset_width(); + int reminded = (containing_block_size.width - left - right) - width - el->content_offset_width(); + if(reminded) + { + int divider = 0; + if (el->css().get_margins().left.is_predefined()) divider++; + if (el->css().get_margins().right.is_predefined()) divider++; + if (divider != 0) + { + if (el->css().get_margins().left.is_predefined()) el->m_margins.left = reminded / divider; + if (el->css().get_margins().right.is_predefined()) el->m_margins.right = reminded / divider; + } + } + width += el->content_offset_width(); + } else + { + width = containing_block_size.width - left - right; + } + if(!el->css().get_max_width().is_predefined()) + { + int max_width = el->css().get_max_width().calc_percent(containing_block_size.height); + if(width - el->content_offset_width() > max_width) + { + int reminded = width - el->content_offset_width() - max_width; + width = max_width; + int divider = 0; + if(el->css().get_margins().left.is_predefined()) divider++; + if(el->css().get_margins().right.is_predefined()) divider++; + if(divider != 0) + { + if(el->css().get_margins().left.is_predefined()) el->m_margins.left = reminded / divider; + if(el->css().get_margins().right.is_predefined()) el->m_margins.right = reminded / divider; + } + width += el->content_offset_width(); + } + } + } else if(!css_left.is_predefined() && css_right.is_predefined() && !el_width.is_predefined()) + { + // 6. 'right' is 'auto', 'left' and 'width' are not 'auto', then solve for 'right' + if(el->css().get_margins().left.is_predefined()) el->m_margins.left = 0; + if(el->css().get_margins().right.is_predefined()) el->m_margins.right = 0; + left = css_left.calc_percent(containing_block_size.width); + width = fix_width_min_max(el_width.calc_percent(containing_block_size.width)); + } else if(css_left.is_predefined() && css_right.is_predefined() && el_width.is_predefined()) + { + // If all three of 'left', 'width', and 'right' are 'auto': First set any 'auto' values for + // 'margin-left' and 'margin-right' to 0. Then, if the 'direction' property of the element + // establishing the static-position containing block is 'ltr' set 'left' to the static position + // and apply rule number three below; otherwise, set 'right' to the static position and apply + // rule number one below. + if(el->css().get_margins().left.is_predefined()) el->m_margins.left = 0; + if(el->css().get_margins().right.is_predefined()) el->m_margins.right = 0; + width = el->width(); + left = el_static_x - el->content_offset_left(); + } else + { + // If none of the three is 'auto': + width = fix_width_min_max(el_width.calc_percent(containing_block_size.width)); + left = css_left.calc_percent(containing_block_size.width); + right = css_right.calc_percent(containing_block_size.width); + int remained = containing_block_size.width - width - left - right; - if(!css_top.is_predefined() || !css_bottom.is_predefined()) - { - if(!css_top.is_predefined() && css_bottom.is_predefined()) - { - el->m_pos.y = css_top.calc_percent(containing_block_size.height) + el->content_offset_top() - m_padding.top; - } else if(css_top.is_predefined() && !css_bottom.is_predefined()) - { - el->m_pos.y = m_pos.height + m_padding.bottom - css_bottom.calc_percent(containing_block_size.height) - el->m_pos.height - - el->content_offset_bottom(); - } else - { - el->m_pos.y = css_top.calc_percent(containing_block_size.height) + el->content_offset_top() - m_padding.top; - el->m_pos.height = m_pos.height + m_padding.top + m_padding.bottom - - css_top.calc_percent(containing_block_size.height) - - css_bottom.calc_percent(containing_block_size.height) - - (el->content_offset_top() + el->content_offset_bottom()); - if (new_height != -1) - { - el->m_pos.y += (el->m_pos.height - new_height) / 2; - el->m_pos.height = new_height; - } - need_render = true; - } - cvt_y = true; - } - } + if(el->css().get_margins().left.is_predefined() && el->css().get_margins().right.is_predefined()) + { + // If both 'margin-left' and 'margin-right' are 'auto', solve the equation under the extra + // constraint that the two margins get equal values, unless this would make them negative, + // in which case when direction of the containing block is 'ltr' ('rtl'), set 'margin-left' + // ('margin-right') to zero and solve for 'margin-right' ('margin-left'). + el->m_margins.left = el->m_margins.right = remained / 2; + if(el->m_margins.left < 0) + { + el->m_margins.left = 0; + el->m_margins.right = remained; + } + width += el->m_margins.left + el->m_margins.right; + } else + { + // If one of 'margin-left' or 'margin-right' is 'auto', solve the equation + // for that value. If the values are over-constrained, ignore the value for 'left' + // (in case the 'direction' property of the containing block is 'rtl') or 'right' (in case + // 'direction' is 'ltr') and solve for that value. + if(el->css().get_margins().left.is_predefined()) + { + el->m_margins.left = remained; + width += el->m_margins.left; + } + if(el->css().get_margins().right.is_predefined()) + { + el->m_margins.right = remained; + width += el->m_margins.right; + } + } + } + el->m_pos.x = left + el->content_offset_left(); + if(el->m_pos.width != width - el->content_offset_width()) + { + el->m_pos.width = width - el->content_offset_width(); + need_render = true; + } - if(cvt_x || cvt_y) + if(el_position != element_position_fixed) { - int offset_x = 0; - int offset_y = 0; - auto cur_el = el->parent(); - auto this_el = shared_from_this(); - while(cur_el && cur_el != this_el) - { - offset_x += cur_el->m_pos.x; - offset_y += cur_el->m_pos.y; - cur_el = cur_el->parent(); - } - if(cvt_x) el->m_pos.x -= offset_x; - if(cvt_y) el->m_pos.y -= offset_y; + el->m_pos.x -= el_static_offset_x; + el->m_pos.y -= el_static_offset_y; } if(need_render) @@ -443,8 +647,8 @@ void litehtml::render_item::render_positioned(render_type rt) if(el_position == element_position_fixed) { - position fixed_pos; - el->get_redraw_box(fixed_pos); + position fixed_pos = el->pos(); + el->get_redraw_box(fixed_pos); src_el()->get_document()->add_fixed_box(fixed_pos); } } @@ -505,33 +709,45 @@ void litehtml::render_item::get_redraw_box(litehtml::position& pos, int x /*= 0* void litehtml::render_item::calc_document_size( litehtml::size& sz, litehtml::size& content_size, int x /*= 0*/, int y /*= 0*/ ) { - if(is_visible() && src_el()->css().get_position() != element_position_fixed) - { - sz.width = std::max(sz.width, x + right()); - sz.height = std::max(sz.height, y + bottom()); - - if(!src_el()->is_root() && !src_el()->is_body()) + if(css().get_display() != display_inline && css().get_display() != display_table_row) + { + if (is_visible() && src_el()->css().get_position() != element_position_fixed) { - content_size.width = std::max(content_size.width, x + right()); - content_size.height = std::max(content_size.height, y + bottom()); - } + sz.width = std::max(sz.width, x + right()); + sz.height = std::max(sz.height, y + bottom()); - // All children of tables and blocks with style other than "overflow: visible" are inside element. - // We can skip calculating size of children - if(src_el()->css().get_overflow() == overflow_visible && src_el()->css().get_display() != display_table) - { - for(auto& el : m_children) - { - el->calc_document_size(sz, content_size, x + m_pos.x, y + m_pos.y); - } - } + if (!src_el()->is_root() && !src_el()->is_body()) + { + content_size.width = std::max(content_size.width, x + right()); + content_size.height = std::max(content_size.height, y + bottom()); + } + + // All children of tables and blocks with style other than "overflow: visible" are inside element. + // We can skip calculating size of children + if (src_el()->css().get_overflow() == overflow_visible && src_el()->css().get_display() != display_table) + { + for (auto &el: m_children) + { + el->calc_document_size(sz, content_size, x + m_pos.x, y + m_pos.y); + } + } - if(src_el()->is_root() || src_el()->is_body()) + if (src_el()->is_root() || src_el()->is_body()) + { + content_size.width = std::max(content_size.width, x + right()); + content_size.height = std::max(content_size.height, y + bottom()); + } + } + } else + { + position::vector boxes; + get_inline_boxes(boxes); + for(auto& box : boxes) { - content_size.width += content_offset_right(); - content_size.height += content_offset_bottom(); + content_size.width = std::max(content_size.width, x + box.x + box.width); + content_size.height = std::max(content_size.height, y + box.y + box.height); } - } + } } void litehtml::render_item::draw_stacking_context( uint_ptr hdc, int x, int y, const position* clip, bool with_positioned ) @@ -615,12 +831,10 @@ void litehtml::render_item::draw_children(uint_ptr hdc, int x, int y, const posi if (el->src_el()->is_positioned() && el->src_el()->css().get_z_index() == zindex) { if (el->src_el()->css().get_position() == element_position_fixed) - { - position browser_wnd; - doc->container()->get_client_rect(browser_wnd); - - el->src_el()->draw(hdc, browser_wnd.x, browser_wnd.y, clip, el); - el->draw_stacking_context(hdc, browser_wnd.x, browser_wnd.y, clip, true); + { + // Fixed elements position is always relative to the (0,0) + el->src_el()->draw(hdc, 0, 0, clip, el); + el->draw_stacking_context(hdc, 0, 0, clip, true); } else { @@ -912,17 +1126,39 @@ void litehtml::render_item::get_rendering_boxes( position::vector& redraw_boxes) if(src_el()->css().get_position() != element_position_fixed) { - auto cur_el = parent(); + auto cur_el = parent(); + int add_x = 0; + int add_y = 0; + while(cur_el) - { - for(auto& box : redraw_boxes) - { - box.x += cur_el->m_pos.x; - box.y += cur_el->m_pos.y; - } + { + if(cur_el->css().get_position() == element_position_fixed) + { + position view_port; + src_el()->get_document()->container()->get_viewport(view_port); + add_x += cur_el->m_pos.x + view_port.left(); + add_y += cur_el->m_pos.y + view_port.top(); + break; + } + add_x += cur_el->m_pos.x; + add_y += cur_el->m_pos.y; cur_el = cur_el->parent(); } - } + for(auto& box : redraw_boxes) + { + box.x += add_x; + box.y += add_y; + } + } else + { + position view_port; + src_el()->get_document()->container()->get_viewport(view_port); + for(auto& box : redraw_boxes) + { + box.x += view_port.left(); + box.y += view_port.top(); + } + } } void litehtml::render_item::dump(litehtml::dumper& cout) @@ -988,7 +1224,7 @@ void litehtml::render_item::calc_cb_length(const css_length& len, int percent_ba out_value.type = litehtml::containing_block_context::cbc_value_type_percentage; } else { - out_value.value = src_el()->get_document()->to_pixels(len, src_el()->css().get_font_size()); + out_value.value = src_el()->get_document()->to_pixels(len, css().get_font_metrics(), 0); out_value.type = containing_block_context::cbc_value_type_absolute; } } @@ -1098,3 +1334,29 @@ litehtml::containing_block_context litehtml::render_item::calculate_containing_b return ret; } + +std::tuple<int, int> litehtml::render_item::element_static_offset(const std::shared_ptr<litehtml::render_item>& el) +{ + int offset_x = 0; + int offset_y = 0; + auto cur_el = el->parent(); + auto this_el = el->css().get_position() != element_position_fixed ? shared_from_this() : src_el()->get_document()->root_render(); + while(cur_el && cur_el != this_el) + { + offset_x += cur_el->m_pos.x; + offset_y += cur_el->m_pos.y; + cur_el = cur_el->parent(); + } + + if(el->css().get_position() == element_position_fixed || (is_root() && !src_el()->is_positioned())) + { + offset_x += this_el->m_pos.x; + offset_y += this_el->m_pos.y; + } else + { + offset_x += m_padding.left; + offset_y += m_padding.top; + } + + return {offset_x, offset_y}; +} diff --git a/src/render_table.cpp b/src/render_table.cpp index fdff1f826..4792f1497 100644 --- a/src/render_table.cpp +++ b/src/render_table.cpp @@ -1,4 +1,3 @@ -#include "html.h" #include "render_table.h" #include "document.h" #include "iterators.h" @@ -11,7 +10,7 @@ litehtml::render_item_table::render_item_table(std::shared_ptr<element> _src_el) { } -int litehtml::render_item_table::_render(int x, int y, const containing_block_context &containing_block_size, formatting_context* fmt_ctx, bool second_pass) +int litehtml::render_item_table::_render(int x, int y, const containing_block_context &containing_block_size, formatting_context* fmt_ctx, bool /*second_pass*/) { if (!m_grid) return 0; @@ -380,7 +379,7 @@ int litehtml::render_item_table::_render(int x, int y, const containing_block_co std::shared_ptr<litehtml::render_item> litehtml::render_item_table::init() { // Initialize Grid - m_grid = std::unique_ptr<table_grid>(new table_grid()); + m_grid = std::make_unique<table_grid>(); go_inside_table table_selector; table_rows_selector row_selector; @@ -388,7 +387,7 @@ std::shared_ptr<litehtml::render_item> litehtml::render_item_table::init() elements_iterator row_iter(false, &table_selector, &row_selector); - row_iter.process(shared_from_this(), [&](std::shared_ptr<render_item>& el, iterator_item_type item_type) + row_iter.process(shared_from_this(), [&](std::shared_ptr<render_item>& el, iterator_item_type /*item_type*/) { m_grid->begin_row(el); @@ -417,10 +416,10 @@ std::shared_ptr<litehtml::render_item> litehtml::render_item_table::init() if(src_el()->css().get_border_collapse() == border_collapse_separate) { - int font_size = src_el()->css().get_font_size(); + auto fm = css().get_font_metrics(); document::ptr doc = src_el()->get_document(); - m_border_spacing_x = doc->to_pixels(src_el()->css().get_border_spacing_x(), font_size); - m_border_spacing_y = doc->to_pixels(src_el()->css().get_border_spacing_y(), font_size); + m_border_spacing_x = doc->to_pixels(src_el()->css().get_border_spacing_x(), fm, 0); + m_border_spacing_y = doc->to_pixels(src_el()->css().get_border_spacing_y(), fm, 0); } else { m_border_spacing_x = 0; diff --git a/src/string_id.cpp b/src/string_id.cpp index 9f8390303..d197d2446 100644 --- a/src/string_id.cpp +++ b/src/string_id.cpp @@ -1,6 +1,6 @@ #include "html.h" #include "string_id.h" -#include <assert.h> +#include <cassert> #ifndef LITEHTML_NO_THREADS #include <mutex> diff --git a/src/style.cpp b/src/style.cpp index 356f49afc..65aefdaf8 100644 --- a/src/style.cpp +++ b/src/style.cpp @@ -1,9 +1,28 @@ #include "html.h" #include "style.h" +#include "css_parser.h" +#include "internal.h" +#include <set> +#include "html_tag.h" +#include "document.h" + + +// All functions here assume that whitespace have been removed. namespace litehtml { +bool parse_bg_image(const css_token& token, image& bg_image, document_container* container); +bool parse_bg_position_size(const css_token_vector& tokens, int& index, css_length& x, css_length& y, css_size& size); +bool parse_bg_size(const css_token_vector& tokens, int& index, css_size& size); +bool parse_two_lengths(const css_token_vector& tokens, css_length len[2], int options); +template<class T, class... Args> +int parse_1234_values(const css_token_vector& tokens, T result[4], bool (*func)(const css_token&, T&, Args...), Args... args); +int parse_1234_lengths(const css_token_vector& tokens, css_length len[4], int options, string keywords = ""); +bool parse_border_width(const css_token& tok, css_length& width); +bool parse_font_family(const css_token_vector& tokens, string& font_family); +bool parse_font_weight(const css_token& tok, css_length& weight); + std::map<string_id, string> style::m_valid_values = { { _display_, style_display_strings }, @@ -12,6 +31,7 @@ std::map<string_id, string> style::m_valid_values = { _float_, element_float_strings }, { _clear_, element_clear_strings }, { _overflow_, overflow_strings }, + { _appearance_, appearance_strings }, { _box_sizing_, box_sizing_strings }, { _text_align_, text_align_strings }, @@ -41,69 +61,149 @@ std::map<string_id, string> style::m_valid_values = { _flex_direction_, flex_direction_strings }, { _flex_wrap_, flex_wrap_strings }, { _justify_content_, flex_justify_content_strings }, - { _align_items_, flex_align_items_strings }, { _align_content_, flex_align_content_strings }, + { _align_items_, flex_align_items_strings }, { _align_self_, flex_align_items_strings }, { _caption_side_, caption_side_strings }, + + { _text_decoration_style_, style_text_decoration_style_strings }, + { _text_emphasis_position_, style_text_emphasis_position_strings }, +}; + +std::map<string_id, vector<string_id>> shorthands = +{ + { _font_, {_font_style_, _font_variant_, _font_weight_, _font_size_, _line_height_, _font_family_}}, + + { _background_, { + _background_color_, + _background_position_x_, + _background_position_y_, + _background_repeat_, + _background_attachment_, + _background_image_, + _background_image_baseurl_, + _background_size_, + _background_origin_, + _background_clip_ + } }, + + { _list_style_, {_list_style_image_, _list_style_image_baseurl_, _list_style_position_, _list_style_type_}}, + + { _margin_, {_margin_top_, _margin_right_, _margin_bottom_, _margin_left_}}, + { _padding_, {_padding_top_, _padding_right_, _padding_bottom_, _padding_left_}}, + + { _border_width_, {_border_top_width_, _border_right_width_, _border_bottom_width_, _border_left_width_}}, + { _border_style_, {_border_top_style_, _border_right_style_, _border_bottom_style_, _border_left_style_}}, + { _border_color_, {_border_top_color_, _border_right_color_, _border_bottom_color_, _border_left_color_}}, + { _border_top_, {_border_top_width_, _border_top_style_, _border_top_color_}}, + { _border_right_, {_border_right_width_, _border_right_style_, _border_right_color_}}, + { _border_bottom_, {_border_bottom_width_, _border_bottom_style_, _border_bottom_color_}}, + { _border_left_, {_border_left_width_, _border_left_style_, _border_left_color_}}, + { _border_, { + _border_top_width_, _border_right_width_, _border_bottom_width_, _border_left_width_, + _border_top_style_, _border_right_style_, _border_bottom_style_, _border_left_style_, + _border_top_color_, _border_right_color_, _border_bottom_color_, _border_left_color_ + } }, + + { _flex_, {_flex_grow_, _flex_shrink_, _flex_basis_}}, + { _flex_flow_, {_flex_direction_, _flex_wrap_}}, + + { _text_decoration_, {_text_decoration_color_, _text_decoration_line_, _text_decoration_style_, _text_decoration_thickness_}}, + { _text_emphasis_, {_text_emphasis_style_, _text_emphasis_color_}}, }; -void style::parse(const string& txt, const string& baseurl, document_container* container) +void style::add(const string& txt, const string& baseurl, document_container* container) +{ + auto tokens = normalize(txt, f_componentize); + add(tokens, baseurl, container); +} + +void style::add(const css_token_vector& tokens, const string& baseurl, document_container* container) { - std::vector<string> properties; - split_string(txt, properties, ";", "", "\"'"); + raw_declaration::vector decls; + raw_rule::vector rules; + css_parser(tokens).consume_style_block_contents(decls, rules); + if (!rules.empty()) + css_parse_error("rule inside a style block"); + if (decls.empty()) + return; - for(const auto & property : properties) + // Parse each declaration + for (auto& decl : decls) { - parse_property(property, baseurl, container); + remove_whitespace(decl.value); + // Note: decl.value is already componentized, see consume_qualified_rule and consume_style_block_contents. + // Note: decl.value may be empty. + string name = decl.name.substr(0, 2) == "--" ? decl.name : lowcase(decl.name); + add_property(_id(name), decl.value, baseurl, decl.important, container); } } -void style::parse_property(const string& txt, const string& baseurl, document_container* container) +bool has_var(const css_token_vector& tokens) { - string::size_type pos = txt.find_first_of(':'); - if(pos != string::npos) + for (auto& tok : tokens) { - string name = txt.substr(0, pos); - string val = txt.substr(pos + 1); - - trim(name); lcase(name); - trim(val); + if (tok.type == CV_FUNCTION && lowcase(tok.name) == "var") + return true; + if (tok.is_component_value() && has_var(tok.value)) + return true; + } + return false; +} - if(!name.empty() && !val.empty()) - { - string_vector vals; - split_string(val, vals, "!"); - if(vals.size() == 1) - { - add_property(_id(name), val, baseurl, false, container); - } else if(vals.size() > 1) - { - trim(vals[0]); - lcase(vals[1]); - add_property(_id(name), vals[0], baseurl, vals[1] == "important", container); - } - } +void style::inherit_property(string_id name, bool important) +{ + auto atomic_properties = at(shorthands, name); + if (!atomic_properties.empty()) + { + for (auto atomic : atomic_properties) + add_parsed_property(atomic, property_value(inherit(), important)); } + else + add_parsed_property(name, property_value(inherit(), important)); } -void style::add_property(string_id name, const string& val, const string& baseurl, bool important, document_container* container) +void style::add_length_property(string_id name, css_token val, string keywords, int options, bool important) { - if (val.find("var(") != -1) return add_parsed_property(name, property_value(val, important, prop_type_var)); - if (val == "inherit" && name != _font_) return add_parsed_property(name, property_value(important, prop_type_inherit)); + css_length length; + if (length.from_token(val, options, keywords)) + add_parsed_property(name, property_value(length, important)); +} - int idx; - string url; - css_length len[4], length; +// `value` is a list of component values with all whitespace tokens removed, including those inside component values +void style::add_property(string_id name, const css_token_vector& value, const string& baseurl, bool important, document_container* container) +{ + // Note: empty value is a valid value for a custom property. + if (value.empty() && _s(name).substr(0, 2) != "--") + return; + + if (has_var(value)) + return add_parsed_property(name, property_value(value, important, true)); + + // valid only if value contains a single token + css_token val = value.size() == 1 ? value[0] : css_token(); + // nonempty if value is a single identifier + string ident = val.ident(); + + if (ident == "inherit") + return inherit_property(name, important); + + int idx[4]; + web_color clr[4]; + css_length len[4]; + string str; switch (name) { - // keyword-only properties + // ============================= SINGLE KEYWORD ============================= + case _display_: case _visibility_: case _position_: case _float_: case _clear_: + case _appearance_: case _box_sizing_: case _overflow_: @@ -114,7 +214,7 @@ void style::add_property(string_id name, const string& val, const string& baseur case _font_style_: case _font_variant_: - case _font_weight_: + case _text_decoration_style_: case _list_style_type_: case _list_style_position_: @@ -132,1011 +232,1317 @@ void style::add_property(string_id name, const string& val, const string& baseur case _caption_side_: - idx = value_index(val, m_valid_values[name]); - if (idx >= 0) - { - add_parsed_property(name, property_value(idx, important)); - } + if (int index = value_index(ident, m_valid_values[name]); index >= 0) + add_parsed_property(name, property_value(index, important)); break; - case _align_items_: - case _align_self_: - parse_align_self(name, val, important); - break; + // ============================= LENGTH ============================= - // <length> + // auto | <integer> https://developer.mozilla.org/en-US/docs/Web/CSS/z-index#formal_syntax + case _z_index_: + return add_length_property(name, val, "auto", f_integer, important); + + // <length-percentage> https://developer.mozilla.org/en-US/docs/Web/CSS/text-indent#formal_syntax case _text_indent_: + return add_length_property(name, val, "", f_length_percentage, important); + + // <length-percentage [0,∞]> https://developer.mozilla.org/en-US/docs/Web/CSS/padding-left case _padding_left_: case _padding_right_: case _padding_top_: case _padding_bottom_: - length.fromString(val); - add_parsed_property(name, property_value(length, important)); - break; - - // <length> | auto + return add_length_property(name, val, "", f_length_percentage|f_positive, important); + + // auto | <length-percentage> https://developer.mozilla.org/en-US/docs/Web/CSS/left case _left_: case _right_: case _top_: case _bottom_: - case _z_index_: // <integer> | auto - case _width_: - case _height_: - case _min_width_: - case _min_height_: case _margin_left_: case _margin_right_: case _margin_top_: case _margin_bottom_: - length.fromString(val, "auto", -1); - add_parsed_property(name, property_value(length, important)); - break; + return add_length_property(name, val, "auto", f_length_percentage, important); + + // auto | min-content | max-content | fit-content | <length-percentage [0,∞]> https://developer.mozilla.org/en-US/docs/Web/CSS/width#formal_syntax + case _width_: + case _height_: + case _min_width_: + case _min_height_: + return add_length_property(name, val, "auto", f_length_percentage|f_positive, important); - // <length> | none + // none | min-content | max-content | fit-content | <length-percentage [0,∞]> https://developer.mozilla.org/en-US/docs/Web/CSS/max-width#formal_syntax case _max_width_: case _max_height_: - length.fromString(val, "none", -1); - add_parsed_property(name, property_value(length, important)); - break; - + return add_length_property(name, val, "none", f_length_percentage|f_positive, important); + + // normal | <number [0,∞]> | <length-percentage [0,∞]> https://developer.mozilla.org/en-US/docs/Web/CSS/line-height#formal_syntax case _line_height_: - length.fromString(val, "normal", -1); - add_parsed_property(name, property_value(length, important)); - break; + return add_length_property(name, val, "normal", f_number|f_length_percentage|f_positive, important); + // font-size = <absolute-size> | <relative-size> | <length-percentage [0,∞]> https://developer.mozilla.org/en-US/docs/Web/CSS/font-size#formal_syntax case _font_size_: - length.fromString(val, font_size_strings, -1); - add_parsed_property(name, property_value(length, important)); + return add_length_property(name, val, font_size_strings, f_length_percentage|f_positive, important); + + case _margin_: + if (int n = parse_1234_lengths(value, len, f_length_percentage, "auto")) + add_four_properties(_margin_top_, len, n, important); break; - // Parse background shorthand properties - case _background_: - parse_background(val, baseurl, important, container); + case _padding_: + if (int n = parse_1234_lengths(value, len, f_length_percentage | f_positive)) + add_four_properties(_padding_top_, len, n, important); break; - case _background_image_: - parse_background_image(val, baseurl, important); + // ============================= COLOR ============================= + + case _color_: + if (ident == "currentcolor") return inherit_property(name, important); + // fallthrough + case _background_color_: + case _border_top_color_: + case _border_bottom_color_: + case _border_left_color_: + case _border_right_color_: + if (parse_color(val, *clr, container)) + add_parsed_property(name, property_value(*clr, important)); break; - case _background_attachment_: - case _background_repeat_: - case _background_clip_: - case _background_origin_: - parse_keyword_comma_list(name, val, important); + // ============================= BACKGROUND ============================= + + case _background_: + parse_background(value, baseurl, important, container); + break; + + case _background_image_: + parse_background_image(value, baseurl, important, container); break; case _background_position_: - parse_background_position(val, important); + parse_background_position(value, important); break; case _background_size_: - parse_background_size(val, important); + parse_background_size(value, important); break; - // Parse border spacing properties - case _border_spacing_: - parse_two_lengths(val, len); - add_parsed_property(__litehtml_border_spacing_x_, property_value(len[0], important)); - add_parsed_property(__litehtml_border_spacing_y_, property_value(len[1], important)); + case _background_repeat_: + case _background_attachment_: + case _background_origin_: + case _background_clip_: + parse_keyword_comma_list(name, value, important); break; - // Parse borders shorthand properties + // ============================= BORDER ============================= + case _border_: - { - string_vector tokens; - split_string(val, tokens, " ", "", "("); - for (const auto& token : tokens) - { - int idx = value_index(token, border_style_strings); - if (idx >= 0) - { - property_value style(idx, important); - add_parsed_property(_border_left_style_, style); - add_parsed_property(_border_right_style_, style); - add_parsed_property(_border_top_style_, style); - add_parsed_property(_border_bottom_style_, style); - } - else if (t_isdigit(token[0]) || token[0] == '.' || - value_in_list(token, border_width_strings)) - { - property_value width(parse_border_width(token), important); - add_parsed_property(_border_left_width_, width); - add_parsed_property(_border_right_width_, width); - add_parsed_property(_border_top_width_, width); - add_parsed_property(_border_bottom_width_, width); - } - else if (web_color::is_color(token, container)) - { - web_color _color = web_color::from_string(token, container); - property_value color(_color, important); - add_parsed_property(_border_left_color_, color); - add_parsed_property(_border_right_color_, color); - add_parsed_property(_border_top_color_, color); - add_parsed_property(_border_bottom_color_, color); - } - } + parse_border(value, important, container); break; - } case _border_left_: case _border_right_: case _border_top_: case _border_bottom_: - { - string_vector tokens; - split_string(val, tokens, " ", "", "("); - for (const auto& token : tokens) - { - int idx = value_index(token, border_style_strings); - if (idx >= 0) - { - add_parsed_property(_id(_s(name) + "-style"), property_value(idx, important)); - } - else if (t_isdigit(token[0]) || token[0] == '.' || - value_in_list(token, border_width_strings)) - { - property_value width(parse_border_width(token), important); - add_parsed_property(_id(_s(name) + "-width"), width); - } - else if (web_color::is_color(token, container)) - { - web_color color = web_color::from_string(token, container); - add_parsed_property(_id(_s(name) + "-color"), property_value(color, important)); - } - } + parse_border_side(name, value, important, container); break; - } - // Parse border-width/style/color shorthand properties case _border_width_: + if (int n = parse_1234_values(value, len, parse_border_width)) + add_four_properties(_border_top_width_, len, n, important); + break; case _border_style_: + if (int n = parse_1234_values(value, idx, parse_keyword, (string)border_style_strings, 0)) + add_four_properties(_border_top_style_, idx, n, important); + break; case _border_color_: - { - string prop = name == _border_width_ ? "-width" : name == _border_style_ ? "-style" : "-color"; - - string_vector tokens; - split_string(val, tokens, " "); - if (tokens.size() == 4) - { - add_property(_id("border-top" + prop), tokens[0], baseurl, important, container); - add_property(_id("border-right" + prop), tokens[1], baseurl, important, container); - add_property(_id("border-bottom" + prop), tokens[2], baseurl, important, container); - add_property(_id("border-left" + prop), tokens[3], baseurl, important, container); - } - else if (tokens.size() == 3) - { - add_property(_id("border-top" + prop), tokens[0], baseurl, important, container); - add_property(_id("border-right" + prop), tokens[1], baseurl, important, container); - add_property(_id("border-left" + prop), tokens[1], baseurl, important, container); - add_property(_id("border-bottom" + prop), tokens[2], baseurl, important, container); - } - else if (tokens.size() == 2) - { - add_property(_id("border-top" + prop), tokens[0], baseurl, important, container); - add_property(_id("border-bottom" + prop), tokens[0], baseurl, important, container); - add_property(_id("border-right" + prop), tokens[1], baseurl, important, container); - add_property(_id("border-left" + prop), tokens[1], baseurl, important, container); - } - else if (tokens.size() == 1) - { - add_property(_id("border-top" + prop), tokens[0], baseurl, important, container); - add_property(_id("border-bottom" + prop), tokens[0], baseurl, important, container); - add_property(_id("border-right" + prop), tokens[0], baseurl, important, container); - add_property(_id("border-left" + prop), tokens[0], baseurl, important, container); - } + if (int n = parse_1234_values(value, clr, parse_color, container)) + add_four_properties(_border_top_color_, clr, n, important); break; - } case _border_top_width_: case _border_bottom_width_: case _border_left_width_: case _border_right_width_: - length = parse_border_width(val); - add_parsed_property(name, property_value(length, important)); - break; - - case _color_: - case _background_color_: - case _border_top_color_: - case _border_bottom_color_: - case _border_left_color_: - case _border_right_color_: - if (web_color::is_color(val, container)) - { - web_color color = web_color::from_string(val, container); - add_parsed_property(name, property_value(color, important)); - } + if (parse_border_width(val, *len)) + add_parsed_property(name, property_value(*len, important)); break; - // Parse border radius shorthand properties + // border-bottom-left-radius = <length-percentage [0,∞]>{1,2} https://developer.mozilla.org/en-US/docs/Web/CSS/border-bottom-left-radius case _border_bottom_left_radius_: case _border_bottom_right_radius_: case _border_top_right_radius_: case _border_top_left_radius_: - parse_two_lengths(val, len); - add_parsed_property(_id(_s(name) + "-x"), property_value(len[0], important)); - add_parsed_property(_id(_s(name) + "-y"), property_value(len[1], important)); - break; - - // Parse border-radius shorthand properties - case _border_radius_: - { - string_vector tokens; - split_string(val, tokens, "/"); - if (tokens.size() == 1) - { - add_property(_border_radius_x_, tokens[0], baseurl, important, container); - add_property(_border_radius_y_, tokens[0], baseurl, important, container); - } - else if (tokens.size() >= 2) + if (parse_two_lengths(value, len, f_length_percentage | f_positive)) { - add_property(_border_radius_x_, tokens[0], baseurl, important, container); - add_property(_border_radius_y_, tokens[1], baseurl, important, container); + add_parsed_property(_id(_s(name) + "-x"), property_value(len[0], important)); + add_parsed_property(_id(_s(name) + "-y"), property_value(len[1], important)); } break; - } + case _border_radius_x_: case _border_radius_y_: { - string_id top_left, top_right, bottom_right, bottom_left; - if (name == _border_radius_x_) - { - top_left = _border_top_left_radius_x_; - top_right = _border_top_right_radius_x_; - bottom_right = _border_bottom_right_radius_x_; - bottom_left = _border_bottom_left_radius_x_; - } - else - { - top_left = _border_top_left_radius_y_; - top_right = _border_top_right_radius_y_; - bottom_right = _border_bottom_right_radius_y_; - bottom_left = _border_bottom_left_radius_y_; - } + string_id top_left = name == _border_radius_x_ ? + _border_top_left_radius_x_ : + _border_top_left_radius_y_; - switch (parse_four_lengths(val, len)) - { - case 1: - add_parsed_property(top_left, property_value(len[0], important)); - add_parsed_property(top_right, property_value(len[0], important)); - add_parsed_property(bottom_right, property_value(len[0], important)); - add_parsed_property(bottom_left, property_value(len[0], important)); - break; - case 2: - add_parsed_property(top_left, property_value(len[0], important)); - add_parsed_property(top_right, property_value(len[1], important)); - add_parsed_property(bottom_right, property_value(len[0], important)); - add_parsed_property(bottom_left, property_value(len[1], important)); - break; - case 3: - add_parsed_property(top_left, property_value(len[0], important)); - add_parsed_property(top_right, property_value(len[1], important)); - add_parsed_property(bottom_right, property_value(len[2], important)); - add_parsed_property(bottom_left, property_value(len[1], important)); - break; - case 4: - add_parsed_property(top_left, property_value(len[0], important)); - add_parsed_property(top_right, property_value(len[1], important)); - add_parsed_property(bottom_right, property_value(len[2], important)); - add_parsed_property(bottom_left, property_value(len[3], important)); - break; - } + if (int n = parse_1234_lengths(value, len, f_length_percentage | f_positive)) + add_four_properties(top_left, len, n, important); break; } - // Parse list-style shorthand properties - case _list_style_: - { - add_parsed_property(_list_style_type_, property_value(list_style_type_disc, important)); - add_parsed_property(_list_style_position_, property_value(list_style_position_outside, important)); - add_parsed_property(_list_style_image_, property_value("", important)); - add_parsed_property(_list_style_image_baseurl_, property_value("", important)); - - string_vector tokens; - split_string(val, tokens, " ", "", "("); - for (const auto& token : tokens) + case _border_radius_: + parse_border_radius(value, important); + break; + + // border-spacing = <length>{1,2} https://developer.mozilla.org/en-US/docs/Web/CSS/border-spacing + // Lengths may not be negative. https://drafts.csswg.org/css2/#separated-borders + case _border_spacing_: + if (parse_two_lengths(value, len, f_length | f_positive)) { - int idx = value_index(token, list_style_type_strings); - if (idx >= 0) - { - add_parsed_property(_list_style_type_, property_value(idx, important)); - } - else - { - idx = value_index(token, list_style_position_strings); - if (idx >= 0) - { - add_parsed_property(_list_style_position_, property_value(idx, important)); - } - else if (!strncmp(token.c_str(), "url", 3)) - { - css::parse_css_url(token, url); - add_parsed_property(_list_style_image_, property_value(url, important)); - add_parsed_property(_list_style_image_baseurl_, property_value(baseurl, important)); - } - } + add_parsed_property(__litehtml_border_spacing_x_, property_value(len[0], important)); + add_parsed_property(__litehtml_border_spacing_y_, property_value(len[1], important)); } break; - } - case _list_style_image_: - css::parse_css_url(val, url); - add_parsed_property(_list_style_image_, property_value(url, important)); - add_parsed_property(_list_style_image_baseurl_, property_value(baseurl, important)); - break; + // ============================= LIST ============================= - // Parse margin and padding shorthand properties - case _margin_: - case _padding_: - { - switch (parse_four_lengths(val, len)) + case _list_style_image_: + if (string url; parse_list_style_image(val, url)) { - case 4: - add_parsed_property(_id(_s(name) + "-top"), property_value(len[0], important)); - add_parsed_property(_id(_s(name) + "-right"), property_value(len[1], important)); - add_parsed_property(_id(_s(name) + "-bottom"), property_value(len[2], important)); - add_parsed_property(_id(_s(name) + "-left"), property_value(len[3], important)); - break; - case 3: - add_parsed_property(_id(_s(name) + "-top"), property_value(len[0], important)); - add_parsed_property(_id(_s(name) + "-right"), property_value(len[1], important)); - add_parsed_property(_id(_s(name) + "-left"), property_value(len[1], important)); - add_parsed_property(_id(_s(name) + "-bottom"), property_value(len[2], important)); - break; - case 2: - add_parsed_property(_id(_s(name) + "-top"), property_value(len[0], important)); - add_parsed_property(_id(_s(name) + "-bottom"), property_value(len[0], important)); - add_parsed_property(_id(_s(name) + "-right"), property_value(len[1], important)); - add_parsed_property(_id(_s(name) + "-left"), property_value(len[1], important)); - break; - case 1: - add_parsed_property(_id(_s(name) + "-top"), property_value(len[0], important)); - add_parsed_property(_id(_s(name) + "-bottom"), property_value(len[0], important)); - add_parsed_property(_id(_s(name) + "-right"), property_value(len[0], important)); - add_parsed_property(_id(_s(name) + "-left"), property_value(len[0], important)); - break; + add_parsed_property(_list_style_image_, property_value(url, important)); + add_parsed_property(_list_style_image_baseurl_, property_value(baseurl, important)); } break; - } - // Parse font shorthand properties + case _list_style_: + parse_list_style(value, baseurl, important); + break; + + // ============================= FONT ============================= + case _font_: - parse_font(val, important); + parse_font(value, important); break; - // Parse flex-flow shorthand properties - case _flex_flow_: - { - string_vector tokens; - split_string(val, tokens, " "); - for (const auto& tok : tokens) - { - int idx; - if ((idx = value_index(tok, flex_direction_strings)) >= 0) - { - add_parsed_property(_flex_direction_, property_value(idx, important)); - } - else if ((idx = value_index(tok, flex_wrap_strings)) >= 0) - { - add_parsed_property(_flex_wrap_, property_value(idx, important)); - } - } + case _font_family_: + if (parse_font_family(value, str)) + add_parsed_property(name, property_value(str, important)); + break; + + case _font_weight_: + if (parse_font_weight(val, *len)) + add_parsed_property(name, property_value(*len, important)); + break; + + case _text_decoration_: + parse_text_decoration(value, important, container); + break; + + case _text_decoration_thickness_: + add_length_property(name, val, style_text_decoration_thickness_strings, f_length_percentage|f_positive, important); break; - } - // Parse flex shorthand properties + case _text_decoration_color_: + parse_text_decoration_color(val, important, container); + break; + + case _text_decoration_line_: + parse_text_decoration_line(value, important); + break; + + case _text_emphasis_: + parse_text_emphasis(value, important, container); + break; + + case _text_emphasis_style_: + str = get_repr(value, 0, -1, true); + add_parsed_property(name, property_value(str, important)); + break; + + case _text_emphasis_color_: + parse_text_emphasis_color(val, important, container); + break; + + case _text_emphasis_position_: + parse_text_emphasis_position(value, important); + break; + + // ============================= FLEX ============================= + case _flex_: - parse_flex(val, important); + parse_flex(value, important); break; - case _flex_grow_: + case _flex_grow_: // <number [0,∞]> case _flex_shrink_: - add_parsed_property(name, property_value(t_strtof(val), important)); + if (val.type == NUMBER && val.n.number >= 0) + add_parsed_property(name, property_value(val.n.number, important)); break; case _flex_basis_: - length.fromString(val, flex_basis_strings, -1); - add_parsed_property(_flex_basis_, property_value(length, important)); + add_length_property(name, val, flex_basis_strings, f_length_percentage|f_positive, important); + break; + + case _flex_flow_: + parse_flex_flow(value, important); + break; + + case _align_items_: + case _align_self_: + parse_align_self(name, value, important); break; case _order_: // <integer> - { - char* end; - int int_val = (int) strtol(val.c_str(), &end, 10); - if(end[0] == '\0') - { - add_parsed_property(name, property_value(int_val, important)); - } - } + if (val.type == NUMBER && val.n.number_type == css_number_integer) + add_parsed_property(name, property_value((int)val.n.number, important)); break; + + // ============================= COUNTER, CONTENT ============================= + case _counter_increment_: case _counter_reset_: - { - string_vector tokens; - split_string(val, tokens, " "); - add_parsed_property(name, property_value(tokens, important)); + { + // TODO: parse it properly here + string_vector strings; + for (const auto& tok : value) strings.push_back(tok.get_repr(true)); + add_parsed_property(name, property_value(strings, important)); break; } + case _content_: + // TODO: parse it properly here + str = get_repr(value, 0, -1, true); + add_parsed_property(name, property_value(str, important)); + break; + + // ================================== OTHER ================================== + + case _cursor_: + str = get_repr(value, 0, -1, true); + add_parsed_property(name, property_value(str, important)); + break; + + // ============================= CUSTOM PROPERTY ============================= + + // https://drafts.csswg.org/css-variables-2/#defining-variables default: - add_parsed_property(name, property_value(val, important)); + if (_s(name).substr(0, 2) == "--" && _s(name).size() >= 3 && + (value.empty() || is_declaration_value(value))) + add_parsed_property(name, property_value(value, important)); } } -css_length style::parse_border_width(const string& str) +void style::add_property(string_id name, const string& value, const string& baseurl, bool important, document_container* container) { - css_length len; - if (t_isdigit(str[0]) || str[0] == '.') - { - len.fromString(str); - } - else - { - int idx = value_index(str, border_width_strings); - if (idx >= 0) - { - len.set_value(border_width_values[idx], css_units_px); - } - } - return len; + auto tokens = normalize(value, f_componentize | f_remove_whitespace); + add_property(name, tokens, baseurl, important, container); } -void style::parse_two_lengths(const string& str, css_length len[2]) +// This should be the same as parse_bg_image, but list-style-image is currently a string (not an image). +bool style::parse_list_style_image(const css_token& tok, string& url) { - string_vector tokens; - split_string(str, tokens, " "); - if (tokens.size() == 1) - { - css_length length; - length.fromString(tokens[0]); - len[0] = len[1] = length; - } - else if (tokens.size() == 2) + if (tok.ident() == "none") { - len[0].fromString(tokens[0]); - len[1].fromString(tokens[1]); + url = ""; + return true; } + + return parse_url(tok, url); } -int style::parse_four_lengths(const string& str, css_length len[4]) +// https://drafts.csswg.org/css-lists/#list-style-property +// <'list-style-position'> || <'list-style-image'> || <'list-style-type'> +void style::parse_list_style(const css_token_vector& tokens, string baseurl, bool important) { - string_vector tokens; - split_string(str, tokens, " "); - if (tokens.size() == 0 || tokens.size() > 4) + // initial values: https://developer.mozilla.org/en-US/docs/Web/CSS/list-style#formal_definition + int type = list_style_type_disc; + int position = list_style_position_outside; + string image = ""; // none + + bool type_found = false; + bool position_found = false; + bool image_found = false; + int none_count = 0; + + for (const auto& token : tokens) { - return 0; + // "...none is a valid value for both list-style-image and list-style-type. To resolve this ambiguity, + // a value of none ... must be applied to whichever of the two properties aren’t otherwise set by the shorthand." + if (token.ident() == "none") { + none_count++; + continue; + } + if (!type_found && parse_keyword(token, type, list_style_type_strings)) + type_found = true; + else if (!position_found && parse_keyword(token, position, list_style_position_strings)) + position_found = true; + else if (!image_found && parse_list_style_image(token, image)) + image_found = true; + else + return; // syntax error } - for (size_t i = 0; i < tokens.size(); i++) + + switch (none_count) { - len[i].fromString(tokens[i]); + case 0: + break; + case 1: + if (type_found && image_found) return; + if (!type_found) type = list_style_type_none; + break; + case 2: + if (type_found || image_found) return; + type = list_style_type_none; + break; + default: + return; // syntax error } - return (int)tokens.size(); + + add_parsed_property(_list_style_type_, property_value(type, important)); + add_parsed_property(_list_style_position_, property_value(position, important)); + add_parsed_property(_list_style_image_, property_value(image, important)); + add_parsed_property(_list_style_image_baseurl_, property_value(baseurl, important)); } -void style::parse_background(const string& val, const string& baseurl, bool important, document_container* container) +// https://developer.mozilla.org/en-US/docs/Web/CSS/border-radius +// border-radius = <length-percentage [0,∞]>{1,4} [ / <length-percentage [0,∞]>{1,4} ]? +void style::parse_border_radius(const css_token_vector& tokens, bool important) { - string_vector tokens; - split_string(val, tokens, ",", "", "("); - if (tokens.empty()) return; + int i; + for (i = 0; i < (int)tokens.size() && tokens[i].ch != '/'; i++) {} - web_color color; - string_vector images; - int_vector repeats, origins, clips, attachments; + if (i == (int)tokens.size()) // no '/' + { + css_length len[4]; + if (int n = parse_1234_lengths(tokens, len, f_length_percentage | f_positive)) + { + add_four_properties(_border_top_left_radius_x_, len, n, important); + add_four_properties(_border_top_left_radius_y_, len, n, important); + } + } + else + { + auto raduis_x = slice(tokens, 0, i); + auto raduis_y = slice(tokens, i + 1); + + css_length rx[4], ry[4]; + int n = parse_1234_lengths(raduis_x, rx, f_length_percentage | f_positive); + int m = parse_1234_lengths(raduis_y, ry, f_length_percentage | f_positive); + + if (n && m) + { + add_four_properties(_border_top_left_radius_x_, rx, n, important); + add_four_properties(_border_top_left_radius_y_, ry, m, important); + } + } +} + +bool parse_border_width(const css_token& token, css_length& w) +{ + css_length width; + if (!width.from_token(token, f_length | f_positive, border_width_strings)) + return false; + + if (width.is_predefined()) + width.set_value(border_width_values[width.predef()], css_units_px); + + w = width; + return true; +} + +// https://drafts.csswg.org/css-backgrounds/#propdef-border +// <line-width> || <line-style> || <color> +// <line-width> = <length [0,∞]> | thin | medium | thick +// <line-style> = none | hidden | dotted | dashed | solid | double | groove | ridge | inset | outset +bool parse_border_helper(const css_token_vector& tokens, document_container* container, + css_length& width, border_style& style, web_color& color) +{ + // initial values: https://developer.mozilla.org/en-US/docs/Web/CSS/border#formal_definition + css_length _width = border_width_medium_value; + border_style _style = border_style_none; + web_color _color = web_color::current_color; + + bool width_found = false; + bool style_found = false; + bool color_found = false; + + for (const auto& token : tokens) + { + if (!width_found && parse_border_width(token, _width)) + width_found = true; + else if (!style_found && parse_keyword(token, _style, border_style_strings)) + style_found = true; + else if (!color_found && parse_color(token, _color, container)) + color_found = true; + else + return false; + } + + width = _width; + style = _style; + color = _color; + return true; +} + +void style::parse_border(const css_token_vector& tokens, bool important, document_container* container) +{ + css_length width; + border_style style; + web_color color; + + if (!parse_border_helper(tokens, container, width, style, color)) + return; + + for (auto name : {_border_left_width_, _border_right_width_, _border_top_width_, _border_bottom_width_}) + add_parsed_property(name, property_value(width, important)); + + for (auto name : {_border_left_style_, _border_right_style_, _border_top_style_, _border_bottom_style_}) + add_parsed_property(name, property_value(style, important)); + + for (auto name : {_border_left_color_, _border_right_color_, _border_top_color_, _border_bottom_color_}) + add_parsed_property(name, property_value(color, important)); +} + +// https://drafts.csswg.org/css-backgrounds/#border-shorthands +// border-top, border-right, border-bottom, border-left +void style::parse_border_side(string_id name, const css_token_vector& tokens, bool important, document_container* container) +{ + css_length width; + border_style style; + web_color color; + + if (!parse_border_helper(tokens, container, width, style, color)) + return; + + add_parsed_property(_id(_s(name) + "-width"), property_value(width, important)); + add_parsed_property(_id(_s(name) + "-style"), property_value(style, important)); + add_parsed_property(_id(_s(name) + "-color"), property_value(color, important)); +} + +bool parse_length(const css_token& tok, css_length& length, int options, string keywords) +{ + return length.from_token(tok, options, keywords); +} + +// parses 1 or 2 lengths, but always returns 2 lengths +bool parse_two_lengths(const css_token_vector& tokens, css_length len[2], int options) +{ + auto n = tokens.size(); + if (n != 1 && n != 2) return false; + + css_length a, b; + if (!a.from_token(tokens[0], options)) return false; + if (n == 1) b = a; + + if (n == 2 && !b.from_token(tokens[1], options)) return false; + + len[0] = a; + len[1] = b; + return true; +} + +// parses 1,2,3 or 4 tokens, returns number of tokens parsed or 0 if error +template<class T, class... Args> +int parse_1234_values(const css_token_vector& tokens, T result[4], bool (*parse)(const css_token&, T&, Args...), Args... args) +{ + if (tokens.size() > 4) + return 0; + + for (size_t i = 0; i < tokens.size(); i++) + { + if (!parse(tokens[i], result[i], args...)) + return 0; + } + return (int)tokens.size(); +} + +int parse_1234_lengths(const css_token_vector& tokens, css_length len[4], int options, string keywords) +{ + return parse_1234_values(tokens, len, parse_length, options, keywords); +} + +// This function implements the logic of the kind "if two values are specified, the first one applies to +// top and bottom, the second one to left and right". Works in conjunction with parse_1234_values. +template<class T> +void style::add_four_properties(string_id top_name, T val[4], int n, bool important) +{ + // These always go in trbl order, see comment for "CSS property names" in string_id. + string_id top = top_name; // top-left for corners + string_id right = string_id(top_name + 1); + string_id bottom = string_id(top_name + 2); + string_id left = string_id(top_name + 3); + + // n 4 3 2 1 + // top 0 0 0 0 0 + // right 1 1 1 0 n>1 + // bottom 2 2 0 0 n/3*2 + // left 3 1 1 0 n/2+n/4 + + add_parsed_property(top, property_value(val[0], important)); + add_parsed_property(right, property_value(val[n>1], important)); + add_parsed_property(bottom, property_value(val[n/3*2], important)); + add_parsed_property(left, property_value(val[n/2+n/4], important)); +} + +void style::parse_background(const css_token_vector& tokens, const string& baseurl, bool important, document_container* container) +{ + auto layers = parse_comma_separated_list(tokens); + if (layers.empty()) return; + + web_color color; + std::vector<image> images; length_vector x_positions, y_positions; size_vector sizes; + int_vector repeats, attachments, origins, clips; - for (const auto& token : tokens) + for (size_t i = 0; i < layers.size(); i++) { background bg; - if (!parse_one_background(token, container, bg)) + if (!parse_bg_layer(layers[i], container, bg, i == layers.size() - 1)) return; - + color = bg.m_color; images.push_back(bg.m_image[0]); - repeats.push_back(bg.m_repeat[0]); - origins.push_back(bg.m_origin[0]); - clips.push_back(bg.m_clip[0]); - attachments.push_back(bg.m_attachment[0]); x_positions.push_back(bg.m_position_x[0]); y_positions.push_back(bg.m_position_y[0]); sizes.push_back(bg.m_size[0]); + repeats.push_back(bg.m_repeat[0]); + attachments.push_back(bg.m_attachment[0]); + origins.push_back(bg.m_origin[0]); + clips.push_back(bg.m_clip[0]); } add_parsed_property(_background_color_, property_value(color, important)); add_parsed_property(_background_image_, property_value(images, important)); add_parsed_property(_background_image_baseurl_, property_value(baseurl, important)); - add_parsed_property(_background_repeat_, property_value(repeats, important)); - add_parsed_property(_background_origin_, property_value(origins, important)); - add_parsed_property(_background_clip_, property_value(clips, important)); - add_parsed_property(_background_attachment_, property_value(attachments, important)); add_parsed_property(_background_position_x_, property_value(x_positions, important)); add_parsed_property(_background_position_y_, property_value(y_positions, important)); add_parsed_property(_background_size_, property_value(sizes, important)); + add_parsed_property(_background_repeat_, property_value(repeats, important)); + add_parsed_property(_background_attachment_, property_value(attachments, important)); + add_parsed_property(_background_origin_, property_value(origins, important)); + add_parsed_property(_background_clip_, property_value(clips, important)); } -bool style::parse_one_background(const string& val, document_container* container, background& bg) +// https://drafts.csswg.org/css-backgrounds/#typedef-bg-layer +// <bg-layer> = <bg-image> || <bg-position> [ / <bg-size> ]? || <repeat-style> || <attachment> || <visual-box> || <visual-box> +// <final-bg-layer> = <bg-layer> || <'background-color'> +bool style::parse_bg_layer(const css_token_vector& tokens, document_container* container, background& bg, bool final_layer) { bg.m_color = web_color::transparent; - bg.m_image = {""}; - bg.m_repeat = { background_repeat_repeat }; - bg.m_origin = { background_box_padding }; - bg.m_clip = { background_box_border }; - bg.m_attachment = { background_attachment_scroll }; + bg.m_image = {{}}; bg.m_position_x = { css_length(0, css_units_percentage) }; bg.m_position_y = { css_length(0, css_units_percentage) }; bg.m_size = { css_size(css_length::predef_value(background_size_auto), css_length::predef_value(background_size_auto)) }; - - if(val == "none") - { - return true; - } - - string_vector tokens; - split_string(val, tokens, " \t\n\r", "", "("); + bg.m_repeat = { background_repeat_repeat }; + bg.m_attachment = { background_attachment_scroll }; + bg.m_origin = { background_box_padding }; + bg.m_clip = { background_box_border }; bool color_found = false; bool image_found = false; - bool origin_found = false; - bool clip_found = false; + bool position_found = false; bool repeat_found = false; bool attachment_found = false; + bool origin_found = false; + bool clip_found = false; - string position; - for(const auto& token : tokens) + for (int i = 0; i < (int)tokens.size(); i++) { - int idx; - if(token.substr(0, 3) == "url") - { - if (image_found) return false; - string url; - css::parse_css_url(token, url); - bg.m_image = { url }; + if (!color_found && final_layer && parse_color(tokens[i], bg.m_color, container)) + color_found = true; + else if (!image_found && parse_bg_image(tokens[i], bg.m_image[0], container)) image_found = true; - } else if( (idx = value_index(token, background_repeat_strings)) >= 0 ) - { - if (repeat_found) return false; - bg.m_repeat = { idx }; + else if (!position_found && parse_bg_position_size(tokens, i, bg.m_position_x[0], bg.m_position_y[0], bg.m_size[0])) + position_found = true, i--; + // Note: double keyword <repeat-style> values are not supported yet. + else if (!repeat_found && parse_keyword(tokens[i], bg.m_repeat[0], background_repeat_strings)) repeat_found = true; - } else if( (idx = value_index(token, background_attachment_strings)) >= 0 ) - { - if (attachment_found) return false; - bg.m_attachment = { idx }; + else if (!attachment_found && parse_keyword(tokens[i], bg.m_attachment[0], background_attachment_strings)) attachment_found = true; - } else if( (idx = value_index(token, background_box_strings)) >= 0 ) - { - if(!origin_found) - { - bg.m_origin = { idx }; - origin_found = true; - } else - { - if (clip_found) return false; - bg.m_clip = { idx }; - clip_found = true; - } - } else if( value_in_list(token, background_position_strings) || - token.find('/') != -1 || - t_isdigit(token[0]) || - token[0] == '+' || - token[0] == '-' || - token[0] == '.' ) - { - position += " " + token; - } else if (web_color::is_color(token, container)) - { - if (color_found) return false; - bg.m_color = web_color::from_string(token, container); - color_found = true; - } + // If one <visual-box> value is present then it sets both background-origin and background-clip to that value. + // If two values are present, then the first sets background-origin and the second background-clip. + else if (!origin_found && parse_keyword(tokens[i], bg.m_origin[0], background_box_strings)) + origin_found = true, bg.m_clip[0] = bg.m_origin[0]; + else if (!clip_found && parse_keyword(tokens[i], bg.m_clip[0], background_box_strings)) + clip_found = true; else - { return false; - } } - - if (position != "") + return true; +} + +// <bg-position> [ / <bg-size> ]? +bool parse_bg_position_size(const css_token_vector& tokens, int& index, css_length& x, css_length& y, css_size& size) +{ + if (!parse_bg_position(tokens, index, x, y, true)) + return false; + + if (at(tokens, index).ch != '/') + return true; // no [ / <bg-size> ] + + if (!parse_bg_size(tokens, ++index, size)) + { + index--; // restore index to point to '/' + return false; // has '/', but <bg-size> failed to parse + } + + return true; // both <bg-position> and <bg-size> parsed successfully +} + +// https://drafts.csswg.org/css-backgrounds/#typedef-bg-size +// <bg-size> = [ <length-percentage [0,∞]> | auto ]{1,2} | cover | contain +bool parse_bg_size(const css_token_vector& tokens, int& index, css_size& size) +{ + css_length a, b; + + if (!a.from_token(at(tokens, index), f_length_percentage | f_positive, background_size_strings)) + return false; + + // cover | contain + if (a.is_predefined() && a.predef() != background_size_auto) + { + size.width = size.height = a; + index++; + return true; + } + + if (b.from_token(at(tokens, index + 1), f_length_percentage | f_positive, "auto")) + index += 2; + else { - string_vector tokens; - split_string(position, tokens, "/"); + b.predef(background_size_auto); // If only one value is given the second is assumed to be auto. + index++; + } - if (tokens.size() > 2) return false; + size.width = a; + size.height = b; + return true; +} - if (tokens.size() == 2 && !parse_one_background_size(tokens[1], bg.m_size[0])) - return false; +bool is_one_of_predef(const css_length& x, int idx1, int idx2) +{ + return x.is_predefined() && is_one_of(x.predef(), idx1, idx2); +} - if (tokens.size() > 0 && !parse_one_background_position(tokens[0], bg.m_position_x[0], bg.m_position_y[0])) +// https://drafts.csswg.org/css-backgrounds/#typedef-bg-position +// https://www.w3.org/TR/css-values-4/#position +// <bg-position> = [ left | center | right | top | bottom | <length-percentage> ] | +// [ left | center | right | <length-percentage> ] [ top | center | bottom | <length-percentage> ] +// Side-relative values (like bottom 10%) are not supported, so only 1 or 2 values are parsed (not 3 or 4). +bool parse_bg_position(const css_token_vector& tokens, int& index, css_length& x, css_length& y, bool convert_keywords_to_percents) +{ + enum { + left = background_position_left, + right = background_position_right, + top = background_position_top, + bottom = background_position_bottom, + center = background_position_center + }; + + css_length a, b; + + if (!a.from_token(at(tokens, index), f_length_percentage, background_position_strings)) + return false; + + if (!b.from_token(at(tokens, index + 1), f_length_percentage, background_position_strings)) + { + // If only one value is specified, the second value is assumed to be center. + b.predef(center); + + // fix wrong order + if (is_one_of_predef(a, top, bottom)) + swap(a, b); + + index++; + } + else // two values + { + // try to fix wrong order + // A pair of keywords can be reordered, while a combination of keyword and length or percentage cannot. + if ((is_one_of_predef(a, top, bottom) && b.is_predefined()) || + (a.is_predefined() && is_one_of_predef(b, left, right))) + swap(a, b); + + // check for wrong order + if (is_one_of_predef(a, top, bottom) || is_one_of_predef(b, left, right)) return false; + + index += 2; + } + + if (convert_keywords_to_percents) + { + if (a.is_predefined()) + a.set_value(background_position_percentages[a.predef()], css_units_percentage); + if (b.is_predefined()) + b.set_value(background_position_percentages[b.predef()], css_units_percentage); } - + + x = a; + y = b; return true; } -void style::parse_background_image(const string& val, const string& baseurl, bool important) +void style::parse_background_image(const css_token_vector& tokens, const string& baseurl, bool important, document_container* container) { - string_vector tokens; - split_string(val, tokens, ",", "", "("); - if (tokens.empty()) return; + auto layers = parse_comma_separated_list(tokens); + if (layers.empty()) return; - string_vector images; + std::vector<image> images; - for (const auto& token : tokens) + for (const auto& layer : layers) { - string url; - css::parse_css_url(token, url); - images.push_back(url); + image image; + if (layer.size() != 1) + return; + if (!parse_bg_image(layer[0], image, container)) + return; + images.push_back(image); } - add_parsed_property(_background_image_, property_value(images, important)); + add_parsed_property(_background_image_, property_value(images, important)); add_parsed_property(_background_image_baseurl_, property_value(baseurl, important)); } -void style::parse_keyword_comma_list(string_id name, const string& val, bool important) +// <bg-image> = <image> | none +// <image> = <url> | <gradient> +bool parse_bg_image(const css_token& tok, image& bg_image, document_container* container) +{ + if (tok.ident() == "none") + { + bg_image.type = image::type_none; + return true; + } + + string url; + if (parse_url(tok, url)) + { + bg_image.type = image::type_url; + bg_image.url = url; + return true; + } + + if (parse_gradient(tok, bg_image.m_gradient, container)) + { + bg_image.type = image::type_gradient; + return true; + } + + return false; +} + +// https://drafts.csswg.org/css-values-4/#urls +bool parse_url(const css_token& tok, string& url) +{ + if (tok.type == URL) // legacy syntax without quotes: url(x.com) + { + url = trim(tok.str); + return true; + } + + if (tok.type == CV_FUNCTION && is_one_of(lowcase(tok.name), "url", "src") && + // note: relying on whitespace having been removed from tok.value + tok.value.size() == 1 && tok.value[0].type == STRING) + { + url = trim(tok.value[0].str); + return true; + } + + return false; +} + +void style::parse_keyword_comma_list(string_id name, const css_token_vector& tokens, bool important) { - string_vector tokens; - split_string(val, tokens, ","); - if (tokens.empty()) return; + auto layers = parse_comma_separated_list(tokens); + if (layers.empty()) return; int_vector vec; - for (auto& token : tokens) + for (const auto& layer : layers) { - trim(token); - int idx = value_index(token, m_valid_values[name]); - if (idx == -1) return; + int idx; + if (layer.size() != 1) return; + if (!parse_keyword(layer[0], idx, m_valid_values[name])) return; vec.push_back(idx); } add_parsed_property(name, property_value(vec, important)); } -void style::parse_background_position(const string& val, bool important) +void style::parse_background_position(const css_token_vector& tokens, bool important) { - string_vector tokens; - split_string(val, tokens, ","); - if (tokens.empty()) return; + auto layers = parse_comma_separated_list(tokens); + if (layers.empty()) return; length_vector x_positions, y_positions; - for (const auto& token : tokens) + for (const auto& layer : layers) { css_length x, y; - if(!parse_one_background_position(token, x, y)) return; + int index = 0; + if (!parse_bg_position(layer, index, x, y, true) || index != (int)layer.size()) + return; + x_positions.push_back(x); y_positions.push_back(y); } - + add_parsed_property(_background_position_x_, property_value(x_positions, important)); add_parsed_property(_background_position_y_, property_value(y_positions, important)); } -bool style::parse_one_background_position(const string& val, css_length& x, css_length& y) +void style::parse_background_size(const css_token_vector& tokens, bool important) { - string_vector pos; - split_string(val, pos, " \t"); - - if (pos.empty() || pos.size() > 2) + auto layers = parse_comma_separated_list(tokens); + if (layers.empty()) return; + + size_vector sizes; + + for (const auto& layer : layers) { - return false; + css_size size; + int index = 0; + if (!parse_bg_size(layer, index, size) || index != (int)layer.size()) + return; + + sizes.push_back(size); } - - if (pos.size() == 1) + + add_parsed_property(_background_size_, property_value(sizes, important)); +} + +bool parse_font_weight(const css_token& tok, css_length& weight) +{ + if (int idx = value_index(tok.ident(), font_weight_strings); idx >= 0) { - if (value_in_list(pos[0], "left;right;center")) - { - x.fromString(pos[0], "left;right;center"); - y.set_value(50, css_units_percentage); - } - else if (value_in_list(pos[0], "top;bottom;center")) - { - y.fromString(pos[0], "top;bottom;center"); - x.set_value(50, css_units_percentage); - } - else - { - x.fromString(pos[0], "left;right;center"); - y.set_value(50, css_units_percentage); - } + weight.predef(idx); + return true; } - else if (pos.size() == 2) + + // https://drafts.csswg.org/css-fonts/#font-weight-absolute-values + // Note: fractional values are allowed. + if (tok.type == NUMBER && tok.n.number >= 1 && tok.n.number <= 1000) { - if (value_in_list(pos[0], "left;right")) - { - x.fromString(pos[0], "left;right;center"); - y.fromString(pos[1], "top;bottom;center"); - } - else if (value_in_list(pos[0], "top;bottom")) + weight.set_value(tok.n.number, css_units_none); + return true; + } + + return false; +} + +// <font-style> || <font-variant-css2> || <font-weight> +// None of the allowed values intersect with <font-size>, it cannot consume <font-size>. +// Note: <font-weight> can be a number >=1, but <font-size> inside <font> is <length-percentage>, +// so it can be only 0 number. <font-size> as a standalone property does allow <number>s in quirks mode. +bool parse_font_style_variant_weight(const css_token_vector& tokens, int& index, + int& style, int& variant, css_length& weight) +{ + bool style_found = false; + bool variant_found = false; + bool weight_found = false; + bool res = false; + + int i = index, count = 0; + while (i < (int)tokens.size() && count++ < 3) + { + const auto& tok = tokens[i++]; + // All three properties can have value "normal", and it changes nothing because initial + // values of all three properties are "normal". + if (tok.ident() == "normal") { - x.fromString(pos[1], "left;right;center"); - y.fromString(pos[0], "top;bottom;center"); - } - else if (value_in_list(pos[1], "left;right")) + index++; + res = true; + } else if (!style_found && parse_keyword(tok, style, font_style_strings)) { - x.fromString(pos[1], "left;right;center"); - y.fromString(pos[0], "top;bottom;center"); + style_found = true; + index++; + res = true; } - else if (value_in_list(pos[1], "top;bottom")) + else if (!variant_found && parse_keyword(tok, variant, font_variant_strings)) { - x.fromString(pos[0], "left;right;center"); - y.fromString(pos[1], "top;bottom;center"); + variant_found = true; + index++; + res = true; } - else + else if (!weight_found && parse_font_weight(tok, weight)) { - x.fromString(pos[0], "left;right;center"); - y.fromString(pos[1], "top;bottom;center"); - } + weight_found = true; + index++; + res = true; + } else break; } + return res; +} + +// https://www.w3.org/TR/css-values-4/#custom-idents +bool is_custom_ident(const css_token& tok) +{ + if (tok.type != IDENT) return false; + // Custom identifiers are case-sensitive, but they should not case-insensitively match any of + // CSS-wide keywords or "default". + return !is_one_of(lowcase(tok.name), "default", "initial", "inherit", "unset"); +} + +// https://drafts.csswg.org/css-fonts/#propdef-font-family +// font-family = [ <family-name> | <generic-family> ]# +// <family-name> = <string> | <custom-ident>+ +// <generic-family> = generic( <custom-ident>+ ) | <string> | <custom-ident>+ +bool parse_font_family(const css_token_vector& tokens, string& font_family) +{ + auto list = parse_comma_separated_list(tokens); + if (list.empty()) return false; - if (x.is_predefined()) + string result; + for (const auto& name : list) { - switch (x.predef()) + if (name.size() == 1 && name[0].type == STRING) { - case 0: - x.set_value(0, css_units_percentage); - break; - case 1: - x.set_value(100, css_units_percentage); - break; - case 2: - x.set_value(50, css_units_percentage); - break; + //result.push_back(name[0].str); + result += name[0].str + ','; + continue; } - } - if (y.is_predefined()) - { - switch (y.predef()) + + // Otherwise: name must be a list of <custom-ident>s + // Note: generic( <custom-ident>+ ) is not supported + string str; + for (const auto& tok : name) { - case 0: - y.set_value(0, css_units_percentage); - break; - case 1: - y.set_value(100, css_units_percentage); - break; - case 2: - y.set_value(50, css_units_percentage); - break; + if (!is_custom_ident(tok)) return false; + str += tok.name + ' '; } + //result.push_back(trim(str)); + result += trim(str) + ','; } + result.resize(result.size() - 1); // remove last ',' + font_family = result; return true; } -void style::parse_background_size(const string& val, bool important) +// https://developer.mozilla.org/en-US/docs/Web/CSS/font +// https://drafts.csswg.org/css-fonts/#font-prop +// font = <font-style-weight>? <font-size> [ / <line-height> ]? <font-family> +// font = <system-family-name*> +// <font-style-weight> = <font-style> || <font-variant-css2> || <font-weight> || <font-width-css3*> +// values marked * are not supported +void style::parse_font(css_token_vector tokens, bool important) { - string_vector tokens; - split_string(val, tokens, ","); - if (tokens.empty()) return; + // initial values + int style = font_style_normal; + int variant = font_variant_normal; + css_length weight = css_length::predef_value(font_weight_normal); + css_length size = css_length::predef_value(font_size_medium); + css_length line_height = css_length::predef_value(line_height_normal); + string font_family; // this argument is mandatory, no need to set initial value + + if(tokens.size() == 1 && (tokens[0].type == STRING || tokens[0].type == IDENT) && value_in_list(tokens[0].str, font_system_family_name_strings)) + { + font_family = tokens[0].str; + } else + { + int index = 0; + parse_font_style_variant_weight(tokens, index, style, variant, weight); - size_vector sizes; + // font-size = <absolute-size> | <relative-size> | <length-percentage [0,∞]> | math + if (!size.from_token(at(tokens, index), f_length_percentage | f_positive, font_size_strings)) + return; + index++; - for (const auto& token : tokens) - { - css_size size; - if (!parse_one_background_size(token, size)) return; - sizes.push_back(size); - } + if (at(tokens, index).ch == '/') + { + index++; + // https://drafts.csswg.org/css2/#propdef-line-height + // line-height = normal | <number> | <length> | <percentage> + if (!line_height.from_token(at(tokens, index), f_number | f_length_percentage, line_height_strings)) + return; + index++; + } - add_parsed_property(_background_size_, property_value(sizes, important)); + remove(tokens, 0, index); + if (!parse_font_family(tokens, font_family)) + return; + } + add_parsed_property(_font_style_, property_value(style, important)); + add_parsed_property(_font_variant_, property_value(variant, important)); + add_parsed_property(_font_weight_, property_value(weight, important)); + add_parsed_property(_font_size_, property_value(size, important)); + add_parsed_property(_line_height_, property_value(line_height, important)); + add_parsed_property(_font_family_, property_value(font_family, important)); } -bool style::parse_one_background_size(const string& val, css_size& size) +void style::parse_text_decoration(const css_token_vector& tokens, bool important, document_container* container) { - string_vector res; - split_string(val, res, " \t"); - if (res.empty()) + css_length len; + css_token_vector line_tokens; + for(const auto& token : tokens) { - return false; - } + if(parse_text_decoration_color(token, important, container)) continue; - size.width.fromString(res[0], background_size_strings); - if (res.size() > 1) - { - size.height.fromString(res[1], background_size_strings); + if(parse_length(token, len, f_length_percentage|f_positive, style_text_decoration_thickness_strings)) + { + add_parsed_property(_text_decoration_thickness_, property_value(len, important)); + } else + { + if(token.type == IDENT) + { + int style = value_index(token.ident(), style_text_decoration_style_strings); + if(style >= 0) + { + add_parsed_property(_text_decoration_style_, property_value(style, important)); + } else + { + line_tokens.push_back(token); + } + } else + { + line_tokens.push_back(token); + } + } } - else + if(!line_tokens.empty()) { - size.height.predef(background_size_auto); + parse_text_decoration_line(line_tokens, important); } - return true; } -void style::parse_font(const string& val, bool important) +bool style::parse_text_decoration_color(const css_token& token, bool important, document_container* container) { - if (val == "inherit") + web_color _color; + if(parse_color(token, _color, container)) { - add_parsed_property(_font_style_, property_value(important, prop_type_inherit)); - add_parsed_property(_font_variant_, property_value(important, prop_type_inherit)); - add_parsed_property(_font_weight_, property_value(important, prop_type_inherit)); - add_parsed_property(_font_size_, property_value(important, prop_type_inherit)); - add_parsed_property(_line_height_, property_value(important, prop_type_inherit)); - return; - } else + add_parsed_property(_text_decoration_color_, property_value(_color, important)); + return true; + } + if(token.type == IDENT && value_in_list(token.ident(), "auto;currentcolor")) { - add_parsed_property(_font_style_, property_value(font_style_normal, important)); - add_parsed_property(_font_variant_, property_value(font_variant_normal, important)); - add_parsed_property(_font_weight_, property_value(font_weight_normal, important)); - add_parsed_property(_font_size_, property_value(font_size_medium, important)); - add_parsed_property(_line_height_, property_value(line_height_normal, important)); + add_parsed_property(_text_decoration_color_, property_value(web_color::current_color, important)); + return true; } + return false; +} - string_vector tokens; - split_string(val, tokens, " ", "", "\""); - - int idx; - bool is_family = false; - string font_family; +void style::parse_text_decoration_line(const css_token_vector& tokens, bool important) +{ + int val = 0; for(const auto& token : tokens) { - if(is_family) - { - font_family += token; - continue; - } - - if((idx = value_index(token, font_style_strings)) >= 0) - { - if(idx == 0) - { - add_parsed_property(_font_style_, property_value(font_style_normal, important)); - add_parsed_property(_font_variant_, property_value(font_variant_normal, important)); - add_parsed_property(_font_weight_, property_value(font_weight_normal, important)); - } else - { - add_parsed_property(_font_style_, property_value(idx, important)); - } - } else if((idx = value_index(token, font_weight_strings)) >= 0) + if(token.type == IDENT) { - add_parsed_property(_font_weight_, property_value(idx, important)); - } else if((idx = value_index(token, font_variant_strings)) >= 0) - { - add_parsed_property(_font_variant_, property_value(idx, important)); - } - else if(t_isdigit(token[0]) || token[0] == '.' || - value_in_list(token, font_size_strings) || token.find('/') != -1) - { - string_vector szlh; - split_string(token, szlh, "/"); - if(!szlh.empty()) + int idx = value_index(token.ident(), style_text_decoration_line_strings); + if(idx >= 0) { - auto size = css_length::from_string(szlh[0], font_size_strings, -1); - add_parsed_property(_font_size_, property_value(size, important)); - - if (szlh.size() == 2) - { - auto height = css_length::from_string(szlh[1], "normal", -1); - add_parsed_property(_line_height_, property_value(height, important)); - } + val |= 1 << (idx - 1); } - } else - { - is_family = true; - font_family += token; } } - add_parsed_property(_font_family_, property_value(font_family, important)); + add_parsed_property(_text_decoration_line_, property_value(val, important)); } -void style::parse_flex(const string& val, bool important) -{ - css_length _auto = css_length::predef_value(flex_basis_auto); - - if (val == "initial") +void style::parse_text_emphasis(const css_token_vector& tokens, bool important, document_container *container) { + string style; + for(const auto& token : std::vector(tokens.rbegin(), tokens.rend())) + { + if(parse_text_emphasis_color(token, important, container)) continue; + style.insert(0, token.str + " "); + } + style = trim(style); + if (!style.empty()) { - // 0 1 auto - add_parsed_property(_flex_grow_, property_value(0.f, important)); - add_parsed_property(_flex_shrink_, property_value(1.f, important)); - add_parsed_property(_flex_basis_, property_value(_auto, important)); + add_parsed_property(_text_emphasis_style_, property_value(style, important)); } - else if (val == "auto") +} + +bool style::parse_text_emphasis_color(const css_token &token, bool important, document_container *container) +{ + web_color _color; + if(parse_color(token, _color, container)) { - // 1 1 auto - add_parsed_property(_flex_grow_, property_value(1.f, important)); - add_parsed_property(_flex_shrink_, property_value(1.f, important)); - add_parsed_property(_flex_basis_, property_value(_auto, important)); + add_parsed_property(_text_emphasis_color_, property_value(_color, important)); + return true; } - else if (val == "none") + if(token.type == IDENT && value_in_list(token.ident(), "auto;currentcolor")) { - // 0 0 auto - add_parsed_property(_flex_grow_, property_value(0.f, important)); - add_parsed_property(_flex_shrink_, property_value(0.f, important)); - add_parsed_property(_flex_basis_, property_value(_auto, important)); + add_parsed_property(_text_emphasis_color_, property_value(web_color::current_color, important)); + return true; } - else + return false; +} + +void style::parse_text_emphasis_position(const css_token_vector &tokens, bool important) +{ + int val = 0; + for(const auto& token : tokens) { - string_vector tokens; - split_string(val, tokens, " "); - if (tokens.size() == 3) - { - float grow = t_strtof(tokens[0]); - float shrink = t_strtof(tokens[1]); - auto basis = css_length::from_string(tokens[2], flex_basis_strings, -1); - if(!basis.is_predefined() && basis.units() == css_units_none && basis.val() == 0) - { - basis.set_value(basis.val(), css_units_px); - } - - add_parsed_property(_flex_grow_, property_value(grow, important)); - add_parsed_property(_flex_shrink_, property_value(shrink, important)); - add_parsed_property(_flex_basis_, property_value(basis, important)); - } - else if (tokens.size() == 2) + if(token.type == IDENT) { - float grow = t_strtof(tokens[0]); - add_parsed_property(_flex_grow_, property_value(grow, important)); - - if (litehtml::is_number(tokens[1])) - { - float shrink = t_strtof(tokens[1]); - add_parsed_property(_flex_shrink_, property_value(shrink, important)); - add_parsed_property(_flex_basis_, property_value(css_length(0), important)); - } - else + int idx = value_index(token.ident(), style_text_emphasis_position_strings); + if(idx >= 0) { - auto basis = css_length::from_string(tokens[1], flex_basis_strings, -1); - add_parsed_property(_flex_basis_, property_value(basis, important)); - } - } - else if (tokens.size() == 1) - { - if (is_number(tokens[0])) - { - float grow = t_strtof(tokens[0]); - add_parsed_property(_flex_grow_, property_value(grow, important)); - add_parsed_property(_flex_shrink_, property_value(1.f, important)); - add_parsed_property(_flex_basis_, property_value(css_length(0), important)); - } - else - { - auto basis = css_length::from_string(tokens[0], flex_basis_strings, -1); - add_parsed_property(_flex_grow_, property_value(1.f, important)); - add_parsed_property(_flex_shrink_, property_value(1.f, important)); - add_parsed_property(_flex_basis_, property_value(basis, important)); + val |= 1 << (idx - 1); } } } + add_parsed_property(_text_emphasis_position_, property_value(val, important)); } -void style::parse_align_self(string_id name, const string& val, bool important) +// https://developer.mozilla.org/en-US/docs/Web/CSS/flex +// https://drafts.csswg.org/css-flexbox/#flex-property +// flex = none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ] +void style::parse_flex(const css_token_vector& tokens, bool important) { - string_vector tokens; - split_string(val, tokens, " "); - if(tokens.size() == 1) + auto n = tokens.size(); + if (n > 3) return; + + const auto& a = at(tokens, 0); + const auto& b = at(tokens, 1); + const auto& c = at(tokens, 2); + + struct flex { - int idx = value_index(val, m_valid_values[name]); - if (idx >= 0) + float m_grow = 1; // flex-grow is set to 1 when omitted + float m_shrink = 1; + css_length m_basis = 0; + + bool grow(const css_token& tok) { - add_parsed_property(name, property_value(idx, important)); + if (tok.type != NUMBER || tok.n.number < 0) return false; + m_grow = tok.n.number; + return true; } - } else + bool shrink(const css_token& tok) + { + if (tok.type != NUMBER || tok.n.number < 0) return false; + m_shrink = tok.n.number; + return true; + } + // unitless_zero_allowed enforces the rule "A unitless zero that is not already preceded by two flex factors must be interpreted as a flex factor." + bool basis(const css_token& tok, bool unitless_zero_allowed = false) + { + if (!unitless_zero_allowed and tok.type == NUMBER and tok.n.number == 0) + return false; + return m_basis.from_token(tok, f_length_percentage | f_positive, flex_basis_strings); + } + }; + flex flex; + + if (n == 1) { - int val1 = 0; - int val2 = -1; - for(auto &token : tokens) + string_id ident = _id(a.ident()); + if (is_one_of(ident, _initial_, _auto_, _none_)) { - if(token == "first") - { - val1 |= flex_align_items_first; - } else if(token == "last") - { - val1 |= flex_align_items_last; - } else if(token == "safe") - { - val1 |= flex_align_items_safe; - } else if(token == "unsafe") - { - val1 |= flex_align_items_unsafe; - } else + css_length _auto = css_length::predef_value(flex_basis_auto); + + switch (ident) { - int idx = value_index(token, m_valid_values[name]); - if(idx >= 0) - { - val2 = idx; - } + case _initial_: flex = {0, 1, _auto}; break; + case _auto_: flex = {1, 1, _auto}; break; // can be handled by else + case _none_: flex = {0, 0, _auto}; break; + default:; } } - if(val2 >= 0) + else { - add_parsed_property(name, property_value(val1 | val2, important)); + bool ok = flex.grow(a) || flex.basis(a); + if (!ok) return; } } + else if (n == 2) + { + // <number> <number> + // <number> <basis> + // <basis> <number> + bool ok = + (flex.grow(a) && (flex.shrink(b) || flex.basis(b))) || + (flex.basis(a) && flex.grow(b)); + + if (!ok) return; + } + else // n == 3 + { + // <number> <number> <basis> + // <basis> <number> <number> + bool ok = + (flex.grow(a) && flex.shrink(b) && flex.basis(c, true)) || + (flex.basis(a) && flex.grow(b) && flex.shrink(c)); + + if (!ok) return; + } + + add_parsed_property(_flex_grow_, property_value(flex.m_grow, important)); + add_parsed_property(_flex_shrink_, property_value(flex.m_shrink, important)); + add_parsed_property(_flex_basis_, property_value(flex.m_basis, important)); +} + +// flex-flow = <'flex-direction'> || <'flex-wrap'> +void style::parse_flex_flow(const css_token_vector& tokens, bool important) +{ + // initial values: https://developer.mozilla.org/en-US/docs/Web/CSS/flex-flow#formal_definition + int flex_direction = flex_direction_row; + int flex_wrap = flex_wrap_nowrap; + + bool direction_found = false; + bool wrap_found = false; + + for (const auto& token : tokens) + { + if (!direction_found && parse_keyword(token, flex_direction, flex_direction_strings)) + direction_found = true; + else if (!wrap_found && parse_keyword(token, flex_wrap, flex_wrap_strings)) + wrap_found = true; + else + return; + } + + add_parsed_property(_flex_direction_, property_value(flex_direction, important)); + add_parsed_property(_flex_wrap_, property_value(flex_wrap, important)); +} + +// https://www.w3.org/TR/css-align/#align-self-property +// value = auto | normal | stretch | [ [first | last]? && baseline ] | [safe | unsafe]? <self-position> +// <self-position> = center | start | end | self-start | self-end | flex-start | flex-end +// https://www.w3.org/TR/css-align/#align-items-property +// same as align-self, except auto is not allowed +void style::parse_align_self(string_id name, const css_token_vector& tokens, bool important) +{ + auto n = tokens.size(); + if (n > 2) + return; + if (tokens[0].type != IDENT || (n == 2 && tokens[1].type != IDENT)) + return; + + string a = tokens[0].ident(); + + if (name == _align_items_ && a == "auto") + return; + + if (n == 1) + { + int idx = value_index(a, flex_align_items_strings); + if (idx >= 0) + add_parsed_property(name, property_value(idx, important)); + return; + } + + // Otherwise: n == 2 + + string b = tokens[1].ident(); + + if (a == "baseline") swap(a, b); + if (b == "baseline" && is_one_of(a, "first", "last")) + { + int idx = flex_align_items_baseline | (a == "first" ? flex_align_items_first : flex_align_items_last); + add_parsed_property(name, property_value(idx, important)); + return; + } + + // <overflow-position> <self-position> + int idx = value_index(b, self_position_strings); + if (idx >= 0 && is_one_of(a, "safe", "unsafe")) + { + idx |= (a == "safe" ? flex_align_items_safe : flex_align_items_unsafe); + add_parsed_property(name, property_value(idx, important)); + } } void style::add_parsed_property( string_id name, const property_value& propval ) @@ -1186,33 +1592,80 @@ const property_value& style::get_property(string_id name) const return dummy; } -void style::subst_vars_(string& str, const element* el) +// var( <custom-property-name> , <declaration-value>? ) +bool check_var_syntax(const css_token_vector& args) +{ + if (args.empty()) return false; + + string name = args[0].ident(); + if (name.substr(0, 2) != "--" || name.size() <= 2) + return false; + + if (args.size() > 1 && args[1].ch != ',') + return false; + if (args.size() > 2 && !is_declaration_value(args, 2)) + return false; + + return true; +} + +// https://drafts.csswg.org/css-variables/#using-variables +// var( <custom-property-name> , <declaration-value>? ) +// returns true if one var() was substituted +// returns true if there was error or var() was not found +bool subst_var(css_token_vector& tokens, const html_tag* el, std::set<string_id>& used_vars) { - while (1) + for (int i = 0; i < (int)tokens.size(); i++) { - auto start = str.find("var("); - if (start == -1) break; - if (start > 0 && isalnum(str[start - 1])) break; - auto end = str.find(")", start + 4); - if (end == -1) break; - auto name = str.substr(start + 4, end - start - 4); - trim(name); - string val = el->get_custom_property(_id(name), ""); - str.replace(start, end - start + 1, val); + auto& tok = tokens[i]; + if (tok.type == CV_FUNCTION && lowcase(tok.name) == "var") + { + auto args = tok.value; // copy is intentional + if (!check_var_syntax(args)) return false; + + auto name = _id(args[0].name); + if (name in used_vars) return false; // dependency cycle https://drafts.csswg.org/css-variables/#cycles + used_vars.insert(name); + + css_token_vector value; + if (el->get_custom_property(name, value)) + { + remove(tokens, i); + insert(tokens, i, value); + } + else // custom property not defined + { + if (args.size() == 1) return false; // default value not provided + remove(args, 0, 2); + remove(tokens, i); + insert(tokens, i, args); + } + return true; + } + if (tok.is_component_value() && subst_var(tok.value, el, used_vars)) + return true; } + return false; +} + +void subst_vars_(string_id name, css_token_vector& tokens, const html_tag* el) +{ + std::set<string_id> used_vars = {name}; + while (subst_var(tokens, el, used_vars)); } -void style::subst_vars(const element* el) +void style::subst_vars(const html_tag* el) { for (auto& prop : m_properties) { - if (prop.second.m_type == prop_type_var) + if (prop.second.m_has_var) { - subst_vars_(prop.second.m_string, el); + auto& value = prop.second.get<css_token_vector>(); + subst_vars_(prop.first, value, el); // re-adding the same property - // if it is a custom property it will be readded as a string (currently it is prop_type_var) + // if it is a custom property it will be readded as a css_token_vector // if it is a standard css property it will be parsed and properly added as typed property - add_property(prop.first, prop.second.m_string, "", prop.second.m_important, el->get_document()->container()); + add_property(prop.first, value, "", prop.second.m_important, el->get_document()->container()); } } } diff --git a/src/stylesheet.cpp b/src/stylesheet.cpp index 8c4b78431..d6ddca5e8 100644 --- a/src/stylesheet.cpp +++ b/src/stylesheet.cpp @@ -1,136 +1,136 @@ #include "html.h" #include "stylesheet.h" -#include <algorithm> +#include "css_parser.h" #include "document.h" +#include "document_container.h" +namespace litehtml +{ -void litehtml::css::parse_stylesheet(const char* str, const char* baseurl, const std::shared_ptr<document>& doc, const media_query_list::ptr& media) +// https://www.w3.org/TR/css-syntax-3/#parse-a-css-stylesheet +template<class Input> // Input == string or css_token_vector +void css::parse_css_stylesheet(const Input& input, string baseurl, document::ptr doc, media_query_list_list::ptr media, bool top_level) { - string text = str; + if (doc && media) + doc->add_media_list(media); - // remove comments - string::size_type c_start = text.find("/*"); - while(c_start != string::npos) - { - string::size_type c_end = text.find("*/", c_start + 2); - if(c_end == string::npos) - { - text.erase(c_start); - break; - } - text.erase(c_start, c_end - c_start + 2); - c_start = text.find("/*"); - } + // To parse a CSS stylesheet, first parse a stylesheet. + auto rules = css_parser::parse_stylesheet(input, top_level); + bool import_allowed = top_level; - string::size_type pos = text.find_first_not_of(" \n\r\t"); - while(pos != string::npos) + // Interpret all of the resulting top-level qualified rules as style rules, defined below. + // If any style rule is invalid, or any at-rule is not recognized or is invalid according + // to its grammar or context, it's a parse error. Discard that rule. + for (auto rule : rules) { - while(pos != string::npos && text[pos] == '@') + if (rule->type == raw_rule::qualified) { - string::size_type sPos = pos; - pos = text.find_first_of("{;", pos); - if(pos != string::npos && text[pos] == '{') - { - pos = find_close_bracket(text, pos, '{', '}'); - } - if(pos != string::npos) - { - parse_atrule(text.substr(sPos, pos - sPos + 1), baseurl, doc, media); - } else - { - parse_atrule(text.substr(sPos), baseurl, doc, media); - } - - if(pos != string::npos) - { - pos = text.find_first_not_of(" \n\r\t", pos + 1); - } + if (parse_style_rule(rule, baseurl, doc, media)) + import_allowed = false; + continue; } - if(pos == string::npos) + // Otherwise: at-rule + switch (_id(lowcase(rule->name))) { + case _charset_: // ignored https://www.w3.org/TR/css-syntax-3/#charset-rule break; - } - string::size_type style_start = text.find('{', pos); - string::size_type style_end = text.find('}', pos); - if(style_start != string::npos && style_end != string::npos) - { - auto str_style = text.substr(style_start + 1, style_end - style_start - 1); - style::ptr style = std::make_shared<litehtml::style>(); - style->add(str_style, baseurl ? baseurl : "", doc->container()); - - parse_selectors(text.substr(pos, style_start - pos), style, media); + case _import_: + if (import_allowed) + parse_import_rule(rule, baseurl, doc, media); + else + css_parse_error("incorrect placement of @import rule"); + break; - if(media && doc) + // https://www.w3.org/TR/css-conditional-3/#at-media + // @media <media-query-list> { <stylesheet> } + case _media_: + { + if (rule->block.type != CURLY_BLOCK) break; + auto new_media = media; + auto mq_list = parse_media_query_list(rule->prelude, doc); + // An empty media query list evaluates to true. https://drafts.csswg.org/mediaqueries-5/#example-6f06ee45 + if (!mq_list.empty()) { - doc->add_media_list(media); + new_media = make_shared<media_query_list_list>(media ? *media : media_query_list_list()); + new_media->add(mq_list); } - - pos = style_end + 1; - } else - { - pos = string::npos; + parse_css_stylesheet(rule->block.value, baseurl, doc, new_media, false); + import_allowed = false; + break; } - if(pos != string::npos) - { - pos = text.find_first_not_of(" \n\r\t", pos); + default: + css_parse_error("unrecognized rule @" + rule->name); } } } -void litehtml::css::parse_css_url( const string& str, string& url ) +// https://drafts.csswg.org/css-cascade-5/#at-import +// `layer` and `supports` are not supported +// @import [ <url> | <string> ] <media-query-list>? +void css::parse_import_rule(raw_rule::ptr rule, string baseurl, document::ptr doc, media_query_list_list::ptr media) { - url = ""; - size_t pos1 = str.find('('); - size_t pos2 = str.find(')'); - if(pos1 != string::npos && pos2 != string::npos) + auto tokens = rule->prelude; + int index = 0; + skip_whitespace(tokens, index); + auto tok = at(tokens, index); + string url; + auto parse_string = [](const css_token& tok, string& str) { - url = str.substr(pos1 + 1, pos2 - pos1 - 1); - if(url.length()) - { - if(url[0] == '\'' || url[0] == '"') - { - url.erase(0, 1); - } - } - if(url.length()) - { - if(url[url.length() - 1] == '\'' || url[url.length() - 1] == '"') - { - url.erase(url.length() - 1, 1); - } - } + if (tok.type != STRING) return false; + str = tok.str; + return true; + }; + bool ok = parse_url(tok, url) || parse_string(tok, url); + if (!ok) { + css_parse_error("invalid @import rule"); + return; + } + document_container* container = doc->container(); + string css_text; + string css_baseurl = baseurl; + container->import_css(css_text, url, css_baseurl); + + auto new_media = media; + tokens = slice(tokens, index + 1); + auto mq_list = parse_media_query_list(tokens, doc); + if (!mq_list.empty()) + { + new_media = make_shared<media_query_list_list>(media ? *media : media_query_list_list()); + new_media->add(mq_list); } + + parse_css_stylesheet(css_text, css_baseurl, doc, new_media, true); } -bool litehtml::css::parse_selectors( const string& txt, const style::ptr& styles, const media_query_list::ptr& media ) +// https://www.w3.org/TR/css-syntax-3/#style-rules +bool css::parse_style_rule(raw_rule::ptr rule, string baseurl, document::ptr doc, media_query_list_list::ptr media) { - string selector = txt; - trim(selector); - string_vector tokens; - split_string(selector, tokens, ","); + // The prelude of the qualified rule is parsed as a <selector-list>. If this returns failure, the entire style rule is invalid. + auto list = parse_selector_list(rule->prelude, strict_mode, doc->mode()); + if (list.empty()) + { + css_parse_error("invalid selector"); + return false; + } - bool added_something = false; + style::ptr style = make_shared<litehtml::style>(); // style block + // The content of the qualified rule's block is parsed as a style block's contents. + style->add(rule->block.value, baseurl, doc->container()); - for(auto & token : tokens) + for (auto sel : list) { - css_selector::ptr new_selector = std::make_shared<css_selector>(media); - new_selector->m_style = styles; - trim(token); - if(new_selector->parse(token)) - { - new_selector->calc_specificity(); - add_selector(new_selector); - added_something = true; - } + sel->m_style = style; + sel->m_media_query = media; + sel->calc_specificity(); + add_selector(sel); } - - return added_something; + return true; } -void litehtml::css::sort_selectors() +void css::sort_selectors() { std::sort(m_selectors.begin(), m_selectors.end(), [](const css_selector::ptr& v1, const css_selector::ptr& v2) @@ -140,86 +140,4 @@ void litehtml::css::sort_selectors() ); } -void litehtml::css::parse_atrule(const string& text, const char* baseurl, const std::shared_ptr<document>& doc, const media_query_list::ptr& media) -{ - if(text.substr(0, 7) == "@import") - { - int sPos = 7; - string iStr; - iStr = text.substr(sPos); - if(iStr[iStr.length() - 1] == ';') - { - iStr.erase(iStr.length() - 1); - } - trim(iStr); - string_vector tokens; - split_string(iStr, tokens, " ", "", "(\""); - if(!tokens.empty()) - { - string url; - parse_css_url(tokens.front(), url); - if(url.empty()) - { - url = tokens.front(); - } - tokens.erase(tokens.begin()); - if(doc) - { - document_container* doc_cont = doc->container(); - if(doc_cont) - { - string css_text; - string css_baseurl; - if(baseurl) - { - css_baseurl = baseurl; - } - doc_cont->import_css(css_text, url, css_baseurl); - if(!css_text.empty()) - { - media_query_list::ptr new_media = media; - if(!tokens.empty()) - { - string media_str; - for(auto iter = tokens.begin(); iter != tokens.end(); iter++) - { - if(iter != tokens.begin()) - { - media_str += " "; - } - media_str += (*iter); - } - new_media = media_query_list::create_from_string(media_str, doc); - if(!new_media) - { - new_media = media; - } - } - parse_stylesheet(css_text.c_str(), css_baseurl.c_str(), doc, new_media); - } - } - } - } - } else if(text.substr(0, 6) == "@media") - { - string::size_type b1 = text.find_first_of('{'); - string::size_type b2 = text.find_last_of('}'); - if(b1 != string::npos) - { - string media_type = text.substr(6, b1 - 6); - trim(media_type); - media_query_list::ptr new_media = media_query_list::create_from_string(media_type, doc); - - string media_style; - if(b2 != string::npos) - { - media_style = text.substr(b1 + 1, b2 - b1 - 1); - } else - { - media_style = text.substr(b1 + 1); - } - - parse_stylesheet(media_style.c_str(), baseurl, doc, new_media); - } - } -} +} // namespace litehtml diff --git a/src/table.cpp b/src/table.cpp index da272c070..f91326726 100644 --- a/src/table.cpp +++ b/src/table.cpp @@ -13,7 +13,7 @@ void litehtml::table_grid::add_cell(const std::shared_ptr<render_item>& el) while( is_rowspanned( (int) m_cells.size() - 1, (int) m_cells.back().size() ) ) { - m_cells.back().push_back(table_cell()); + m_cells.back().emplace_back(); } m_cells.back().push_back(cell); @@ -30,7 +30,7 @@ void litehtml::table_grid::begin_row(const std::shared_ptr<render_item>& row) std::vector<table_cell> r; m_cells.push_back(r); - m_rows.push_back(table_row(0, row)); + m_rows.emplace_back(0, row); } @@ -72,7 +72,7 @@ void litehtml::table_grid::finish() m_columns.clear(); for(int i = 0; i < m_cols_count; i++) { - m_columns.push_back(table_column(0, 0)); + m_columns.emplace_back(0, 0); } for(int col = 0; col < m_cols_count; col++) @@ -256,12 +256,12 @@ void litehtml::table_grid::distribute_width( int width, int start, int end ) add = round_f( (float) width * ((float) (column->max_width - column->min_width) / (float) cols_width) ); if(column->width + add >= column->min_width) { - column->width += add; + column->width += add; added_width += add; } else { added_width += (column->width - column->min_width) * (add / abs(add)); - column->width = column->min_width; + column->width = column->min_width; } } if(added_width < width && step) @@ -480,7 +480,7 @@ void litehtml::table_grid::calc_vertical_positions( const margins& table_borders } } -void litehtml::table_grid::calc_rows_height(int blockHeight, int borderSpacingY) +void litehtml::table_grid::calc_rows_height(int blockHeight, int /*borderSpacingY*/) { int min_table_height = 0; @@ -595,15 +595,15 @@ int& litehtml::table_column_accessor_width::get( table_column& col ) litehtml::table_row::table_row(int h, const std::shared_ptr<render_item>& row) { - min_height = 0; - height = h; - el_row = row; - border_bottom = 0; - border_top = 0; - top = 0; - bottom = 0; - if (row) - { - css_height = row->src_el()->css().get_height(); - } + min_height = 0; + height = h; + el_row = row; + border_bottom = 0; + border_top = 0; + top = 0; + bottom = 0; + if (row) + { + css_height = row->src_el()->css().get_height(); + } } diff --git a/src/url.cpp b/src/url.cpp index 13076e338..b1b0fba26 100644 --- a/src/url.cpp +++ b/src/url.cpp @@ -29,12 +29,11 @@ #include "url.h" -#include <iostream> #include <sstream> #include <algorithm> - #include "codepoint.h" #include "url_path.h" +#include <iomanip> namespace litehtml { @@ -95,35 +94,87 @@ url::url(const string& str) path_ = tmp; } -url::url(const string& scheme, - const string& authority, - const string& path, - const string& query, - const string& fragment) -: scheme_(scheme) -, authority_(authority) -, path_(path) -, query_(query) -, fragment_(fragment) +url::url(const string& scheme, const string& authority, const string& path, const string& query, + const string& fragment) : + scheme_(scheme), + authority_(authority), + path_(path), + query_(query), + fragment_(fragment) { - std::stringstream tss; + std::stringstream tss; + + if(!scheme_.empty()) + { + tss << scheme_ << ":"; + } + if(!authority_.empty()) + { + tss << "//" << authority_; + } + if(!path_.empty()) + { + tss << path_; + } + if(!query_.empty()) + { + tss << "?" << query_; + } + if(!fragment_.empty()) + { + tss << "#" << fragment_; + } + str_ = tss.str(); +} - if (!scheme_.empty()) { - tss << scheme_ << ":"; - } - if (!authority_.empty()) { - tss << "//" << authority_; - } - if (!path_.empty()) { - tss << path_; - } - if (!query_.empty()) { - tss << "?" << query_; - } - if (!fragment_.empty()) { - tss << "#" << fragment_; - } - str_ = tss.str(); +string url::encode(const string& str) +{ + std::ostringstream encoded; + encoded << std::hex << std::uppercase; + + for(unsigned char c : str) + { + if(isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') + { + encoded << c; + } else + { + encoded << '%' << std::setw(2) << int((unsigned char) c); + } + } + + return encoded.str(); +} + +string url::decode(const string& str) +{ + string decoded; + size_t i = 0; + + while(i < str.size()) + { + char c = str[i]; + if(c == '%') + { + if(i + 2 >= str.size()) + { + break; + } + + // Decode the percent-encoded character + char hex[3] = {str[i + 1], str[i + 2], '\0'}; + c = static_cast<char>(std::strtol(hex, nullptr, 16)); + i += 2; // Skip the next two characters + } else if(c == '+') + { + // Replace '+' with space + c = ' '; + } + decoded += c; + i++; + } + + return decoded; } url resolve(const url& b, const url& r) @@ -136,7 +187,7 @@ url resolve(const url& b, const url& r) if (r.has_scheme()) { return r; } else if (r.has_authority()) { - return url(b.scheme(), r.authority(), r.path(), r.query(), r.fragment()); + return {b.scheme(), r.authority(), r.path(), r.query(), r.fragment()}; } else if (r.has_path()) { // The relative URL path is either an absolute path or a relative @@ -145,18 +196,18 @@ url resolve(const url& b, const url& r) // against the base path and build the URL using the resolved path. if (is_url_path_absolute(r.path())) { - return url(b.scheme(), b.authority(), r.path(), r.query(), r.fragment()); + return {b.scheme(), b.authority(), r.path(), r.query(), r.fragment()}; } else { string path = url_path_resolve(b.path(), r.path()); - return url(b.scheme(), b.authority(), path, r.query(), r.fragment()); + return {b.scheme(), b.authority(), path, r.query(), r.fragment()}; } } else if (r.has_query()) { - return url(b.scheme(), b.authority(), b.path(), r.query(), r.fragment()); + return {b.scheme(), b.authority(), b.path(), r.query(), r.fragment()}; } else { // The resolved URL never includes the base URL fragment (i.e., it // always includes the reference URL fragment). - return url(b.scheme(), b.authority(), b.path(), b.query(), r.fragment()); + return {b.scheme(), b.authority(), b.path(), b.query(), r.fragment()}; } } diff --git a/src/utf8_strings.cpp b/src/utf8_strings.cpp index 787af86f4..6a578e1b9 100644 --- a/src/utf8_strings.cpp +++ b/src/utf8_strings.cpp @@ -1,28 +1,19 @@ -#include "html.h" #include "utf8_strings.h" - -litehtml::utf8_to_wchar::utf8_to_wchar(const char* val) +namespace litehtml { - m_utf8 = (const byte*) val; - if (!m_utf8) return; - while (true) - { - ucode_t wch = get_char(); - if (!wch) break; - m_str += wch; - } -} - -litehtml::ucode_t litehtml::utf8_to_wchar::get_char() +// consume one utf-8 char and increment index accordingly +// if str[index] == 0 index is not incremented +char32_t read_utf8_char(const string& str, int& index) { - ucode_t b1 = getb(); - - if (!b1) + auto getb = [&]() -> byte { - return 0; - } + if (!str[index]) return 0; + return str[index++]; + }; + + byte b1 = getb(); // Determine whether we are dealing // with a one-, two-, three-, or four- @@ -35,65 +26,78 @@ litehtml::ucode_t litehtml::utf8_to_wchar::get_char() else if ((b1 & 0xe0) == 0xc0) { // 2-byte sequence: 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx - ucode_t r = (b1 & 0x1f) << 6; - r |= get_next_utf8(getb()); + char32_t r = (b1 & 0x1f) << 6; + r |= getb() & 0x3f; return r; } else if ((b1 & 0xf0) == 0xe0) { // 3-byte sequence: zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx - ucode_t r = (b1 & 0x0f) << 12; - r |= get_next_utf8(getb()) << 6; - r |= get_next_utf8(getb()); + char32_t r = (b1 & 0x0f) << 12; + r |= (getb() & 0x3f) << 6; + r |= getb() & 0x3f; return r; } else if ((b1 & 0xf8) == 0xf0) { - // 4-byte sequence: 11101110wwwwzzzzyy + 110111yyyyxxxxxx - // = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx - // (uuuuu = wwww + 1) - int b2 = get_next_utf8(getb()); - int b3 = get_next_utf8(getb()); - int b4 = get_next_utf8(getb()); - return ((b1 & 7) << 18) | ((b2 & 0x3f) << 12) | - ((b3 & 0x3f) << 6) | (b4 & 0x3f); + // 4-byte sequence: uuuzzzzzzyyyyyyxxxxxx = 11110uuu 10zzzzzz 10yyyyyy 10xxxxxx + byte b2 = getb() & 0x3f; + byte b3 = getb() & 0x3f; + byte b4 = getb() & 0x3f; + return ((b1 & 7) << 18) | (b2 << 12) | (b3 << 6) | b4; } - //bad start for UTF-8 multi-byte sequence - return '?'; + return 0xFFFD; +} + +// No error handling, str must be valid UTF-8 (it is ensured by document::parse_html and css_parser::parse_stylesheet). +// Currently used only in css parser, where actual char value is not needed, so it returns void. +void prev_utf8_char(const string& str, int& index) +{ + while (index && ((byte)str[--index] >> 6) == 0b10); // skip continuation bytes } -litehtml::wchar_to_utf8::wchar_to_utf8(const std::wstring& val) +void append_char(string& str, char32_t code) { - unsigned int code; - for (int i = 0; val[i]; i++) + if (code <= 0x7F) + { + str += (char)code; + } + else if (code <= 0x7FF) { - code = val[i]; - if (code <= 0x7F) - { - m_str += (char)code; - } - else if (code <= 0x7FF) - { - m_str += (code >> 6) + 192; - m_str += (code & 63) + 128; - } - else if (0xd800 <= code && code <= 0xdfff) - { - //invalid block of utf8 - } - else if (code <= 0xFFFF) - { - m_str += (code >> 12) + 224; - m_str += ((code >> 6) & 63) + 128; - m_str += (code & 63) + 128; - } - else if (code <= 0x10FFFF) - { - m_str += (code >> 18) + 240; - m_str += ((code >> 12) & 63) + 128; - m_str += ((code >> 6) & 63) + 128; - m_str += (code & 63) + 128; - } + str += char((code >> 6) + 192); + str += (code & 63) + 128; + } + else if (0xd800 <= code && code <= 0xdfff) + { + // error: unexpected surrogate (code is UTF-32, not UTF-16) + } + else if (code <= 0xFFFF) + { + str += char((code >> 12) + 224); + str += ((code >> 6) & 63) + 128; + str += (code & 63) + 128; + } + else if (code <= 0x10FFFF) + { + str += char((code >> 18) + 240); + str += ((code >> 12) & 63) + 128; + str += ((code >> 6) & 63) + 128; + str += (code & 63) + 128; } } + +utf8_to_utf32::utf8_to_utf32(const string& val) +{ + int index = 0; + while (char32_t ch = read_utf8_char(val, index)) + m_str += ch; +} + +utf32_to_utf8::utf32_to_utf8(const std::u32string& val) +{ + for (auto ch : val) + append_char(m_str, ch); +} + +} // namespace litehtml diff --git a/src/web_color.cpp b/src/web_color.cpp index 78b201fac..c9472432d 100644 --- a/src/web_color.cpp +++ b/src/web_color.cpp @@ -1,12 +1,26 @@ #include "html.h" #include "web_color.h" -#include <cstring> +#include "css_parser.h" +#include "os_types.h" +#include "document_container.h" -const litehtml::web_color litehtml::web_color::transparent = web_color(0, 0, 0, 0); -const litehtml::web_color litehtml::web_color::black = web_color(0, 0, 0, 255); -const litehtml::web_color litehtml::web_color::white = web_color(255, 255, 255, 255); +namespace litehtml +{ + +const web_color web_color::transparent = web_color(0, 0, 0, 0); +const web_color web_color::black = web_color(0, 0, 0, 255); +const web_color web_color::white = web_color(255, 255, 255, 255); +const web_color web_color::current_color = web_color(true); + +gradient gradient::transparent; + +struct def_color +{ + const char* name; + const char* rgb; +}; -litehtml::def_color litehtml::g_def_colors[] = +def_color g_def_colors[] = { {"transparent","rgba(0, 0, 0, 0)"}, {"AliceBlue","#F0F8FF"}, @@ -154,121 +168,280 @@ litehtml::def_color litehtml::g_def_colors[] = {"WhiteSmoke","#F5F5F5"}, {"Yellow","#FFFF00"}, {"YellowGreen","#9ACD32"}, - {nullptr,nullptr} }; - -litehtml::web_color litehtml::web_color::from_string(const string& _str, document_container* callback) +// <hex-color> https://drafts.csswg.org/css-color-4/#typedef-hex-color +bool parse_hash_color(const css_token& tok, web_color& color) { - auto str = _str.c_str(); - if(!str[0]) + if (tok.type != HASH) return false; + + string s = tok.str; + int len = (int)s.size(); + if (!is_one_of(len, 3, 4, 6, 8)) return false; + for (auto ch : s) if (!is_hex_digit(ch)) return false; + + string r, g, b, a = "ff"; + if (len == 3 || len == 4) { - return web_color(0, 0, 0); + r = {s[0], s[0]}; + g = {s[1], s[1]}; + b = {s[2], s[2]}; + if (len == 4) + a = {s[3], s[3]}; } - if(str[0] == '#') + else // 6 or 8 { - string red; - string green; - string blue; - if(strlen(str + 1) == 3) - { - red += str[1]; - red += str[1]; - green += str[2]; - green += str[2]; - blue += str[3]; - blue += str[3]; - } else if(strlen(str + 1) == 6) - { - red += str[1]; - red += str[2]; - green += str[3]; - green += str[4]; - blue += str[5]; - blue += str[6]; - } - char* sss = nullptr; - web_color clr; - clr.red = (byte) strtol(red.c_str(), &sss, 16); - clr.green = (byte) strtol(green.c_str(), &sss, 16); - clr.blue = (byte) strtol(blue.c_str(), &sss, 16); - return clr; - } else if(!strncmp(str, "rgb", 3)) + r = {s[0], s[1]}; + g = {s[2], s[3]}; + b = {s[4], s[5]}; + if (len == 8) + a = {s[6], s[7]}; + } + auto read_two_hex_digits = [](auto str) { - string s = str; + return byte(16 * digit_value(str[0]) + digit_value(str[1])); + }; - string::size_type pos = s.find_first_of('('); - if(pos != string::npos) - { - s.erase(s.begin(), s.begin() + pos + 1); - } - pos = s.find_last_of(')'); - if(pos != string::npos) + color = web_color( + read_two_hex_digits(r), + read_two_hex_digits(g), + read_two_hex_digits(b), + read_two_hex_digits(a)); + return true; +} + +float clamp(float x, float min, float max) +{ + if (x < min) return min; + if (x > max) return max; + return x; +} + +// [ <number> | <percentage> | none ]{3} [ / [<alpha-value> | none] ]? +bool parse_modern_syntax(const css_token_vector& tokens, bool is_hsl, + css_length& x, css_length& y, css_length& z, css_length& a) +{ + auto n = tokens.size(); + if (!(n == 3 || n == 5)) return false; + if (is_hsl) + { + // [<hue> | none] [<number> | <percentage> | none]{2} [ / [<alpha-value> | none] ]? + // <hue> = <number> | <angle> + if (!x.from_token(tokens[0], f_number, "none")) { - s.erase(s.begin() + pos, s.end()); + float hue; + if (!parse_angle(tokens[0], hue)) return false; + x.set_value(hue, css_units_none); } + } + else if (!x.from_token(tokens[0], f_number | f_percentage, "none")) return false; + if (!y.from_token(tokens[1], f_number | f_percentage, "none")) return false; + if (!z.from_token(tokens[2], f_number | f_percentage, "none")) return false; + if (n == 5) + { + if (tokens[3].ch != '/') return false; + // <alpha-value> = <number> | <percentage> + if (!a.from_token(tokens[4], f_number | f_percentage, "none")) return false; + } + // convert nones to zeros + // https://drafts.csswg.org/css-color-4/#missing + // For all other purposes, a missing component behaves as a zero value... + for (auto t : {&x,&y,&z,&a}) if (t->is_predefined()) t->set_value(0, css_units_none); + return true; +} - std::vector<string> tokens; - split_string(s, tokens, ", \t"); +// https://drafts.csswg.org/css-color-4/#rgb-functions +// Values outside these ranges are not invalid, but are clamped to the ranges defined here at parsed-value time. +byte calc_percent_and_clamp(const css_length& val, float max = 255) +{ + float x = val.val(); + if (val.units() == css_units_percentage) x = (x / 100) * max; + x = clamp(x, 0, max); + return (byte)round(max == 1 ? x * 255 : x); +} - web_color clr; +// https://drafts.csswg.org/css-color-4/#rgb-functions +bool parse_rgb_func(const css_token& tok, web_color& color) +{ + if (tok.type != CV_FUNCTION || !is_one_of(lowcase(tok.name), "rgb", "rgba")) + return false; - if(tokens.size() >= 1) clr.red = (byte) atoi(tokens[0].c_str()); - if(tokens.size() >= 2) clr.green = (byte) atoi(tokens[1].c_str()); - if(tokens.size() >= 3) clr.blue = (byte) atoi(tokens[2].c_str()); - if(tokens.size() >= 4) clr.alpha = (byte) (t_strtod(tokens[3].c_str(), nullptr) * 255.0); + auto list = parse_comma_separated_list(tok.value); + int n = (int)list.size(); + if (!is_one_of(n, 1, 3, 4)) + return false; - return clr; - } else + css_length r, g, b, a(1, css_units_none); + // legacy syntax: <percentage>#{3} , <alpha-value>? | <number>#{3} , <alpha-value>? + if (n != 1) { - string rgb = resolve_name(str, callback); - if(!rgb.empty()) - { - return from_string(rgb.c_str(), callback); - } + for (const auto& item : list) if (item.size() != 1) return false; + auto type = list[0][0].type; + if (!is_one_of(type, PERCENTAGE, NUMBER)) return false; + int options = type == PERCENTAGE ? f_percentage : f_number; + if (!r.from_token(list[0][0], options)) return false; + if (!g.from_token(list[1][0], options)) return false; + if (!b.from_token(list[2][0], options)) return false; + // <alpha-value> = <number> | <percentage> + if (n == 4 && !a.from_token(list[3][0], f_number | f_percentage)) return false; } - return web_color(0, 0, 0); + // modern syntax: [ <number> | <percentage> | none ]{3} [ / [<alpha-value> | none] ]? + else if (!parse_modern_syntax(tok.value, false, r, g, b, a)) return false; + + color = web_color( + calc_percent_and_clamp(r), + calc_percent_and_clamp(g), + calc_percent_and_clamp(b), + calc_percent_and_clamp(a, 1)); + return true; } -litehtml::string litehtml::web_color::resolve_name(const string& name, document_container* callback) +// https://drafts.csswg.org/css-color-4/#hsl-to-rgb +void hsl_to_rgb(float hue, float sat, float light, float& r, float& g, float& b) { - for(int i=0; g_def_colors[i].name; i++) + hue = fmod(hue, 360.f); + + if (hue < 0) + hue += 360; + + sat /= 100; + light /= 100; + + auto f = [=](float n) { - if(!t_strcasecmp(name.c_str(), g_def_colors[i].name)) - { - return g_def_colors[i].rgb; - } + float k = fmod(n + hue / 30, 12.f); + float a = sat * min(light, 1 - light); + return light - a * max(-1.f, min({k - 3, 9 - k, 1.f})); + }; + + r = f(0); + g = f(8); + b = f(4); +} + +// https://drafts.csswg.org/css-color-4/#the-hsl-notation +bool parse_hsl_func(const css_token& tok, web_color& color) +{ + if (tok.type != CV_FUNCTION || !is_one_of(lowcase(tok.name), "hsl", "hsla")) + return false; + + auto list = parse_comma_separated_list(tok.value); + int n = (int)list.size(); + if (!is_one_of(n, 1, 3, 4)) + return false; + + css_length h, s, l, a(1, css_units_none); + // legacy syntax: <hue>, <percentage>, <percentage>, <alpha-value>? + if (n != 1) + { + for (const auto& item : list) if (item.size() != 1) return false; + const auto& tok0 = list[0][0]; + float hue; + // <hue> = <number> | <angle> + // number is interpreted as a number of degrees https://drafts.csswg.org/css-color-4/#typedef-hue + if (tok0.type == NUMBER) hue = tok0.n.number; + else if (!parse_angle(tok0, hue)) return false; + h.set_value(hue, css_units_none); + + if (!s.from_token(list[1][0], f_percentage)) return false; + if (!l.from_token(list[2][0], f_percentage)) return false; + if (n == 4 && !a.from_token(list[3][0], f_number | f_percentage)) return false; } - if (callback) - { - string clr = callback->resolve_color(name); - return clr; - } - return ""; + // modern syntax: [<hue> | none] [<percentage> | <number> | none]{2} [ / [<alpha-value> | none] ]? + else if (!parse_modern_syntax(tok.value, true, h, s, l, a)) return false; + + float hue = h.val(); + // no percent calculation needed for sat and lit because 0% ~ 0, 100% ~ 100 + float sat = s.val(); + float lit = l.val(); + // For historical reasons, if the saturation is less than 0% it is clamped to 0% at parsed-value time, before being converted to an sRGB color. + if (sat < 0) sat = 0; + + // Note: Chrome and Firefox treat invalid hsl values differently. + // + // Note: at this point, sat is not clamped at 100, and lit is not clamped at all. The standard + // mentions only clamping sat at 0. As a result, returning rgb values may not be inside [0,1]. + float r, g, b; + hsl_to_rgb(hue, sat, lit, r, g, b); + + r = clamp(r, 0, 1); + g = clamp(g, 0, 1); + b = clamp(b, 0, 1); + + color = web_color( + (byte)round(r * 255), + (byte)round(g * 255), + (byte)round(b * 255), + calc_percent_and_clamp(a, 1)); + return true; } -bool litehtml::web_color::is_color(const string& str, document_container* callback) +// https://drafts.csswg.org/css-color-5/#typedef-color-function +bool parse_func_color(const css_token& tok, web_color& color) { - if (!t_strncasecmp(str.c_str(), "rgb", 3) || str[0] == '#') + return parse_rgb_func(tok, color) || parse_hsl_func(tok, color); +} + +string resolve_name(const string& name, document_container* container) +{ + for (auto clr : g_def_colors) { - return true; + if (equal_i(name, clr.name)) + return clr.rgb; } - if (t_isalpha(str[0]) && resolve_name(str, callback) != "") + + if (container) + return container->resolve_color(name); + + return ""; +} + +bool parse_name_color(const css_token& tok, web_color& color, document_container* container) +{ + if (tok.type != IDENT) return false; + if (tok.ident() == "currentcolor") { + color = web_color::current_color; return true; } - return false; + string str = resolve_name(tok.name, container); + auto tokens = normalize(str, f_componentize | f_remove_whitespace); + if (tokens.size() != 1) return false; + return parse_color(tokens[0], color, container); +} + +// https://drafts.csswg.org/css-color-5/#typedef-color +bool parse_color(const css_token& tok, web_color& color, document_container* container) +{ + return + parse_hash_color(tok, color) || + parse_func_color(tok, color) || + parse_name_color(tok, color, container); } -litehtml::string litehtml::web_color::to_string() const +web_color web_color::darken(double fraction) const { - char str[9]; - if(alpha) - { + int v_red = (int)red; + int v_blue = (int)blue; + int v_green = (int)green; + v_red = (int)max(v_red - (v_red * fraction), 0.0); + v_blue = (int)max(v_blue - (v_blue * fraction), 0.0); + v_green = (int)max(v_green - (v_green * fraction), 0.0); + return {(byte)v_red, (byte)v_green, (byte)v_blue, alpha}; +} + + +string web_color::to_string() const +{ + char str[9]; + if(alpha) + { t_snprintf(str, 9, "%02X%02X%02X%02X", red, green, blue, alpha); - } else - { + } else + { t_snprintf(str, 9, "%02X%02X%02X", red, green, blue); - } - return str; + } + return str; } + +} // namespace litehtml \ No newline at end of file diff --git a/support/README.md b/support/README.md new file mode 100644 index 000000000..58f35ac06 --- /dev/null +++ b/support/README.md @@ -0,0 +1,309 @@ +# Litehtml support sources + +This folder contains source code that can help to integrate litehtml to the application. + +## [gtkmm4] GTKMM4 Widget + +This is implementation widget for GTK4 applications. Widget is written with GTKMM4 library. + +This is the example how to add widget to the window: + +```c++ +// Declaration of the widget +html_widget m_html; + +// Append widget to the vertical box. +m_vbox.append(m_html); +m_html.set_expand(true); + +// Connect to signals +m_html.signal_set_address().connect( sigc::mem_fun(*this, &browser_window::set_address) ); +m_html.signal_update_state().connect( sigc::mem_fun(*this, &browser_window::update_buttons) ); +signal_close_request().connect(sigc::mem_fun(m_html, &html_widget::on_close), false); + +..... + +// Set url to the text +void browser_window::set_address(const std::string& text) +{ + m_address_bar.set_text(text); +} + +// Update the state of buttons depending of the current state of the widget +void browser_window::update_buttons(uint32_t) +{ + uint32_t state = m_html.get_state(); + + if((m_prev_state & page_state_has_back) != (state & page_state_has_back)) + { + m_back_button.set_sensitive(state & page_state_has_back); + } + if((m_prev_state & page_state_has_forward) != (state & page_state_has_forward)) + { + m_forward_button.set_sensitive(state & page_state_has_forward); + } + if((m_prev_state & page_state_downloading) != (state & page_state_downloading)) + { + if (state & page_state_downloading) + { + m_stop_reload_button.set_image_from_icon_name("process-stop-symbolic"); + } else + { + m_stop_reload_button.set_image_from_icon_name("view-refresh-symbolic"); + } + } + m_prev_state = state; +} + +``` + +To open url just call ```m_html.open_url("http://domain.tld/index")```. + +Please note, widget requires all file from **webpage** folder. + +## [webpage] Webpage implementation + +A set of classes to implement webpage handling: + +* Using ```container_cairo_pango``` as a container +* Download files with libcurl library +* Multi-threading downloading + +Please refer the gtkmm4 widget to find detailed information about using webpage classes. + +### litebrowser::html_host_interface + +The interface to interact with widget or browser window. You have to implement all methods of this class and pass pointer to the ```litebrowser::web_page``` constructor. + +#### Methods + +```c++ +virtual void open_url(const std::string& url); +``` +Method is called to open urls, for example when user click the anchor. + +```c++ +virtual void update_cursor(); +``` +Method is called when the mounse cursor was changed. Call ```web_page::get_cursor()``` to get the cursor name. + +```c++ +virtual void scroll_to(int x, int y); +``` +Scroll page to the position [x, y]. Applied to jump to the url fragment (#fragment). + +```c++ +virtual void get_client_rect(litehtml::position& client) const; +``` +Return the client rectangle of the widget. Usually this is the rectangle with widget width and height. + +```c++ +virtual void redraw_boxes(const litehtml::position::vector& boxes); +``` +Redraw the boxes from ```boxes``` vector. This method is called to support changes on mouse move/click like ```:hover```. + +```c++ +virtual int get_render_width(); +``` +Get width to render to. Usually it is the widget width. + +```c++ +virtual double get_dpi() +``` +Get screen DPI. This value is used to calculate font size and some units like ```em```. Application should return at least default value 96. + +```c++ +virtual int get_screen_width(); +virtual int get_screen_height(); +``` +Get screen width and height. Used for media queries. + +```c++ +virtual cairo_surface_t* load_image(const std::string& path); +``` +Load image from specified ```path```. + + +### litebrowser::browser_notify_interface + +This class is similar to the ```litebrowser::html_host_interface```, but all methods should be dispatched to the GUI thread. + +#### Methods + +```c++ +virtual void redraw(); +``` +Query widget redraw + +```c++ +virtual void render(); +``` +Query widget re-render + +```c++ +virtual void update_state(); +``` +Notify about webpage state change + +```c++ +virtual void on_set_caption(const std::string& caption_text); +``` +Query change the window title + +```c++ +virtual void on_page_loaded(uint64_t web_page_id); +``` +Notify about page was loaded and rendered + +### litebroeser::web_page + +A helper class for the web page. This class implements all required methods of the ```container_cairo_pango``` class. Also it implements ```std::enable_shared_from_this``` class; + +#### Methods + +```c++ +web_page(html_host_interface* html_host, std::shared_ptr<browser_notify_interface> notify, int pool_size); +``` +Web page class constructor: +* **html_host** - implementation of ```html_host_interface``` class. +* **notify** - shared pointer to the ```browser_notify_interface``` implementation. +* **pool_size** - number of threads to use for page loading. + +```c++ +uint64_t id() const; +``` +Returns page identifier. + +```c++ +const std::string& get_html_source() const; +``` +Returns source code of the page. + +```c++ +void open(const litehtml::string& url, const litehtml::string& hash); +``` +Open url. +* **url** - page url +* **hash** - page #fragment + +```c++ +void on_mouse_over(int x, int y, int client_x, int client_y); +``` +Call this function on mouse move. +* **x** - mouse pointer horizontal position in document coordinates. +* **y** - mouse pointer vertical position in document coordinates. +* **client_x** - mouse pointer horizontal position in widget coordinates. +* **client_y** - mouse pointer vertical position in widget coordinates. + +```c++ +void on_lbutton_down(int x, int y, int client_x, int client_y); +``` +Call this function on left mouse button down. +* **x** - mouse pointer horizontal position in document coordinates. +* **y** - mouse pointer vertical position in document coordinates. +* **client_x** - mouse pointer horizontal position in widget coordinates. +* **client_y** - mouse pointer vertical position in widget coordinates. + +```c++ +void on_lbutton_up(int x, int y, int client_x, int client_y); +``` +Call this function on left mouse button up. +* **x** - mouse pointer horizontal position in document coordinates. +* **y** - mouse pointer vertical position in document coordinates. +* **client_x** - mouse pointer horizontal position in widget coordinates. +* **client_y** - mouse pointer vertical position in widget coordinates. + +```c++ +const std::string& get_cursor() const; +``` +Get mouse cursor. Refer to the [CSS cursor property](https://developer.mozilla.org/en-US/docs/Web/CSS/cursor) + +```c++ +int render(int max_width); +``` +Render page to the specified width. + +```c++ +const std::string& url() const; +``` +Get page url. + +```c++ +int width() const; +int height() const; +``` +Get page width and height. + +```c++ +bool media_changed(); +``` +Apply changed media to the webpage. Returns ```true``` is re-render is required. + +```c++ +void stop_loading() +``` +Stop downloading the page. + +```c++ +bool is_downloading() +``` +Returns ```true``` if something is downloading. + +### litebrowser::draw_buffer + +A helper class to perform the draw operations into the cairo surface. The application draws everything to the buffer, then buffer are drawn on widged or window. + +Some notes about scaling support: +* All functions accept scale independent coordinates and sizes. So don't apply scale to the coordinates and sizes. +* The size of used ```cairo_surface_t``` is always scaled. So if scale factor is **2.0** and buffer width is **600**, then surface width will **1200**. +* If the scale factor is not **1** then buffer ```left``` and ```top``` will be rounded to the integer coordinates. +* Always use ```draw_buffer::get_left()``` and ```draw_buffer::get_top()``` for functions like ```web_page::on_mouse_over```. + +#### Methods + +```c++ +int get_width() const; +int get_height() const; +``` +Get width and height of the buffer. + +```c++ +int get_left() const; +int get_top() const; +``` +Get left and top position of the buffer's (0, 0) coordinates. Note: buffer works like viewport. left and top of the buffer defines a viewport shift. + +```c++ +cairo_surface_t* get_cairo_surface() const { return m_draw_buffer; } +``` +Get buffer cairo surface. + +```c++ +double get_scale_factor() const { return m_scale_factor; } +``` +Get buffer scale factor + +```c++ +void set_scale_factor(std::shared_ptr<litebrowser::web_page> page, double scale) +``` +Set the scale factor for the buffer. Default scale factor is 1. + +```c++ +void on_size_allocate(std::shared_ptr<litebrowser::web_page> page, int width, int height); +``` +Call this function when widget size is changed. This function re-allocate new buffer, re-render and redraw the page. + +```c++ +void on_scroll(std::shared_ptr<litebrowser::web_page> page, int left, int top); +``` +Scroll page to the specified position. + +```c++ +void redraw_area(std::shared_ptr<litebrowser::web_page> page, int x, int y, int width, int height); +``` +Redraw specified area + +```c++ +void redraw(std::shared_ptr<litebrowser::web_page> page) +``` +Redraw everything. diff --git a/support/draw_buffer/draw_buffer.cpp b/support/draw_buffer/draw_buffer.cpp new file mode 100644 index 000000000..bb9266f4a --- /dev/null +++ b/support/draw_buffer/draw_buffer.cpp @@ -0,0 +1,141 @@ +#include "draw_buffer.h" +#include "litehtml/types.h" + +/// @brief Scrolls draw buffer to the position (left, top). +/// +/// Note, the actual position of the draw buffer can be rounded according to the scale factor. +/// Use get_left() and get_top() to know the actual position. +/// +/// @param cb_draw the callback for drawing the page +/// @param left new horizontal position +/// @param top new vertical position +/// @param fixed_boxes fixed boxes to be redrawn +void litebrowser::draw_buffer::on_scroll(const draw_page_function_t& cb_draw, int left, int top, const litehtml::position::vector& fixed_boxes) +{ + if(m_width <= 0 || m_height <= 0 || !m_draw_buffer) + return; + + top = fix_position(top); + left = fix_position(left); + + if(m_left != left || m_top != top) + { + litehtml::position rec_current(m_left, m_top, m_width, m_height); // Current area + litehtml::position rec_new(left, top, m_width, m_height); // New area + litehtml::position rec_clean = rec_current.intersect(rec_new); // Clean area + if(rec_clean.empty() || rec_new == rec_current) + { + m_left = left; + m_top = top; + redraw(cb_draw); + } else + { + int shift_x = m_left - left; + int shift_y = m_top - top; + + int surface_shift_x = (int) std::floor((double) shift_x * m_scale_factor); + int surface_shift_y = (int) std::floor((double) shift_y * m_scale_factor); + + auto new_surface = make_surface(m_width, m_height, m_scale_factor); + cairo_t* cr = cairo_create(new_surface); + cairo_rectangle(cr, (rec_clean.x - left) * m_scale_factor - m_scale_factor, + (rec_clean.y - top) * m_scale_factor - m_scale_factor, + std::ceil((double) rec_clean.width * m_scale_factor) + 2.0 * m_scale_factor, + std::ceil((double) rec_clean.height * m_scale_factor) + 2.0 * m_scale_factor); + cairo_clip(cr); + cairo_set_source_surface(cr, m_draw_buffer, surface_shift_x, surface_shift_y); + cairo_paint(cr); + cairo_destroy(cr); + cairo_surface_destroy(m_draw_buffer); + m_draw_buffer = new_surface; + + m_left = left; + m_top = top; + + int right = fix_position(m_left + m_width); + int bottom = fix_position(m_top + m_height); + int clean_right = fix_position(rec_clean.x + rec_clean.width); + int clean_bottom = fix_position(rec_clean.y + rec_clean.height); + + if(rec_clean.x > m_left) + { + redraw_area(cb_draw, m_left, rec_clean.y, rec_clean.x - m_left, rec_clean.height); + } + if(clean_right < right) + { + redraw_area(cb_draw, clean_right, rec_clean.y, right - clean_right, rec_clean.height); + } + + if(rec_clean.y > m_top) + { + redraw_area(cb_draw, m_left, m_top, m_width, rec_clean.y - m_top); + } + if(clean_bottom < bottom) + { + redraw_area(cb_draw, m_left, clean_bottom, m_width, bottom - clean_bottom); + } + + for(const auto& box : fixed_boxes) + { + redraw_area(cb_draw, m_left + box.left(), m_top + box.top(), box.width, box.height); + redraw_area(cb_draw, m_left + box.left() + shift_x, m_top + box.top() + shift_y, box.width, box.height); + } + } + } +} + +/// @brief Redraw the defined area of the buffer +/// +/// All coordinated are not scaled. The actual rectangle could be different, according to the scale factor, +/// but it must always cover the requested. +/// +/// @param cb_draw the callback for drawing the page +/// @param x left position of the area +/// @param y top position of the area +/// @param width width of the area +/// @param height height of the area +void litebrowser::draw_buffer::redraw_area(const draw_page_function_t& cb_draw, int x, int y, int width, int height) +{ + if(m_draw_buffer) + { + int fixed_left = fix_position(x - m_left); + int fixed_right = fix_position(x - m_left + width); + int fixed_top = fix_position(y - m_top); + int fixed_bottom = fix_position(y - m_top + height); + + if(fixed_right < x + width) + fixed_right += m_min_int_position; + if(fixed_bottom < y + height) + fixed_bottom += m_min_int_position; + + int fixed_x = fixed_left; + int fixed_y = fixed_top; + int fixed_width = fixed_right - fixed_left; + int fixed_height = fixed_bottom - fixed_top; + + int s_x = (int) std::round((double) fixed_x * m_scale_factor); + int s_y = (int) std::round((double) fixed_y * m_scale_factor); + int s_width = (int) std::round((double) fixed_width * m_scale_factor); + int s_height = (int) std::round((double) fixed_height * m_scale_factor); + + litehtml::position pos{fixed_x, fixed_y, fixed_width, fixed_height}; + cairo_t* cr = cairo_create(m_draw_buffer); + + // Apply clip with scaled position to avoid artifacts + cairo_rectangle(cr, s_x, s_y, s_width, s_height); + cairo_clip(cr); + + // Clear rectangle with scaled position + cairo_rectangle(cr, s_x, s_y, s_width, s_height); + cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); + cairo_fill(cr); + + // Apply scale for drawing + cairo_scale(cr, m_scale_factor, m_scale_factor); + + // Draw page + cb_draw(cr, -m_left, -m_top, &pos); + + cairo_destroy(cr); + } +} diff --git a/support/draw_buffer/draw_buffer.h b/support/draw_buffer/draw_buffer.h new file mode 100644 index 000000000..dbd5d35fd --- /dev/null +++ b/support/draw_buffer/draw_buffer.h @@ -0,0 +1,186 @@ +#ifndef LITEBROWSER_DRAW_BUFFER_H +#define LITEBROWSER_DRAW_BUFFER_H + +#include <cairo.h> +#include <functional> +#include <cmath> +#include <memory> +#include "litehtml/types.h" + +namespace litebrowser +{ + /// @brief Draw Buffer Class + /// + /// This class performs the draw operations into the cairo surface. + /// The application draws everything to the buffer, then buffer is + /// drawn on widget or window. + class draw_buffer + { + cairo_surface_t* m_draw_buffer = nullptr; + int m_width = 0; + int m_height = 0; + int m_top = 0; + int m_left = 0; + double m_scale_factor = 1; + int m_min_int_position = 1; + public: + + using draw_page_function_t = std::function<void(cairo_t* cr, int x, int y, const litehtml::position* clip)>; + + ~draw_buffer() + { + if(m_draw_buffer) + { + cairo_surface_destroy(m_draw_buffer); + } + } + + [[nodiscard]] + int get_width() const { return m_width; } + + [[nodiscard]] + int get_height() const { return m_height; } + + [[nodiscard]] + int get_left() const { return m_left; } + + [[nodiscard]] + int get_top() const { return m_top; } + + [[nodiscard]] + cairo_surface_t* get_cairo_surface() const { return m_draw_buffer; } + + [[nodiscard]] + double get_scale_factor() const { return m_scale_factor; } + + /// @brief Set scale factor for draw buffer + /// @param cb_draw the callback for drawing the page + /// @param scale the scale factor to be applied + void set_scale_factor(const draw_page_function_t& cb_draw, double scale) + { + if(m_scale_factor != scale) + { + m_scale_factor = scale; + m_min_int_position = get_denominator(m_scale_factor); + + m_top = fix_position(m_top); + m_left = fix_position(m_left); + + if(m_draw_buffer) + { + cairo_surface_destroy(m_draw_buffer); + } + m_draw_buffer = nullptr; + create_draw_buffer(m_width, m_height); + redraw(cb_draw); + } + } + + /// @brief Create cairo surface for draw buffer + /// @param width surface width (not scaled) + /// @param height surface height (not scaled) + /// @param scale_factor scale factor + /// @return pointer to the cairo surface + static cairo_surface_t* make_surface(int width, int height, double scale_factor) + { + return cairo_image_surface_create(CAIRO_FORMAT_RGB24, + std::ceil((double) width * scale_factor), + std::ceil((double) height * scale_factor)); + } + + /// @brief Creates new buffer with specified size + /// @param width draw buffer width (not scaled) + /// @param height draw buffer height (not scaled) + /// @return true if new draw buffer was created, false if the old buffer was used + bool create_draw_buffer(int width, int height) + { + if(m_width != width || m_height != height || !m_draw_buffer) + { + m_width = width; + m_height = height; + if(m_draw_buffer) + { + cairo_surface_destroy(m_draw_buffer); + } + m_draw_buffer = nullptr; + if(m_width > 0 && m_height > 0) + { + m_draw_buffer = make_surface(m_width, m_height, m_scale_factor); + } + return true; + } + return false; + } + + /// @brief Call this function when widget size changed + /// @param cb_draw the callback for drawing the page + /// @param width new draw buffer width + /// @param height new draw buffer height + void on_size_allocate(const draw_page_function_t& cb_draw, int width, int height) + { + if(create_draw_buffer(width, height)) + { + redraw(cb_draw); + } + } + + /// @brief Scrolls draw buffer to the position (left, top). + /// + /// Note, the actual position of the draw buffer can be rounded according to the scale factor. + /// Use get_left() and get_top() to know the actual position. + /// + /// @param cb_draw the callback for drawing the page + /// @param left new horizontal position + /// @param top new vertical position + /// @param fixed_boxes fixed boxes to be redrawn + void on_scroll(const draw_page_function_t& cb_draw, int left, int top, const litehtml::position::vector& fixed_boxes); + + /// @brief Redraw the defined area of the buffer + /// + /// All coordinated are not scaled. The actual rectangle could be different, according to the scale factor, + /// but it must always cover the requested. + /// + /// @param cb_draw the callback for drawing the page + /// @param x left position of the area + /// @param y top position of the area + /// @param width width of the area + /// @param height height of the area + void redraw_area(const draw_page_function_t& cb_draw, int x, int y, int width, int height); + + /// @brief Redraw entire buffer + /// @param cb_draw the callback for drawing the page + void redraw(const draw_page_function_t& cb_draw) + { + redraw_area(cb_draw, m_left, m_top, m_width, m_height); + } + + private: + [[nodiscard]] int fix_position(int pos) const + { + return (pos / m_min_int_position) * m_min_int_position; + } + + static int get_common_divisor(int a, int b) + { + while (b != 0) + { + int t = b; + b = a % b; + a = t; + } + return a; + } + + static int get_denominator(double decimal) + { + int numerator = (int)(decimal * 100); + int denominator = 100; + + int common_divisor = get_common_divisor(numerator, denominator); + return denominator / common_divisor; + } + }; + +} + +#endif diff --git a/support/gtkmm4/html_widget.cpp b/support/gtkmm4/html_widget.cpp new file mode 100644 index 000000000..d31d4fb83 --- /dev/null +++ b/support/gtkmm4/html_widget.cpp @@ -0,0 +1,795 @@ +#include "html_widget.h" +#include <chrono> + +html_widget::html_widget() +{ + add_css_class("litehtml"); + + m_notifier = std::make_shared<html_widget_notifier>(); + m_notifier->connect_redraw(sigc::mem_fun(*this, &html_widget::on_redraw)); + m_notifier->connect_render(sigc::mem_fun(*this, &html_widget::render)); + m_notifier->connect_update_state([this]() { m_sig_update_state.emit(get_state()); }); + m_notifier->connect_on_page_loaded(sigc::mem_fun(*this, &html_widget::on_page_loaded)); + m_notifier->connect_on_set_caption(sigc::mem_fun(*this, &html_widget::set_caption)); + + set_focusable(true); + + m_vadjustment = Gtk::Adjustment::create(0.0, 0.0, 0.0, 10); + m_vadjustment->signal_value_changed().connect(sigc::mem_fun(*this, &html_widget::on_vadjustment_changed)); + m_hadjustment = Gtk::Adjustment::create(0.0, 0.0, 0.0, 10); + m_hadjustment->signal_value_changed().connect(sigc::mem_fun(*this, &html_widget::on_hadjustment_changed)); + + m_hadjustment->signal_changed().connect(sigc::mem_fun(*this, &html_widget::on_adjustments_changed)); + m_vadjustment->signal_changed().connect(sigc::mem_fun(*this, &html_widget::on_adjustments_changed)); + + m_vscrollbar = Gtk::make_managed<Gtk::Scrollbar>(m_vadjustment, Gtk::Orientation::VERTICAL); + m_vscrollbar->insert_at_end(*this); + m_vscrollbar->set_visible(true); + m_vscrollbar->add_css_class("right"); + m_vscrollbar->add_css_class("overlay-indicator"); + + m_hscrollbar = Gtk::make_managed<Gtk::Scrollbar>(m_hadjustment, Gtk::Orientation::HORIZONTAL); + m_hscrollbar->insert_at_end(*this); + m_hscrollbar->set_visible(true); + m_hscrollbar->add_css_class("bottom"); + m_hscrollbar->add_css_class("overlay-indicator"); + + auto controller = Gtk::EventControllerScroll::create(); + controller->set_flags(Gtk::EventControllerScroll::Flags::BOTH_AXES); + controller->signal_scroll().connect(sigc::mem_fun(*this, &html_widget::on_scroll), false); + add_controller(controller); + + auto click_gesture = Gtk::GestureClick::create(); + click_gesture->set_button(GDK_BUTTON_PRIMARY); + click_gesture->signal_pressed().connect(sigc::mem_fun(*this, &html_widget::on_button_press_event)); + click_gesture->signal_released().connect(sigc::mem_fun(*this, &html_widget::on_button_release_event)); + add_controller(click_gesture); + + auto move_gesture = Gtk::EventControllerMotion::create(); + move_gesture->signal_motion().connect(sigc::mem_fun(*this, &html_widget::on_mouse_move)); + move_gesture->signal_leave().connect([this]() { restart_scrollbar_timer(); }); + add_controller(move_gesture); + + auto key_controller = Gtk::EventControllerKey::create(); + key_controller->signal_key_pressed().connect(sigc::mem_fun(*this, &html_widget::on_key_pressed), false); + add_controller(key_controller); +} + +html_widget::~html_widget() +{ + m_notifier->disconnect(); + + if (!gobj()) return; + + while (Widget* child = get_first_child()) + { + child->unparent(); + } +} + +double html_widget::get_dpi() +{ + // DPI is always 96 (default). Scaling will make things larger. + return 96.0; +} + +int html_widget::get_screen_width() +{ + auto display = Gdk::Display::get_default(); + if (display) + { + auto monitors = display->get_monitors(); + if (monitors->get_n_items() > 0) + { + auto monitor = monitors->get_typed_object<Gdk::Monitor>(0); + Gdk::Rectangle rect; + monitor->get_geometry(rect); + return rect.get_width(); + } + } + return 800; +} + +int html_widget::get_screen_height() +{ + auto display = Gdk::Display::get_default(); + if (display) + { + auto monitors = display->get_monitors(); + if (monitors->get_n_items() > 0) + { + auto monitor = monitors->get_typed_object<Gdk::Monitor>(0); + Gdk::Rectangle rect; + monitor->get_geometry(rect); + return rect.get_height(); + } + } + return 800; +} + +void html_widget::snapshot_vfunc(const Glib::RefPtr<Gtk::Snapshot>& snapshot) +{ + if (get_allocated_width() <= 0 || get_allocated_height() <= 0) + { + return; + } + + { + auto allocation = get_allocation(); + auto cr = snapshot->append_cairo(Gdk::Rectangle(0, 0, allocation.get_width(), allocation.get_height())); + if(m_draw_buffer.get_cairo_surface()) + { + cr->scale(1.0 / m_draw_buffer.get_scale_factor(), 1.0 / m_draw_buffer.get_scale_factor()); + cairo_set_source_surface(cr->cobj(), m_draw_buffer.get_cairo_surface(), 0, 0); + cr->paint(); + } + } + + snapshot_child(*m_vscrollbar, snapshot); + snapshot_child(*m_hscrollbar, snapshot); +} + +void html_widget::get_viewport(litehtml::position& viewport) const +{ + viewport.x = m_draw_buffer.get_left(); + viewport.y = m_draw_buffer.get_top(); + viewport.width = m_draw_buffer.get_width(); + viewport.height = m_draw_buffer.get_height(); +} + +void html_widget::set_caption(const std::string& caption) +{ + auto root = get_root(); + if(root) + { + auto window = dynamic_cast<Gtk::Window*>(root); + if (window) + { + window->set_title(caption.c_str()); + }; + } +} + +cairo_surface_t *html_widget::load_image(const std::string &path) +{ + Glib::RefPtr<Gdk::Pixbuf> ptr; + + try + { + ptr = Gdk::Pixbuf::create_from_file(path); + } catch (...) { } + + if(!ptr) return nullptr; + + cairo_surface_t* ret = nullptr; + + if(ptr->get_has_alpha()) + { + ret = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, ptr->get_width(), ptr->get_height()); + } else + { + ret = cairo_image_surface_create(CAIRO_FORMAT_RGB24, ptr->get_width(), ptr->get_height()); + } + + Cairo::RefPtr<Cairo::Surface> surface(new Cairo::Surface(ret, false)); + Cairo::RefPtr<Cairo::Context> ctx = Cairo::Context::create(surface); + Gdk::Cairo::set_source_pixbuf(ctx, ptr, 0.0, 0.0); + ctx->paint(); + + return ret; + +} + +void html_widget::open_page(const litehtml::string& url, const litehtml::string& fragment) +{ + { + std::lock_guard<std::mutex> lock(m_page_mutex); + if (m_current_page) + { + m_current_page->stop_loading(); + } + m_next_page = std::make_shared<litebrowser::web_page>(this, m_notifier, 10); + m_next_page->open(url, fragment); + } + m_sig_set_address.emit(url); + m_sig_update_state.emit(get_state()); +} + +void html_widget::scroll_to(int x, int y) +{ + m_hadjustment->set_value(x); + m_vadjustment->set_value(y); +} + +void html_widget::on_redraw() +{ + m_draw_buffer.redraw(get_draw_function(current_page())); + queue_draw(); +} + +void html_widget::on_button_press_event(int /* n_press */, double x, double y) +{ + if(!has_focus()) + { + grab_focus(); + } + auto page = current_page(); + if (page) + { + page->on_lbutton_down( (int) (x + m_draw_buffer.get_left()), + (int) (y + m_draw_buffer.get_top()), + (int) x, (int) y); + } +} + +void html_widget::on_button_release_event(int /* n_press */, double x, double y) +{ + auto page = current_page(); + if(page) + { + page->on_lbutton_up((int) (x + m_draw_buffer.get_left()), + (int) (y + m_draw_buffer.get_top()), + (int) x, (int) y); + } +} + +void html_widget::on_mouse_move(double x, double y) +{ + bool restart_timer = true; + if(m_hscrollbar->is_visible()) + { + if(y >= get_allocated_height() - 16) + { + m_hscrollbar->add_css_class("hovering"); + restart_timer = false; + } else + { + m_hscrollbar->remove_css_class("hovering"); + } + } + if(m_vscrollbar->is_visible()) + { + if(x >= get_allocated_width() - 16) + { + m_vscrollbar->add_css_class("hovering"); + restart_timer = false; + } else + { + m_vscrollbar->remove_css_class("hovering"); + } + } + m_hscrollbar->set_opacity(1); + m_vscrollbar->set_opacity(1); + if(restart_timer) + { + restart_scrollbar_timer(); + } else + { + m_scrollbar_timer.disconnect(); + } + + std::shared_ptr<litebrowser::web_page> page = current_page(); + if(page) + { + page->on_mouse_over((int) (x + m_draw_buffer.get_left()), + (int) (y + m_draw_buffer.get_top()), + (int) x, (int) y); + } +} + +bool html_widget::on_key_pressed(guint keyval, guint /* keycode */, Gdk::ModifierType /* state */) +{ + switch (keyval) + { + case GDK_KEY_KP_Page_Down: + case GDK_KEY_Page_Down: + m_vadjustment->set_value(m_vadjustment->get_value() + m_vadjustment->get_page_size()); + break; + case GDK_KEY_KP_Page_Up: + case GDK_KEY_Page_Up: + m_vadjustment->set_value(m_vadjustment->get_value() - m_vadjustment->get_page_size()); + break; + case GDK_KEY_KP_Down: + case GDK_KEY_Down: + m_vadjustment->set_value(m_vadjustment->get_value() + m_vadjustment->get_step_increment()); + break; + case GDK_KEY_KP_Up: + case GDK_KEY_Up: + m_vadjustment->set_value(m_vadjustment->get_value() - m_vadjustment->get_step_increment()); + break; + case GDK_KEY_KP_Home: + case GDK_KEY_Home: + m_vadjustment->set_value(0); + break; + case GDK_KEY_KP_End: + case GDK_KEY_End: + m_vadjustment->set_value(m_vadjustment->get_upper()); + break; + case GDK_KEY_KP_Left: + case GDK_KEY_Left: + m_hadjustment->set_value(m_hadjustment->get_value() - m_hadjustment->get_step_increment()); + break; + case GDK_KEY_KP_Right: + case GDK_KEY_Right: + m_hadjustment->set_value(m_hadjustment->get_value() + m_hadjustment->get_step_increment()); + break; + + default: + break; + } + + return false; +} + +void html_widget::update_cursor() +{ + std::shared_ptr<litebrowser::web_page> page = current_page(); + if(page) + { + if(page->get_cursor() == "auto") + { + set_cursor("default"); + } else + { + set_cursor(page->get_cursor()); + } + } else + { + set_cursor("default"); + } +} + +long html_widget::draw_measure(int number) +{ + std::shared_ptr<litebrowser::web_page> page = current_page(); + + if(page) + { + litehtml::position view_port; + get_viewport(view_port); + + int width = view_port.width; + int height = view_port.height; + + int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); + auto image = (unsigned char *) g_malloc(stride * height); + + cairo_surface_t *surface = cairo_image_surface_create_for_data(image, CAIRO_FORMAT_ARGB32, width, height, + stride); + cairo_t *cr = cairo_create(surface); + + litehtml::position pos; + pos.width = width; + pos.height = height; + pos.x = 0; + pos.y = 0; + + int x = view_port.x; + int y = view_port.y; + + cairo_rectangle(cr, 0, 0, width, height); + cairo_set_source_rgb(cr, 1, 1, 1); + cairo_paint(cr); + page->draw((litehtml::uint_ptr) cr, -x, -y, &pos); + cairo_surface_write_to_png(surface, "/tmp/litebrowser.png"); + + auto t1 = std::chrono::high_resolution_clock::now(); + for (int i = 0; i < number; i++) + { + page->draw((litehtml::uint_ptr) cr, -x, -y, &pos); + } + auto t2 = std::chrono::high_resolution_clock::now(); + + cairo_destroy(cr); + cairo_surface_destroy(surface); + g_free(image); + + return (std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1)).count(); + } + return 0; +} + +long html_widget::render_measure(int number) +{ + std::shared_ptr<litebrowser::web_page> page = current_page(); + + if(page) + { + auto t1 = std::chrono::high_resolution_clock::now(); + for (int i = 0; i < number; i++) + { + page->render(m_draw_buffer.get_width()); + } + auto t2 = std::chrono::high_resolution_clock::now(); + return (std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1)).count(); + } + return -1; +} + +void html_widget::size_allocate_vfunc(int width, int height, int /* baseline */) +{ + allocate_scrollbars(width, height); + std::shared_ptr<litebrowser::web_page> page = current_page(); + if(page) + { + if(m_rendered_width != width || m_rendered_height != height) + { + m_rendered_width = width; + m_rendered_height = height; + m_draw_buffer.on_size_allocate(get_draw_function(page), width, height); + page->media_changed(); + page->render(m_rendered_width); + update_view_port(page); + m_draw_buffer.redraw(get_draw_function(page)); + queue_draw(); + } + } else + { + m_draw_buffer.on_size_allocate(get_draw_function(page), width, height); + } + +} + +void html_widget::allocate_scrollbars(int width, int height) +{ + m_do_force_redraw_on_adjustment = false; + m_hadjustment->set_page_size(width); + m_vadjustment->set_page_size(height); + if(m_vadjustment->get_value() > m_vadjustment->get_upper() - m_vadjustment->get_page_size()) + { + m_vadjustment->set_value(m_vadjustment->get_upper() - m_vadjustment->get_page_size()); + } + if(m_hadjustment->get_value() > m_hadjustment->get_upper() - m_hadjustment->get_page_size()) + { + m_hadjustment->set_value(m_hadjustment->get_upper() - m_hadjustment->get_page_size()); + } + m_do_force_redraw_on_adjustment = true; + + int minimum = 0, natural = 0, m_baseline = 0, n_baseline = 0; + + m_vscrollbar->measure(Gtk::Orientation::HORIZONTAL, -1, minimum, natural, m_baseline, n_baseline); + Gtk::Allocation vscrollbar_allocation(width - natural, 0, natural, height); + m_vscrollbar->size_allocate(vscrollbar_allocation, -1); + + minimum = 0, natural = 0, m_baseline = 0, n_baseline = 0; + m_hscrollbar->measure(Gtk::Orientation::VERTICAL, -1, minimum, natural, m_baseline, n_baseline); + Gtk::Allocation hscrollbar_allocation(0, height - natural, width, natural); + m_hscrollbar->size_allocate(hscrollbar_allocation, -1); +} + +void html_widget::on_vadjustment_changed() +{ + auto page = current_page(); + m_draw_buffer.on_scroll(get_draw_function(page), + (int) m_hadjustment->get_value(), + (int) m_vadjustment->get_value(), + page ? page->get_fixed_boxes() : litehtml::position::vector{}); + + if(m_do_force_redraw_on_adjustment) + { + force_redraw(); + } else + { + queue_draw(); + } +} + +void html_widget::on_hadjustment_changed() +{ + auto page = current_page(); + m_draw_buffer.on_scroll(get_draw_function(page), + (int) m_hadjustment->get_value(), + (int) m_vadjustment->get_value(), + page ? page->get_fixed_boxes() : litehtml::position::vector{}); + + if(m_do_force_redraw_on_adjustment) + { + force_redraw(); + } else + { + queue_draw(); + } +} + +void html_widget::on_adjustments_changed() +{ + m_hscrollbar->set_visible(m_hadjustment->get_upper() > 0); + m_vscrollbar->set_visible(m_vadjustment->get_upper() > 0); +} + +bool html_widget::on_scroll(double dx, double dy) +{ + m_vadjustment->set_value(m_vadjustment->get_value() + dy * 60); + m_hadjustment->set_value(m_hadjustment->get_value() + dx * 60); + return true; +} + +bool html_widget::on_scrollbar_timeout() +{ + m_hscrollbar->remove_css_class("hovering"); + m_vscrollbar->remove_css_class("hovering"); + m_hscrollbar->set_opacity(0); + m_vscrollbar->set_opacity(0); + return false; +} + +void html_widget::on_realize() +{ + Gtk::Widget::on_realize(); + + if(auto native = get_native()) + { + if(auto surface = native->get_surface()) + { + surface->property_scale().signal_changed().connect([this]() + { + if(auto native = get_native()) + { + if(auto surface = native->get_surface()) + { + m_draw_buffer.set_scale_factor(get_draw_function(current_page()), surface->get_scale()); + queue_draw(); + } + } + }); + } + } +} + +void html_widget::update_view_port(std::shared_ptr<litebrowser::web_page> page) +{ + if(page) + { + auto allocation = get_allocation(); + if(allocation.get_width() < page->width()) + { + m_hadjustment->set_upper(page->width()); + } else + { + m_hadjustment->set_upper(0); + } + if(allocation.get_height() < page->height()) + { + m_vadjustment->set_upper(page->height()); + } else + { + m_vadjustment->set_upper(0); + } + } else + { + m_hadjustment->set_upper(0); + m_vadjustment->set_upper(0); + } +} + +void html_widget::restart_scrollbar_timer() +{ + if (m_scrollbar_timer) + { + m_scrollbar_timer.disconnect(); + } + + m_scrollbar_timer = Glib::signal_timeout().connect_seconds( + sigc::mem_fun(*this, &html_widget::on_scrollbar_timeout), 2); +} + +void html_widget::redraw_boxes(const litehtml::position::vector& boxes) +{ + if(boxes.empty()) return; + + Gdk::Rectangle rect(0, 0, 0, 0); + bool is_first = true; + for(const auto& pos : boxes) + { + if(is_first) + { + rect = Gdk::Rectangle(pos.x, pos.y, pos.width, pos.height); + is_first = false; + } else + { + rect.join(Gdk::Rectangle(pos.x, pos.y, pos.width, pos.height)); + } + } + + if(!rect.has_zero_area()) + { + m_draw_buffer.redraw_area(get_draw_function(current_page()), rect.get_x(), rect.get_y(), rect.get_width(), rect.get_height()); + queue_draw(); + } +} + +int html_widget::get_render_width() +{ + return m_draw_buffer.get_width(); +} + +void html_widget::on_page_loaded(uint64_t web_page_id) +{ + std::string url; + { + std::lock_guard<std::mutex> lock(m_page_mutex); + if(m_next_page->id() != web_page_id) return; + m_current_page = m_next_page; + m_next_page = nullptr; + url = m_current_page->url(); + update_view_port(m_current_page); + } + scroll_to(0, 0); + on_redraw(); + m_sig_set_address.emit(url); + m_sig_update_state.emit(get_state()); +} + +void html_widget::show_fragment(const std::string &fragment) +{ + std::shared_ptr<litebrowser::web_page> page = current_page(); + if(page) + { + page->show_fragment(fragment); + } +} + +void html_widget::open_url(const std::string &url) +{ + std::string fragment; + std::string s_url = url; + + m_sig_set_address.emit(url); + + std::string::size_type fragment_pos = s_url.find_first_of(L'#'); + if(fragment_pos != std::wstring::npos) + { + fragment = s_url.substr(fragment_pos + 1); + s_url.erase(fragment_pos); + } + + bool open_hash_only = false; + bool reload = false; + + auto current_url = m_history.current(); + fragment_pos = current_url.find_first_of(L'#'); + if(fragment_pos != std::wstring::npos) + { + current_url.erase(fragment_pos); + } + + if(!current_url.empty()) + { + if(m_history.current() != url) + { + if (current_url == s_url) + { + open_hash_only = true; + } + } else + { + reload = true; + } + } + if(!open_hash_only) + { + open_page(url, fragment); + } else + { + show_fragment(fragment); + } + if(!reload) + { + m_history.url_opened(url); + } + m_sig_update_state.emit(get_state()); +} + +void html_widget::render() +{ + std::shared_ptr<litebrowser::web_page> page = current_page(); + if(page) + { + page->render(m_draw_buffer.get_width()); + update_view_port(page); + m_draw_buffer.redraw(get_draw_function(page)); + queue_draw(); + } +} + +bool html_widget::on_close() +{ + if(m_current_page) + { + m_current_page->stop_loading(); + } + if(m_next_page) + { + m_next_page->stop_loading(); + } + return false; +} + +void html_widget::dump(litehtml::dumper &cout) +{ + std::shared_ptr<litebrowser::web_page> page = current_page(); + if(page) + { + page->dump(cout); + } +} + +void html_widget::go_forward() +{ + std::string url; + if(m_history.forward(url)) + { + open_url(url); + } +} + +void html_widget::go_back() +{ + std::string url; + if(m_history.back(url)) + { + open_url(url); + } +} + +uint32_t html_widget::get_state() +{ + uint32_t ret = 0; + std::string url; + if(m_history.back(url)) + { + ret |= page_state_has_back; + } + if(m_history.forward(url)) + { + ret |= page_state_has_forward; + } + { + std::lock_guard<std::mutex> lock(m_page_mutex); + if(m_next_page) + { + ret |= page_state_downloading; + } + } + if(!(ret & page_state_downloading)) + { + std::shared_ptr<litebrowser::web_page> page = current_page(); + if(page) + { + if(page->is_downloading()) + { + ret |= page_state_downloading; + } + } + } + return ret; +} + +void html_widget::stop_download() +{ + std::lock_guard<std::mutex> lock(m_page_mutex); + if(m_next_page) + { + m_next_page->stop_loading(); + } else if (m_current_page) + { + m_current_page->stop_loading(); + } +} + +void html_widget::reload() +{ + std::shared_ptr<litebrowser::web_page> page = current_page(); + if(page) + { + open_url(page->url()); + } +} + +std::string html_widget::get_html_source() +{ + std::lock_guard<std::mutex> lock(m_page_mutex); + if(m_current_page) + return m_current_page->get_html_source(); + return {}; +} diff --git a/support/gtkmm4/html_widget.h b/support/gtkmm4/html_widget.h new file mode 100644 index 000000000..f98064c8f --- /dev/null +++ b/support/gtkmm4/html_widget.h @@ -0,0 +1,312 @@ +#pragma once + +#include <gtkmm.h> +#include "html_host.h" +#include "web_page.h" +#include "web_history.h" +#include "../draw_buffer/draw_buffer.h" +#include <queue> + +enum page_state +{ + page_state_has_back = 0x01, + page_state_has_forward = 0x02, + page_state_downloading = 0x04, +}; + +/// @brief Allows perform actions in the GUI thread via notifications +class html_widget_notifier : public litebrowser::browser_notify_interface +{ +public: + using redraw_func = std::function<void()>; + using render_func = std::function<void()>; + using update_state_func = std::function<void()>; + using on_page_loaded_func = std::function<void(uint64_t)>; + using on_set_caption_func = std::function<void(const std::string&)>; +private: + enum func_type + { + func_type_none, + func_type_redraw, + func_type_render, + func_type_update_state, + func_type_on_page_loaded, + func_type_on_set_caption + }; + struct queue_item + { + func_type type; + uint64_t param; + std::string str_param; + }; + + Glib::Dispatcher m_dispatcher; + redraw_func m_redraw_func; + render_func m_render_func; + update_state_func m_update_state_func; + on_page_loaded_func m_on_page_loaded_func; + on_set_caption_func m_on_set_caption_func; + + std::mutex m_lock; + bool m_disconnect = false; + std::queue<queue_item> m_queue; + +public: + + html_widget_notifier() + { + m_dispatcher.connect(sigc::mem_fun(*this, &html_widget_notifier::on_notify)); + } + + void disconnect() + { + std::lock_guard lock(m_lock); + m_disconnect = true; + } + + void connect_redraw(redraw_func _redraw_func) + { + m_redraw_func = _redraw_func; + } + + void connect_render(render_func _render_func) + { + m_render_func = _render_func; + } + + void connect_update_state(update_state_func _update_state_func) + { + m_update_state_func = _update_state_func; + } + + void connect_on_page_loaded(on_page_loaded_func _on_page_loaded_func) + { + m_on_page_loaded_func = _on_page_loaded_func; + } + + void connect_on_set_caption(on_set_caption_func _on_set_caption_func) + { + m_on_set_caption_func = _on_set_caption_func; + } + +private: + + void redraw() override + { + { + std::lock_guard lock(m_lock); + m_queue.push(queue_item{func_type_redraw, 0, {}}); + } + m_dispatcher.emit(); + } + + void render() override + { + { + std::lock_guard lock(m_lock); + m_queue.push(queue_item{func_type_render, 0, {}}); + } + m_dispatcher.emit(); + } + + void update_state() override + { + { + std::lock_guard lock(m_lock); + m_queue.push(queue_item{func_type_update_state, 0, {}}); + } + m_dispatcher.emit(); + } + + void on_page_loaded(uint64_t web_page_id) override + { + { + std::lock_guard lock(m_lock); + m_queue.push(queue_item{func_type_on_page_loaded, web_page_id, {}}); + } + m_dispatcher.emit(); + } + + void on_set_caption(const std::string& caption) override + { + { + std::lock_guard lock(m_lock); + m_queue.push(queue_item{func_type_on_set_caption, 0, caption}); + } + m_dispatcher.emit(); + } + + void on_notify() + { + std::queue<queue_item> local_queue; + { + std::lock_guard lock(m_lock); + if(m_disconnect || m_queue.empty()) return; + + while(!m_queue.empty()) + { + local_queue.push(m_queue.front()); + m_queue.pop(); + } + } + func_type prev_type = func_type_none; + while(!local_queue.empty()) + { + const auto& item = local_queue.front(); + // We don't need do the same action some times + if(item.type != prev_type) + { + prev_type = item.type; + switch (item.type) + { + case func_type_redraw: + if(m_redraw_func) + { + m_redraw_func(); + } + break; + case func_type_render: + if(m_render_func) + { + m_render_func(); + } + break; + case func_type_update_state: + if(m_update_state_func) + { + m_update_state_func(); + } + break; + case func_type_on_page_loaded: + if(m_on_page_loaded_func) + { + m_on_page_loaded_func(item.param); + } + break; + case func_type_on_set_caption: + if(m_on_set_caption_func) + { + m_on_set_caption_func(item.str_param); + } + break; + + default: + break; + } + } + local_queue.pop(); + } + } +}; + +class html_widget : public Gtk::Widget, + public litebrowser::html_host_interface +{ + int m_rendered_width = 0; + int m_rendered_height = 0; + bool m_do_force_redraw_on_adjustment = true; + std::mutex m_page_mutex; + std::shared_ptr<litebrowser::web_page> m_current_page; + std::shared_ptr<litebrowser::web_page> m_next_page; + std::shared_ptr<html_widget_notifier> m_notifier; + web_history m_history; + + Gtk::Scrollbar* m_vscrollbar; + Gtk::Scrollbar* m_hscrollbar; + Glib::RefPtr<Gtk::Adjustment> m_vadjustment; + Glib::RefPtr<Gtk::Adjustment> m_hadjustment; + + sigc::connection m_scrollbar_timer; + + litebrowser::draw_buffer m_draw_buffer; + +public: + explicit html_widget(); + ~html_widget() override; + + void on_page_loaded(uint64_t web_page_id); + void render(); + void go_forward(); + void go_back(); + uint32_t get_state(); + void stop_download(); + void reload(); + + std::string get_html_source(); + long render_measure(int number); + long draw_measure(int number); + void show_fragment(const std::string& fragment); + bool on_close(); + void dump(litehtml::dumper& cout); + + void open_url(const std::string& url) override; + +protected: + // litebrowser::html_host_interface override + double get_dpi() override; + int get_screen_width() override; + int get_screen_height() override; + void open_page(const litehtml::string& url, const litehtml::string& fragment); + void update_cursor() override; + void redraw_boxes(const litehtml::position::vector& boxes) override; + int get_render_width() override; + void scroll_to(int x, int y) override; + void get_viewport(litehtml::position& viewport) const override; + cairo_surface_t* load_image(const std::string& path) override; + + void snapshot_vfunc(const Glib::RefPtr<Gtk::Snapshot>& snapshot) override; + void on_redraw(); + + void on_button_press_event(int n_press, double x, double y); + void on_button_release_event(int n_press, double x, double y); + void on_mouse_move(double x, double y); + bool on_key_pressed(guint keyval, guint keycode, Gdk::ModifierType state); + + void size_allocate_vfunc(int width, int height, int baseline) override; + void allocate_scrollbars(int width, int height); + + void set_caption(const std::string& caption); + void on_vadjustment_changed(); + void on_hadjustment_changed(); + void on_adjustments_changed(); + bool on_scroll(double dx, double dy); + bool on_scrollbar_timeout(); + void on_realize() override; + +private: + std::shared_ptr<litebrowser::web_page> current_page() + { + std::lock_guard<std::mutex> lock(m_page_mutex); + return m_current_page; + } + + void update_view_port(std::shared_ptr<litebrowser::web_page> page); + void restart_scrollbar_timer(); + void force_redraw() + { + queue_draw(); + while (g_main_context_iteration(nullptr, false)) {} + } + litebrowser::draw_buffer::draw_page_function_t get_draw_function(const std::shared_ptr<litebrowser::web_page>& page) + { + return [this, page](cairo_t* cr, int x, int y, const litehtml::position* clip) + { + if (page) + { + page->draw((litehtml::uint_ptr) cr, x, y, clip); + } + }; + } + +public: + // Signals types + using sig_set_address_t = sigc::signal<void(std::string)>; + using sig_update_state_t = sigc::signal<void(uint32_t)>; + + sig_set_address_t signal_set_address() { return m_sig_set_address; } + sig_update_state_t signal_update_state() { return m_sig_update_state; } + +private: + sig_update_state_t m_sig_update_state; + sig_set_address_t m_sig_set_address; +}; diff --git a/support/webpage/html_host.h b/support/webpage/html_host.h new file mode 100644 index 000000000..a8632e177 --- /dev/null +++ b/support/webpage/html_host.h @@ -0,0 +1,40 @@ +#ifndef LITEBROWSER_HTML_HOST_H +#define LITEBROWSER_HTML_HOST_H + +#include <cairo.h> +#include <litehtml.h> + +namespace litebrowser +{ + class html_host_interface + { + public: + virtual ~html_host_interface() = default; + + virtual void open_url(const std::string& url) = 0; + virtual void update_cursor() = 0; + virtual void scroll_to(int x, int y) = 0; + virtual void get_viewport(litehtml::position& viewport) const = 0; + virtual void redraw_boxes(const litehtml::position::vector& boxes) = 0; + virtual int get_render_width() = 0; + virtual double get_dpi() = 0; + virtual int get_screen_width() = 0; + virtual int get_screen_height() = 0; + virtual cairo_surface_t* load_image(const std::string& path) = 0; + }; + + class browser_notify_interface + { + public: + virtual ~browser_notify_interface() = default; + + virtual void redraw() = 0; + virtual void render() = 0; + virtual void update_state() = 0; + virtual void on_set_caption(const std::string& caption_text) = 0; + virtual void on_page_loaded(uint64_t web_page_id) = 0; + }; + +} + +#endif //LITEBROWSER_HTML_HOST_H diff --git a/support/webpage/http_request.cpp b/support/webpage/http_request.cpp new file mode 100644 index 000000000..d83b2c0f7 --- /dev/null +++ b/support/webpage/http_request.cpp @@ -0,0 +1,129 @@ +#include <set> +#include <sstream> +#include "http_request.h" + +bool litebrowser::http_request::open() +{ + m_canceled = false; + CURL* curl = curl_easy_init(); + + if(m_url[0] == '/') + { + std::set<char> chars_to_escape = {' ', ':'}; + std::stringstream new_url; + new_url << "file://"; + for(auto ch : m_url) + { + if( chars_to_escape.find(ch) != chars_to_escape.end()) + { + new_url << '%' << std::hex << (uint32_t) ch; + } else + { + new_url << ch; + } + } + m_url = new_url.str(); + } + + curl_easy_setopt(curl, CURLOPT_URL, m_url.c_str()); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, (long) 1); + curl_easy_setopt(curl, CURLOPT_MAXREDIRS, (long) 10); + + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, http_request::write_function); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, this); + + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, (long) 0); + + curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, (long) 512); + curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, (long) 30); + + curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, http_request::progress_function); + curl_easy_setopt(curl, CURLOPT_XFERINFODATA, this); + + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, (long) 0); + + char errMessage[1024]; + errMessage[0] = 0; + + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errMessage); + curl_easy_setopt(curl, CURLOPT_FAILONERROR, (long) 1); + if(!m_user_agent.empty()) + { + curl_easy_setopt(curl, CURLOPT_USERAGENT, m_user_agent.c_str()); + } else + { + curl_easy_setopt(curl, CURLOPT_USERAGENT, "Mozilla/5.0 (X11; Linux x86_64; rv:122.0) Gecko/20100101 Firefox/122.0"); + } + + curl_slist* slist = nullptr; + if (!m_headers.empty()) + { + for (auto& item : m_headers) + { + slist = curl_slist_append(slist, item.c_str()); + } + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist); + } + + std::string post_str; + if (!m_form_data.empty()) + { + for (const auto& field : m_form_data) + { + if (!post_str.empty()) post_str += "&"; + post_str += field.first; + post_str += "="; + post_str += field.second; + } + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_str.c_str()); + } + + m_session = curl; + CURLcode res = curl_easy_perform(curl); + + long httpCode = 0; + + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpCode); + + m_session = nullptr; + curl_easy_cleanup(curl); + + if (slist) + { + curl_slist_free_all(slist); + } + m_form_data.clear(); + m_headers.clear(); + + if(m_on_finish) + { + m_on_finish(httpCode, res, errMessage, m_url); + } + return true; +} + +size_t litebrowser::http_request::write_function(void *ptr, size_t size, size_t nmemb, void *stream) +{ + auto pThis = (http_request*) stream; + + pThis->m_downloaded += size * nmemb; + if(!pThis->m_total) + { + curl_off_t sz = 0; + curl_easy_getinfo((CURL*) pThis->m_session, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &sz); + pThis->m_total = (size_t) sz; + } + if(pThis->m_on_data) + { + pThis->m_on_data(ptr, size * nmemb, pThis->m_downloaded, pThis->m_total); + } + + return size * nmemb; +} + +int litebrowser::http_request::progress_function(void* clientp, curl_off_t /*dltotal*/, curl_off_t /*dlnow*/, curl_off_t /*ultotal*/, curl_off_t /*ulnow*/) +{ + auto pThis = (http_request*) clientp; + return pThis->m_canceled ? 1 : 0; +} + diff --git a/support/webpage/http_request.h b/support/webpage/http_request.h new file mode 100644 index 000000000..209ad0a8e --- /dev/null +++ b/support/webpage/http_request.h @@ -0,0 +1,75 @@ +#ifndef LITEBROWSER_HTTP_REQUEST_H +#define LITEBROWSER_HTTP_REQUEST_H + +#include <curl/curl.h> +#include <functional> +#include <cstdlib> +#include <string> +#include <thread> +#include <utility> +#include <vector> +#include <memory> + +namespace litebrowser +{ + class http_requests_pool; + + class http_request : public std::enable_shared_from_this<http_request> + { + private: + std::function<void(void* data, size_t len, size_t downloaded, size_t total)> m_on_data; + std::function<void(u_int32_t http_status, u_int32_t err_code, const std::string& err_text, const std::string& url)> m_on_finish; + volatile bool m_canceled; + size_t m_downloaded; + size_t m_total; + volatile void* m_session; + std::vector<std::string> m_headers; + std::vector<std::pair<std::string, std::string>> m_form_data; + std::string m_user_agent; + std::string m_url; + public: + http_request(std::string url, + std::function<void(void* data, size_t len, size_t downloaded, size_t total)> cb_on_data, + std::function<void(u_int32_t http_status, u_int32_t err_code, const std::string& err_text, const std::string& url)> cb_on_finish) : + m_on_data(std::move(cb_on_data)), + m_on_finish(std::move(cb_on_finish)), + m_canceled(false), + m_downloaded(0), + m_total(0), + m_session(nullptr), + m_url(std::move(url)) + {} + ~http_request() + { + } + + bool open(); + void cancel() + { + m_canceled = true; + } + void add_header(const std::string& header) + { + m_headers.push_back(header); + } + void clear_headers() + { + m_headers.clear(); + } + void add_form_data(const std::string& field, const std::string& value) + { + m_form_data.emplace_back(field, value); + } + void clear_form_data() + { + m_form_data.clear(); + } + const std::string& get_url() { return m_url; } + private: + void thread_proc(); + static size_t write_function( void *ptr, size_t size, size_t nmemb, void *stream ); + static int progress_function(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow ); + }; +} + +#endif //LITEBROWSER_HTTP_REQUEST_H diff --git a/support/webpage/http_requests_pool.cpp b/support/webpage/http_requests_pool.cpp new file mode 100644 index 000000000..e0e5e0ce5 --- /dev/null +++ b/support/webpage/http_requests_pool.cpp @@ -0,0 +1,104 @@ +#include "http_requests_pool.h" + +litebrowser::http_requests_pool::http_requests_pool(int pool_size, const std::function<void()>& cb_update_state) : + m_cancel(false), + m_cb_update_state(cb_update_state) +{ + for(int i = 0; i < pool_size; i++) + { + m_threads.emplace_back(std::make_shared<pool_thread>(this)); + } +} + +void litebrowser::http_requests_pool::enqueue(const std::string &url, + const std::function<void(void* data, size_t len, size_t downloaded, size_t total)>& cb_on_data, + const std::function<void(u_int32_t http_status, u_int32_t err_code, const std::string& err_text, const std::string& url)>& cb_on_finish) +{ + { + std::unique_lock<std::mutex> lock(m_queue_mutex); + auto request = std::make_shared<http_request>(url, cb_on_data, cb_on_finish); + m_queue.push_back(request); + } + m_mutex_condition.notify_one(); +} + +void litebrowser::http_requests_pool::stop() +{ + { + std::unique_lock<std::mutex> lock(m_queue_mutex); + m_cancel = true; + } + m_mutex_condition.notify_all(); + for (auto& active_thread : m_threads) + { + auto request = active_thread->get_request(); + if(request) + { + request->cancel(); + } + } + for (auto& active_thread : m_threads) + { + active_thread->thread().join(); + } + m_threads.clear(); +} + +bool litebrowser::http_requests_pool::is_downloading() +{ + { + std::unique_lock<std::mutex> lock(m_queue_mutex); + if(m_cancel) return false; + if(!m_queue.empty()) return true; + } + for (auto& active_thread : m_threads) + { + auto request = active_thread->get_request(); + if(request) + { + return true; + } + } + + return false; +} + +void litebrowser::pool_thread::thread_loop() +{ + while (true) + { + { + std::unique_lock<std::mutex> lock(m_pool->m_queue_mutex); + m_pool->m_mutex_condition.wait(lock, [this] + { + return !m_pool->m_queue.empty() || m_pool->m_cancel; + }); + if (m_pool->m_cancel) + { + if(m_pool->m_cb_update_state) + { + m_pool->m_cb_update_state(); + } + return; + } + { + std::unique_lock<std::mutex> request_lock(m_request_mutex); + m_request = m_pool->m_queue.front(); + } + m_pool->m_queue.pop_front(); + } + if(m_pool->m_cb_update_state) + { + m_pool->m_cb_update_state(); + } + m_request->open(); + { + std::unique_lock<std::mutex> request_lock(m_request_mutex); + m_request = nullptr; + } + if(m_pool->m_cb_update_state) + { + m_pool->m_cb_update_state(); + } + } +} diff --git a/support/webpage/http_requests_pool.h b/support/webpage/http_requests_pool.h new file mode 100644 index 000000000..9511c1a6e --- /dev/null +++ b/support/webpage/http_requests_pool.h @@ -0,0 +1,62 @@ +#ifndef LITEBROWSER_HTTP_REQUESTS_POOL_H +#define LITEBROWSER_HTTP_REQUESTS_POOL_H + +#include <memory> +#include <mutex> +#include <list> +#include <condition_variable> +#include "http_request.h" + +namespace litebrowser +{ + class http_requests_pool; + + class pool_thread + { + http_requests_pool* m_pool; + std::shared_ptr<http_request> m_request; + std::mutex m_request_mutex; + std::thread m_thread; + public: + explicit pool_thread(http_requests_pool* pool) : m_pool(pool) + { + m_thread = std::thread(&pool_thread::thread_loop, this); + } + std::shared_ptr<http_request> get_request() + { + std::unique_lock<std::mutex> request_lock(m_request_mutex); + return m_request; + } + std::thread& thread() { return m_thread; } + private: + void thread_loop(); + }; + + class http_requests_pool : public std::enable_shared_from_this<http_requests_pool> + { + friend class pool_thread; + protected: + bool m_cancel; + std::mutex m_queue_mutex; + std::condition_variable m_mutex_condition; + std::list<std::shared_ptr<http_request>> m_queue; + std::vector<std::shared_ptr<pool_thread>> m_threads; + std::function<void()> m_queue_empty_cb; + std::function<void()> m_cb_update_state; + public: + explicit http_requests_pool(int pool_size, const std::function<void()>& cb_update_state); + ~http_requests_pool() + { + stop(); + } + + void enqueue(const std::string& url, + const std::function<void(void* data, size_t len, size_t downloaded, size_t total)>& cb_on_data, + const std::function<void(u_int32_t http_status, u_int32_t err_code, const std::string& err_text, const std::string& url)>& cb_on_finish); + void stop(); + bool is_downloading(); + private: + }; +} + +#endif //LITEBROWSER_HTTP_REQUESTS_POOL_H diff --git a/support/webpage/web_history.cpp b/support/webpage/web_history.cpp new file mode 100755 index 000000000..63b658657 --- /dev/null +++ b/support/webpage/web_history.cpp @@ -0,0 +1,62 @@ +#include "web_history.h" + + +void web_history::url_opened( const std::string& url ) +{ + if(!m_items.empty()) + { + if(m_current_item != m_items.size() - 1) + { + if(m_current_item > 0 && m_items[m_current_item - 1] == url) + { + m_current_item--; + } else if(m_current_item < m_items.size() - 1 && m_items[m_current_item + 1] == url) + { + m_current_item++; + } else + { + m_items.erase(m_items.begin() + m_current_item + 1, m_items.end()); + m_items.push_back(url); + m_current_item = m_items.size() - 1; + } + } else + { + if(m_current_item > 0 && m_items[m_current_item - 1] == url) + { + m_current_item--; + } else + { + m_items.push_back(url); + m_current_item = m_items.size() - 1; + } + } + } else + { + m_items.push_back(url); + m_current_item = m_items.size() - 1; + } +} + +bool web_history::back( std::string& url ) +{ + if(m_items.empty()) return false; + + if(m_current_item > 0) + { + url = m_items[m_current_item - 1]; + return true; + } + return false; +} + +bool web_history::forward( std::string& url ) +{ + if(m_items.empty()) return false; + + if(m_current_item < m_items.size() - 1) + { + url = m_items[m_current_item + 1]; + return true; + } + return false; +} diff --git a/support/webpage/web_history.h b/support/webpage/web_history.h new file mode 100755 index 000000000..7975b2347 --- /dev/null +++ b/support/webpage/web_history.h @@ -0,0 +1,28 @@ +#pragma once +#include <string> +#include <vector> + +using string_vector = std::vector<std::string>; + +class web_history +{ + string_vector m_items; + string_vector::size_type m_current_item = 0; +public: + web_history() = default; + virtual ~web_history() = default; + + void url_opened(const std::string& url); + bool back(std::string& url); + bool forward(std::string& url); + + [[nodiscard]] + std::string current() const + { + if(!m_items.empty() && m_current_item < m_items.size()) + { + return m_items[m_current_item]; + } + return ""; + } +}; \ No newline at end of file diff --git a/support/webpage/web_page.cpp b/support/webpage/web_page.cpp new file mode 100644 index 000000000..fdbb78067 --- /dev/null +++ b/support/webpage/web_page.cpp @@ -0,0 +1,332 @@ +#include <litehtml/url_path.h> +#include <litehtml/url.h> +#include "web_page.h" + +void litebrowser::text_file::on_data(void* data, size_t len, size_t /*downloaded*/, size_t /*total*/) +{ + stream.write((const char*) data, (std::streamsize) len); +} + +void litebrowser::text_file::on_page_downloaded(u_int32_t http_status, + u_int32_t err_code, + const std::string &/*err_text*/) +{ + if(err_code == 0 && (http_status == 200 || http_status == 0)) + { + data_ready = true; + } else + { + data_ready = false; + } + wait_mutex.unlock(); +} + +void litebrowser::web_page::open(const std::string &url, const std::string &fragment) +{ + litehtml::url l_url(url); + + if(!l_url.has_scheme()) + { + if(url.front() != '/') + { + m_url = "http://" + url; + } else + { + m_url = url; + } + } else + { + m_url = url; + } + m_base_url = m_url; + m_fragment = fragment; + + auto data = std::make_shared<text_file>(); + auto cb_on_data = [data](void* in_data, size_t len, size_t /*downloaded*/, size_t /*total*/) { data->on_data(in_data, len, 0, 0); }; + auto shared_this = shared_from_this(); + auto cb_on_finish = [shared_this, data](u_int32_t http_status, u_int32_t err_code, const std::string &err_text, const std::string& url) + { + shared_this->on_page_downloaded(data, http_status, err_code, err_text, url); + }; + http_request(m_url, cb_on_data, cb_on_finish); +} + +void litebrowser::web_page::get_viewport(litehtml::position& viewport) const +{ + m_html_host->get_viewport(viewport); +} + +void litebrowser::web_page::on_anchor_click(const char* url, const litehtml::element::ptr& /*el*/) +{ + if(url) + { + make_url(url, m_base_url.c_str(), m_clicked_url); + } +} + +void litebrowser::web_page::set_cursor(const char* cursor) +{ + if(cursor) + { + if(m_cursor != cursor) + { + m_cursor = cursor; + m_html_host->update_cursor(); + } + } +} + +void litebrowser::web_page::import_css(litehtml::string& text, const litehtml::string& url, litehtml::string& baseurl) +{ + std::string css_url; + make_url(url.c_str(), baseurl.c_str(), css_url); + + auto data = std::make_shared<text_file>(); + auto cb_on_data = [data](void* in_data, size_t len, size_t /*downloaded*/, size_t /*total*/) { data->on_data(in_data, len, 0, 0); }; + auto cb_on_finish = [data](u_int32_t http_status, u_int32_t err_code, const std::string &err_text, const std::string& /*url*/) + { + data->on_page_downloaded(http_status, err_code, err_text); + }; + http_request(css_url, cb_on_data, cb_on_finish); + data->wait(); + text = data->str(); + if(!text.empty()) + { + baseurl = css_url; + } +} + +void litebrowser::web_page::set_caption(const char* caption) +{ + m_notify->on_set_caption(caption); +} + +void litebrowser::web_page::set_base_url(const char* base_url) +{ + if(base_url) + { + m_base_url = litehtml::resolve(litehtml::url(m_url), litehtml::url(base_url)).str(); + } else + { + m_base_url = m_url; + } +} + +cairo_surface_t* litebrowser::web_page::get_image(const std::string& url) +{ + return m_images.get_image(url); +} + +void litebrowser::web_page::show_fragment(const litehtml::string& fragment) +{ + std::lock_guard<std::recursive_mutex> html_lock(m_html_mutex); + if(fragment.empty() || !m_html) + { + m_html_host->scroll_to(0, 0); + } else + { + auto escaped_hash = litehtml::get_escaped_string(fragment); + std::string selector = ":is([id=\"" + escaped_hash + "\"],[name=\"" + escaped_hash + "\"])"; + litehtml::element::ptr el = m_html->root()->select_one(selector); + if (el) + { + litehtml::position pos = el->get_placement(); + m_html_host->scroll_to(0, pos.top()); + } + } +} + +void litebrowser::web_page::make_url(const char* url, const char* basepath, litehtml::string& out) +{ + if(!basepath || !basepath[0]) + { + if(!m_base_url.empty()) + { + out = litehtml::resolve(litehtml::url(m_base_url), litehtml::url(url)).str(); + } else + { + out = url; + } + } else + { + out = litehtml::resolve(litehtml::url(basepath), litehtml::url(url)).str(); + } +} + +void litebrowser::web_page::on_mouse_over(int x, int y, int client_x, int client_y) +{ + std::lock_guard<std::recursive_mutex> html_lock(m_html_mutex); + if(m_html) + { + litehtml::position::vector redraw_boxes; + if(m_html->on_mouse_over(x, y, client_x, client_y, redraw_boxes)) + { + m_html_host->redraw_boxes(redraw_boxes); + } + } +} + +void litebrowser::web_page::on_lbutton_down(int x, int y, int client_x, int client_y) +{ + std::lock_guard<std::recursive_mutex> html_lock(m_html_mutex); + if(m_html) + { + litehtml::position::vector redraw_boxes; + if(m_html->on_lbutton_down(x, y, client_x, client_y, redraw_boxes)) + { + m_html_host->redraw_boxes(redraw_boxes); + } + } +} + +void litebrowser::web_page::on_lbutton_up(int x, int y, int client_x, int client_y) +{ + if(m_html) + { + { + std::lock_guard<std::recursive_mutex> html_lock(m_html_mutex); + + litehtml::position::vector redraw_boxes; + m_clicked_url.clear(); + if (m_html->on_lbutton_up(x, y, client_x, client_y, redraw_boxes)) + { + m_html_host->redraw_boxes(redraw_boxes); + } + } + if(!m_clicked_url.empty()) + { + m_html_host->open_url(m_clicked_url); + } + } +} + +void litebrowser::web_page::on_page_downloaded(std::shared_ptr<text_file> data, + u_int32_t /*http_status*/, + u_int32_t err_code, + const std::string &err_text, const std::string& url) +{ + if(err_code == 0) + { + m_url = url; + std::lock_guard<std::recursive_mutex> html_lock(m_html_mutex); + data->set_ready(); + m_html_source = data->str(); + } else + { + std::lock_guard<std::recursive_mutex> html_lock(m_html_mutex); + std::stringstream ss; + ss << "<h1>Impossible to load page</h1>" << std::endl; + ss << "<p>Error #" << err_code << ": " << err_text << "</p>" << std::endl; + m_html_source = ss.str(); + } + + m_html = litehtml::document::createFromString(m_html_source, this); + if (m_html) + { + std::lock_guard<std::recursive_mutex> html_lock(m_html_mutex); + int render_width = m_html_host->get_render_width(); + m_html->render(render_width); + } + m_notify->on_page_loaded(id()); +} + +void litebrowser::web_page::on_image_downloaded(std::shared_ptr<image_file> data, + u_int32_t http_status, + u_int32_t err_code, + const std::string &/*err_text*/, + const std::string& /*url*/) +{ + data->close(); + if(!data->path().empty() && !err_code && (http_status == 200 || http_status == 0)) + { + auto ptr = m_html_host->load_image(data->path()); + if(ptr) + { + m_images.add_image(data->url(), ptr); + if(data->redraw_only()) + { + m_notify->redraw(); + } else + { + m_notify->render(); + } + } + } + unlink(data->path().c_str()); +} + +void litebrowser::web_page::load_image(const char *src, const char *baseurl, bool redraw_on_ready) +{ + std::string url; + make_url(src, baseurl, url); + + if(m_images.reserve(url)) + { + auto data = std::make_shared<image_file>(url, redraw_on_ready); + auto cb_on_data = [data](void* in_data, size_t len, size_t /*downloaded*/, size_t /*total*/) { data->on_data(in_data, len, 0, 0); }; + auto shared_this = shared_from_this(); + auto cb_on_finish = [shared_this, data](u_int32_t http_status, u_int32_t err_code, const std::string &err_text, const std::string& url) + { + shared_this->on_image_downloaded(data, http_status, err_code, err_text, url); + }; + + http_request(url, cb_on_data, cb_on_finish); + } +} + +void litebrowser::web_page::http_request(const std::string &url, + const std::function<void(void *, size_t, size_t, size_t)> &cb_on_data, + const std::function<void(u_int32_t, u_int32_t, const std::string &, const std::string &)> &cb_on_finish) +{ + m_requests_pool.enqueue(url, cb_on_data, cb_on_finish); +} + +void litebrowser::web_page::on_pool_update_state() +{ + m_notify->update_state(); +} + +double litebrowser::web_page::get_screen_dpi() const +{ + return m_html_host->get_dpi(); +} + +int litebrowser::web_page::get_screen_width() const +{ + return m_html_host->get_screen_width(); +} + +int litebrowser::web_page::get_screen_height() const +{ + return m_html_host->get_screen_height(); +} + +void litebrowser::web_page::on_mouse_event(const litehtml::element::ptr& /*el*/, litehtml::mouse_event /*event*/) +{ + +} + +////////////////////////////////////////////////////////// + +litebrowser::image_file::image_file(std::string url, bool redraw_on_ready) : + m_url(std::move(url)), + m_redraw_on_ready(redraw_on_ready) +{ +} + +void litebrowser::image_file::on_data(void *data, size_t len, size_t /*downloaded*/, size_t /*total*/) +{ + if(m_fd < 0) + { + char nameBuff[] = "/tmp/litebrowser-XXXXXX"; + m_fd = mkstemp(nameBuff); + if(m_fd >= 0) + { + m_path = nameBuff; + } + } + if(m_fd >= 0) + { + write(m_fd, data, len); + } +} diff --git a/support/webpage/web_page.h b/support/webpage/web_page.h new file mode 100644 index 000000000..6080b4166 --- /dev/null +++ b/support/webpage/web_page.h @@ -0,0 +1,189 @@ +#ifndef LITEBROWSER_WEB_PAGE_H +#define LITEBROWSER_WEB_PAGE_H + +#include <unistd.h> +#include <sstream> +#include "container_cairo_pango.h" +#include "html_host.h" +#include "http_requests_pool.h" +#include "cairo_images_cache.h" +#include "litehtml/types.h" + +namespace litebrowser +{ + class text_file + { + std::mutex wait_mutex; + std::stringstream stream; + bool data_ready = false; + public: + text_file() + { + wait_mutex.lock(); + } + + void set_ready() { data_ready = true; } + + std::string str() const { return data_ready ? stream.str() : ""; } + void wait() + { + wait_mutex.lock(); + } + void on_data(void* data, size_t len, size_t downloaded, size_t total); + void on_page_downloaded(u_int32_t http_status, u_int32_t err_code, const std::string& err_text); + }; + + class image_file + { + int m_fd = -1; + std::string m_path; + std::string m_url; + bool m_redraw_on_ready; + public: + explicit image_file(std::string url, bool redraw_on_ready); + void on_data(void* data, size_t len, size_t downloaded, size_t total); + void close() const + { + if(m_fd > 0) + { + ::close(m_fd); + } + } + [[nodiscard]] + const std::string &path() const { return m_path; } + + [[nodiscard]] + const std::string& url() const { return m_url; } + + [[nodiscard]] + bool redraw_only() const { return m_redraw_on_ready; } + }; + + class web_page : public container_cairo_pango, + public std::enable_shared_from_this<web_page> + { + litehtml::string m_url; + litehtml::string m_base_url; + litehtml::document::ptr m_html; + std::recursive_mutex m_html_mutex; + litehtml::string m_cursor; + litehtml::string m_clicked_url; + std::string m_fragment; + html_host_interface* m_html_host; + cairo_images_cache m_images; + litebrowser::http_requests_pool m_requests_pool; + std::string m_html_source; + + std::shared_ptr<browser_notify_interface> m_notify; + + public: + explicit web_page(html_host_interface* html_host, std::shared_ptr<browser_notify_interface> notify, int pool_size) : + m_html_host(html_host), + m_requests_pool(pool_size, [this] { on_pool_update_state(); }), + m_notify(std::move(notify)) + {} + + [[nodiscard]] + uint64_t id() const { return (uint64_t)this; } + + [[nodiscard]] + const std::string& get_html_source() const { return m_html_source; } + + void open(const litehtml::string& url, const litehtml::string& fragment); + + void get_viewport(litehtml::position& viewport) const override; + void on_anchor_click(const char* url, const litehtml::element::ptr& el) override; + void set_cursor(const char* cursor) override; + void import_css(litehtml::string& text, const litehtml::string& url, litehtml::string& baseurl) override; + void set_caption(const char* caption) override; + void set_base_url(const char* base_url) override; + cairo_surface_t* get_image(const std::string& url) override; + void make_url( const char* url, const char* basepath, litehtml::string& out ) override; + void load_image(const char* src, const char* baseurl, bool redraw_on_ready) override; + void on_mouse_event(const litehtml::element::ptr& el, litehtml::mouse_event event) override; + double get_screen_dpi() const override; + int get_screen_width() const override; + int get_screen_height() const override; + + void show_fragment(const litehtml::string& fragment); + void show_fragment_and_reset() + { + if(!m_fragment.empty() && m_html) + { + show_fragment(m_fragment); + m_fragment = ""; + } + } + + void on_mouse_over(int x, int y, int client_x, int client_y); + void on_lbutton_down(int x, int y, int client_x, int client_y); + void on_lbutton_up(int x, int y, int client_x, int client_y); + + [[nodiscard]] + const std::string& get_cursor() const { return m_cursor; } + + void draw(litehtml::uint_ptr hdc, int x, int y, const litehtml::position* clip) + { + std::lock_guard<std::recursive_mutex> html_lock(m_html_mutex); + if(m_html) m_html->draw(hdc, x, y, clip); + } + + int render(int max_width) + { + std::lock_guard<std::recursive_mutex> html_lock(m_html_mutex); + return m_html ? m_html->render(max_width) : 0; + } + + [[nodiscard]] + const std::string& url() const { return m_url; } + + [[nodiscard]] + int width() const { return m_html ? m_html->width() : 0; } + + [[nodiscard]] + int height() const { return m_html ? m_html->height() : 0; } + + bool media_changed() + { + std::lock_guard<std::recursive_mutex> html_lock(m_html_mutex); + return m_html && m_html->media_changed(); + } + + litehtml::position::vector get_fixed_boxes() + { + litehtml::position::vector ret; + std::lock_guard<std::recursive_mutex> html_lock(m_html_mutex); + if(m_html) { m_html->get_fixed_boxes(ret); } + return ret; + } + + void stop_loading() + { + m_requests_pool.stop(); + } + + [[nodiscard]] + bool is_downloading() + { + return m_requests_pool.is_downloading(); + } + + void dump(litehtml::dumper& cout) + { + std::lock_guard<std::recursive_mutex> html_lock(m_html_mutex); + if(m_html) + { + m_html->dump(cout); + } + } + private: + void http_request(const std::string& url, + const std::function<void(void* data, size_t len, size_t downloaded, size_t total)>& cb_on_data, + const std::function<void(u_int32_t http_status, u_int32_t err_code, const std::string& err_text, const std::string& url)>& cb_on_finish); + void on_page_downloaded(std::shared_ptr<text_file> data, u_int32_t http_status, u_int32_t err_code, const std::string& err_text, const std::string& url); + void on_image_downloaded(std::shared_ptr<image_file> data, u_int32_t http_status, u_int32_t err_code, const std::string& err_text, const std::string& url); + void on_pool_update_state(); + }; +} + +#endif //LITEBROWSER_WEB_PAGE_H diff --git a/test/codepoint_test.cpp b/test/codepoint_test.cpp deleted file mode 100644 index e92cf5fb1..000000000 --- a/test/codepoint_test.cpp +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright (C) 2020-2021 Primate Labs Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the names of the copyright holders nor the names of their -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include "litehtml/codepoint.h" - -#include <iostream> -#include <utility> -#include <vector> - -#include <gtest/gtest.h> - -using namespace litehtml; - -// It's impractical to test every single ASCII codepoint in the codepoint -// tests. Instead, the tests focus on a subset of the codepoints where -// problems are more likely to occur (e.g., near the start and end of ranges -// of similar codepoints). - -TEST(CodepointTest, URLReserved) -{ - std::vector<std::pair<char, bool>> testcases = { - { '!', true }, - { '"', false }, - { '#', true }, - { '$', true }, - { '%', false }, - { '&', true }, - { '\'', true }, - { '(', true }, - { ')', true }, - { '*', true }, - { ',', true }, - { '+', true }, - { ',', true }, - { '-', false }, - { '.', false }, - { '/', true }, - { '0', false }, - { '1', false }, - { '8', false }, - { '9', false }, - { ':', true }, - { ';', true }, - { '<', false }, - { '=', true }, - { '>', false }, - { 'A', false }, - { 'B', false }, - { 'G', false }, - { 'H', false }, - { 'Y', false }, - { 'Z', false }, - { '[', true }, - { '\\', false }, - { ']', true }, - { 'a', false }, - { 'b', false }, - { 'g', false }, - { 'h', false }, - { 'y', false }, - { 'z', false }, - { '{', false }, - { '|', false }, - { '}', false }, - }; - - for (auto testcase : testcases) { - EXPECT_EQ(testcase.second, is_url_reserved_codepoint(testcase.first)); - } -} - -TEST(CodepointTest, URLScheme) -{ - std::vector<std::pair<char, bool>> testcases = { - { '!', false }, - { '"', false }, - { '#', false }, - { '$', false }, - { '%', false }, - { '&', false }, - { '\'', false }, - { '(', false }, - { ')', false }, - { '*', false }, - { ',', false }, - { '+', true }, - { ',', false }, - { '-', true }, - { '.', true }, - { '/', false }, - { '0', true }, - { '1', true }, - { '8', true }, - { '9', true }, - { ':', false }, - { ';', false }, - { '<', false }, - { '=', false }, - { '>', false }, - { 'A', true }, - { 'G', true }, - { 'H', true }, - { 'Y', true }, - { 'Z', true }, - { '[', false }, - { '\\', false }, - { ']', false }, - { 'a', true }, - { 'b', true }, - { 'g', true }, - { 'h', true }, - { 'y', true }, - { 'z', true }, - { '{', false }, - { '|', false }, - { '}', false }, - }; - - for (auto testcase : testcases) { - EXPECT_EQ(testcase.second, is_url_scheme_codepoint(testcase.first)); - } -} diff --git a/test/cssTest.cpp b/test/cssTest.cpp deleted file mode 100644 index 145301054..000000000 --- a/test/cssTest.cpp +++ /dev/null @@ -1,164 +0,0 @@ -#include <gtest/gtest.h> - -#include <assert.h> -#include "litehtml.h" -using namespace litehtml; - -TEST(CSSTest, Url) { - string url; - - css::parse_css_url("", url); - EXPECT_TRUE(url.empty()); - - css::parse_css_url("value", url); - EXPECT_TRUE(url.empty()); - - css::parse_css_url("url()", url); - EXPECT_TRUE(url.empty()); - - css::parse_css_url("url(value)", url); - EXPECT_TRUE(!strcmp(url.c_str(), "value")); - - css::parse_css_url("url('value')", url); - EXPECT_TRUE(!strcmp(url.c_str(), "value")); - - css::parse_css_url("url(\"value\")", url); - EXPECT_TRUE(!strcmp(url.c_str(), "value")); -} - -TEST(CSSTest, LengthParse) { - css_length length; - - length.fromString("calc(todo)"); - assert(length.is_predefined() == true); - assert(length.predef() == 0); - assert(length.val() == 0); - assert(length.units() == css_units_none); - - length.fromString("top", "top;bottom", -1); - assert(length.is_predefined() == true); - assert(length.predef() == 0); - assert(length.val() == 0); - assert(length.units() == css_units_none); - - length.fromString("bottom", "top;bottom", -1); - assert(length.is_predefined() == true); - assert(length.predef() == 1); - assert(length.val() == 0); - assert(length.units() == css_units_none); - - length.fromString("bad", "top;bottom", -1); - assert(length.is_predefined() == true); - assert(length.predef() == -1); - assert(length.val() == 0); - assert(length.units() == css_units_none); - - length.fromString("123", "top;bottom", -1); - assert(length.is_predefined() == false); - assert(length.predef() == 0); - assert(length.val() == 123); - assert(length.units() == css_units_none); - - length.fromString("123px", "top;bottom", -1); - assert(length.is_predefined() == false); - assert(length.predef() == 0); - assert(length.val() == 123); - assert(length.units() == css_units_px); -} - -TEST(CSSTest, ElementSelectorParse) { - css_element_selector selector; - // https://www.w3schools.com/cssref/css_selectors.asp - selector.parse(".class"); - EXPECT_TRUE(selector.m_tag == star_id); - EXPECT_TRUE(selector.m_attrs.size() == 1); - EXPECT_TRUE(selector.m_attrs[0].type == select_class); - EXPECT_TRUE(selector.m_attrs[0].name == _id("class")); - - selector.parse(".class1.class2"); - EXPECT_TRUE(selector.m_tag == star_id); - EXPECT_TRUE(selector.m_attrs.size() == 2); - EXPECT_TRUE(selector.m_attrs[0].type == select_class); - EXPECT_TRUE(selector.m_attrs[0].name == _id("class1")); - EXPECT_TRUE(selector.m_attrs[1].type == select_class); - EXPECT_TRUE(selector.m_attrs[1].name == _id("class2")); - - selector.parse("#id"); - EXPECT_TRUE(selector.m_tag == star_id); - EXPECT_TRUE(selector.m_attrs.size() == 1); - EXPECT_TRUE(selector.m_attrs[0].type == select_id); - EXPECT_TRUE(selector.m_attrs[0].name == _id("id")); - - selector.parse("*"); - EXPECT_TRUE(selector.m_tag == star_id); - EXPECT_TRUE(selector.m_attrs.empty()); - - selector.parse("element"); - EXPECT_TRUE(selector.m_tag == _id("element")); - EXPECT_TRUE(selector.m_attrs.empty()); - - selector.parse("[attribute]"); - EXPECT_TRUE(selector.m_tag == star_id); - EXPECT_TRUE(selector.m_attrs.size() == 1); - EXPECT_TRUE(selector.m_attrs[0].type == select_exists); - EXPECT_TRUE(selector.m_attrs[0].name == _id("attribute")); - - selector.parse("[attribute=value]"); - EXPECT_TRUE(selector.m_tag == star_id); - EXPECT_TRUE(selector.m_attrs.size() == 1); - EXPECT_TRUE(selector.m_attrs[0].type == select_equal); - EXPECT_TRUE(selector.m_attrs[0].name == _id("attribute")); - EXPECT_TRUE(selector.m_attrs[0].val == "value"); - - selector.parse("[attribute~=value]"); - EXPECT_TRUE(selector.m_tag == star_id); - EXPECT_TRUE(selector.m_attrs.size() == 1); - EXPECT_TRUE(selector.m_attrs[0].type == select_contain_str); - EXPECT_TRUE(selector.m_attrs[0].name == _id("attribute")); - EXPECT_TRUE(selector.m_attrs[0].val == "value"); - - selector.parse("::after"); - EXPECT_TRUE(selector.m_tag == star_id); - EXPECT_TRUE(selector.m_attrs.size() == 1); - EXPECT_TRUE(selector.m_attrs[0].type == select_pseudo_element); - EXPECT_TRUE(selector.m_attrs[0].name == _after_); - - selector.parse(":after"); - EXPECT_TRUE(selector.m_tag == star_id); - EXPECT_TRUE(selector.m_attrs.size() == 1); - EXPECT_TRUE(selector.m_attrs[0].type == select_pseudo_element); - EXPECT_TRUE(selector.m_attrs[0].name == _after_); - - selector.parse(":active"); - EXPECT_TRUE(selector.m_tag == star_id); - EXPECT_TRUE(selector.m_attrs.size() == 1); - EXPECT_TRUE(selector.m_attrs[0].type == select_pseudo_class); - EXPECT_TRUE(selector.m_attrs[0].name == _active_); - - selector.parse(":lang(language)"); - EXPECT_TRUE(selector.m_tag == star_id); - EXPECT_TRUE(selector.m_attrs.size() == 1); - EXPECT_TRUE(selector.m_attrs[0].type == select_pseudo_class); - EXPECT_TRUE(selector.m_attrs[0].name == _lang_); - EXPECT_TRUE(selector.m_attrs[0].val == "language"); - - selector.parse(":not(div)"); - EXPECT_TRUE(selector.m_tag == star_id); - EXPECT_TRUE(selector.m_attrs.size() == 1); - EXPECT_TRUE(selector.m_attrs[0].type == select_pseudo_class); - EXPECT_TRUE(selector.m_attrs[0].name == _not_); - EXPECT_TRUE(selector.m_attrs[0].sel->m_tag == _div_); - - selector.parse(":nth-child(2n+3)"); - EXPECT_TRUE(selector.m_tag == star_id); - EXPECT_TRUE(selector.m_attrs.size() == 1); - EXPECT_TRUE(selector.m_attrs[0].type == select_pseudo_class); - EXPECT_TRUE(selector.m_attrs[0].name == _nth_child_); - EXPECT_TRUE(selector.m_attrs[0].a == 2); - EXPECT_TRUE(selector.m_attrs[0].b == 3); - - // other - selector.parse("tag:psudo#anchor"); - EXPECT_TRUE(selector.m_tag == _id("tag")); - EXPECT_TRUE(selector.m_attrs.size() == 2); -} diff --git a/test/dirent.h b/test/dirent.h deleted file mode 100644 index 497f22bce..000000000 --- a/test/dirent.h +++ /dev/null @@ -1,1235 +0,0 @@ -/* - * Dirent interface for Microsoft Visual Studio - * - * Copyright (C) 1998-2019 Toni Ronkko - * This file is part of dirent. Dirent may be freely distributed - * under the MIT license. For all details and documentation, see - * https://github.com/tronkko/dirent - */ -#ifndef DIRENT_H -#define DIRENT_H - -/* Hide warnings about unreferenced local functions */ -#if defined(__clang__) -# pragma clang diagnostic ignored "-Wunused-function" -#elif defined(_MSC_VER) -# pragma warning(disable:4505) -#elif defined(__GNUC__) -# pragma GCC diagnostic ignored "-Wunused-function" -#endif - -/* - * Include windows.h without Windows Sockets 1.1 to prevent conflicts with - * Windows Sockets 2.0. - */ -#ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -#endif -#include <windows.h> - -#include <stdio.h> -#include <stdarg.h> -#include <wchar.h> -#include <string.h> -#include <stdlib.h> -#include <malloc.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <errno.h> -#include <ctype.h> - -/* Indicates that d_type field is available in dirent structure */ -#define _DIRENT_HAVE_D_TYPE - -/* Indicates that d_namlen field is available in dirent structure */ -#define _DIRENT_HAVE_D_NAMLEN - -/* Entries missing from MSVC 6.0 */ -#if !defined(FILE_ATTRIBUTE_DEVICE) -# define FILE_ATTRIBUTE_DEVICE 0x40 -#endif - -/* File type and permission flags for stat(), general mask */ -#if !defined(S_IFMT) -# define S_IFMT _S_IFMT -#endif - -/* Directory bit */ -#if !defined(S_IFDIR) -# define S_IFDIR _S_IFDIR -#endif - -/* Character device bit */ -#if !defined(S_IFCHR) -# define S_IFCHR _S_IFCHR -#endif - -/* Pipe bit */ -#if !defined(S_IFFIFO) -# define S_IFFIFO _S_IFFIFO -#endif - -/* Regular file bit */ -#if !defined(S_IFREG) -# define S_IFREG _S_IFREG -#endif - -/* Read permission */ -#if !defined(S_IREAD) -# define S_IREAD _S_IREAD -#endif - -/* Write permission */ -#if !defined(S_IWRITE) -# define S_IWRITE _S_IWRITE -#endif - -/* Execute permission */ -#if !defined(S_IEXEC) -# define S_IEXEC _S_IEXEC -#endif - -/* Pipe */ -#if !defined(S_IFIFO) -# define S_IFIFO _S_IFIFO -#endif - -/* Block device */ -#if !defined(S_IFBLK) -# define S_IFBLK 0 -#endif - -/* Link */ -#if !defined(S_IFLNK) -# define S_IFLNK 0 -#endif - -/* Socket */ -#if !defined(S_IFSOCK) -# define S_IFSOCK 0 -#endif - -/* Read user permission */ -#if !defined(S_IRUSR) -# define S_IRUSR S_IREAD -#endif - -/* Write user permission */ -#if !defined(S_IWUSR) -# define S_IWUSR S_IWRITE -#endif - -/* Execute user permission */ -#if !defined(S_IXUSR) -# define S_IXUSR 0 -#endif - -/* Read group permission */ -#if !defined(S_IRGRP) -# define S_IRGRP 0 -#endif - -/* Write group permission */ -#if !defined(S_IWGRP) -# define S_IWGRP 0 -#endif - -/* Execute group permission */ -#if !defined(S_IXGRP) -# define S_IXGRP 0 -#endif - -/* Read others permission */ -#if !defined(S_IROTH) -# define S_IROTH 0 -#endif - -/* Write others permission */ -#if !defined(S_IWOTH) -# define S_IWOTH 0 -#endif - -/* Execute others permission */ -#if !defined(S_IXOTH) -# define S_IXOTH 0 -#endif - -/* Maximum length of file name */ -#if !defined(PATH_MAX) -# define PATH_MAX MAX_PATH -#endif -#if !defined(FILENAME_MAX) -# define FILENAME_MAX MAX_PATH -#endif -#if !defined(NAME_MAX) -# define NAME_MAX FILENAME_MAX -#endif - -/* File type flags for d_type */ -#define DT_UNKNOWN 0 -#define DT_REG S_IFREG -#define DT_DIR S_IFDIR -#define DT_FIFO S_IFIFO -#define DT_SOCK S_IFSOCK -#define DT_CHR S_IFCHR -#define DT_BLK S_IFBLK -#define DT_LNK S_IFLNK - -/* Macros for converting between st_mode and d_type */ -#define IFTODT(mode) ((mode) & S_IFMT) -#define DTTOIF(type) (type) - -/* - * File type macros. Note that block devices, sockets and links cannot be - * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are - * only defined for compatibility. These macros should always return false - * on Windows. - */ -#if !defined(S_ISFIFO) -# define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) -#endif -#if !defined(S_ISDIR) -# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) -#endif -#if !defined(S_ISREG) -# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) -#endif -#if !defined(S_ISLNK) -# define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) -#endif -#if !defined(S_ISSOCK) -# define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) -#endif -#if !defined(S_ISCHR) -# define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) -#endif -#if !defined(S_ISBLK) -# define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) -#endif - -/* Return the exact length of the file name without zero terminator */ -#define _D_EXACT_NAMLEN(p) ((p)->d_namlen) - -/* Return the maximum size of a file name */ -#define _D_ALLOC_NAMLEN(p) ((PATH_MAX)+1) - - -#ifdef __cplusplus -extern "C" { -#endif - - -/* Wide-character version */ -struct _wdirent { - /* Always zero */ - long d_ino; - - /* Position of next file in a directory stream */ - long d_off; - - /* Structure size */ - unsigned short d_reclen; - - /* Length of name without \0 */ - size_t d_namlen; - - /* File type */ - int d_type; - - /* File name */ - wchar_t d_name[PATH_MAX+1]; -}; -typedef struct _wdirent _wdirent; - -struct _WDIR { - /* Current directory entry */ - struct _wdirent ent; - - /* Private file data */ - WIN32_FIND_DATAW data; - - /* True if data is valid */ - int cached; - - /* True if next entry is invalid */ - int invalid; - - /* Win32 search handle */ - HANDLE handle; - - /* Initial directory name */ - wchar_t *patt; -}; -typedef struct _WDIR _WDIR; - -/* Multi-byte character version */ -struct dirent { - /* Always zero */ - long d_ino; - - /* Position of next file in a directory stream */ - long d_off; - - /* Structure size */ - unsigned short d_reclen; - - /* Length of name without \0 */ - size_t d_namlen; - - /* File type */ - int d_type; - - /* File name */ - char d_name[PATH_MAX+1]; -}; -typedef struct dirent dirent; - -struct DIR { - struct dirent ent; - struct _WDIR *wdirp; -}; -typedef struct DIR DIR; - - -/* Dirent functions */ -static DIR *opendir(const char *dirname); -static _WDIR *_wopendir(const wchar_t *dirname); - -static struct dirent *readdir(DIR *dirp); -static struct _wdirent *_wreaddir(_WDIR *dirp); - -static int readdir_r( - DIR *dirp, struct dirent *entry, struct dirent **result); -static int _wreaddir_r( - _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result); - -static int closedir(DIR *dirp); -static int _wclosedir(_WDIR *dirp); - -static void rewinddir(DIR *dirp); -static void _wrewinddir(_WDIR *dirp); - -static long telldir(DIR *dirp); -static long _wtelldir(_WDIR *dirp); - -static void seekdir(DIR *dirp, long loc); -static void _wseekdir(_WDIR *dirp, long loc); - -static int scandir(const char *dirname, struct dirent ***namelist, - int (*filter)(const struct dirent*), - int (*compare)(const struct dirent**, const struct dirent**)); - -static int alphasort(const struct dirent **a, const struct dirent **b); - -static int versionsort(const struct dirent **a, const struct dirent **b); - -static int strverscmp(const char *a, const char *b); - -/* For compatibility with Symbian */ -#define wdirent _wdirent -#define WDIR _WDIR -#define wopendir _wopendir -#define wreaddir _wreaddir -#define wclosedir _wclosedir -#define wrewinddir _wrewinddir -#define wtelldir _wtelldir -#define wseekdir _wseekdir - -/* Compatibility with older Microsoft compilers and non-Microsoft compilers */ -#if !defined(_MSC_VER) || _MSC_VER < 1400 -# define wcstombs_s dirent_wcstombs_s -# define mbstowcs_s dirent_mbstowcs_s -#endif - -/* Optimize dirent_set_errno() away on modern Microsoft compilers */ -#if defined(_MSC_VER) && _MSC_VER >= 1400 -# define dirent_set_errno _set_errno -#endif - - -/* Internal utility functions */ -static WIN32_FIND_DATAW *dirent_first(_WDIR *dirp); -static WIN32_FIND_DATAW *dirent_next(_WDIR *dirp); -static long dirent_hash(WIN32_FIND_DATAW *datap); - -#if !defined(_MSC_VER) || _MSC_VER < 1400 -static int dirent_mbstowcs_s( - size_t *pReturnValue, wchar_t *wcstr, size_t sizeInWords, - const char *mbstr, size_t count); -#endif - -#if !defined(_MSC_VER) || _MSC_VER < 1400 -static int dirent_wcstombs_s( - size_t *pReturnValue, char *mbstr, size_t sizeInBytes, - const wchar_t *wcstr, size_t count); -#endif - -#if !defined(_MSC_VER) || _MSC_VER < 1400 -static void dirent_set_errno(int error); -#endif - - -/* - * Open directory stream DIRNAME for read and return a pointer to the - * internal working area that is used to retrieve individual directory - * entries. - */ -static _WDIR * -_wopendir(const wchar_t *dirname) -{ - wchar_t *p; - - /* Must have directory name */ - if (dirname == NULL || dirname[0] == '\0') { - dirent_set_errno(ENOENT); - return NULL; - } - - /* Allocate new _WDIR structure */ - _WDIR *dirp = (_WDIR*) malloc(sizeof(struct _WDIR)); - if (!dirp) - return NULL; - - /* Reset _WDIR structure */ - dirp->handle = INVALID_HANDLE_VALUE; - dirp->patt = NULL; - dirp->cached = 0; - dirp->invalid = 0; - - /* - * Compute the length of full path plus zero terminator - * - * Note that on WinRT there's no way to convert relative paths - * into absolute paths, so just assume it is an absolute path. - */ -#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) - /* Desktop */ - DWORD n = GetFullPathNameW(dirname, 0, NULL, NULL); -#else - /* WinRT */ - size_t n = wcslen(dirname); -#endif - - /* Allocate room for absolute directory name and search pattern */ - dirp->patt = (wchar_t*) malloc(sizeof(wchar_t) * n + 16); - if (dirp->patt == NULL) - goto exit_closedir; - - /* - * Convert relative directory name to an absolute one. This - * allows rewinddir() to function correctly even when current - * working directory is changed between opendir() and rewinddir(). - * - * Note that on WinRT there's no way to convert relative paths - * into absolute paths, so just assume it is an absolute path. - */ -#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) - /* Desktop */ - n = GetFullPathNameW(dirname, n, dirp->patt, NULL); - if (n <= 0) - goto exit_closedir; -#else - /* WinRT */ - wcsncpy_s(dirp->patt, n+1, dirname, n); -#endif - - /* Append search pattern \* to the directory name */ - p = dirp->patt + n; - switch (p[-1]) { - case '\\': - case '/': - case ':': - /* Directory ends in path separator, e.g. c:\temp\ */ - /*NOP*/; - break; - - default: - /* Directory name doesn't end in path separator */ - *p++ = '\\'; - } - *p++ = '*'; - *p = '\0'; - - /* Open directory stream and retrieve the first entry */ - if (!dirent_first(dirp)) - goto exit_closedir; - - /* Success */ - return dirp; - - /* Failure */ -exit_closedir: - _wclosedir(dirp); - return NULL; -} - -/* - * Read next directory entry. - * - * Returns pointer to static directory entry which may be overwritten by - * subsequent calls to _wreaddir(). - */ -static struct _wdirent * -_wreaddir(_WDIR *dirp) -{ - /* - * Read directory entry to buffer. We can safely ignore the return - * value as entry will be set to NULL in case of error. - */ - struct _wdirent *entry; - (void) _wreaddir_r(dirp, &dirp->ent, &entry); - - /* Return pointer to statically allocated directory entry */ - return entry; -} - -/* - * Read next directory entry. - * - * Returns zero on success. If end of directory stream is reached, then sets - * result to NULL and returns zero. - */ -static int -_wreaddir_r( - _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result) -{ - /* Validate directory handle */ - if (!dirp || dirp->handle == INVALID_HANDLE_VALUE || !dirp->patt) { - dirent_set_errno(EBADF); - *result = NULL; - return -1; - } - - /* Read next directory entry */ - WIN32_FIND_DATAW *datap = dirent_next(dirp); - if (!datap) { - /* Return NULL to indicate end of directory */ - *result = NULL; - return /*OK*/0; - } - - /* - * Copy file name as wide-character string. If the file name is too - * long to fit in to the destination buffer, then truncate file name - * to PATH_MAX characters and zero-terminate the buffer. - */ - size_t i = 0; - while (i < PATH_MAX && datap->cFileName[i] != 0) { - entry->d_name[i] = datap->cFileName[i]; - i++; - } - entry->d_name[i] = 0; - - /* Length of file name excluding zero terminator */ - entry->d_namlen = i; - - /* Determine file type */ - DWORD attr = datap->dwFileAttributes; - if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) - entry->d_type = DT_CHR; - else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) - entry->d_type = DT_DIR; - else - entry->d_type = DT_REG; - - /* Read the next directory entry to cache */ - datap = dirent_next(dirp); - if (datap) { - /* Compute 31-bit hash of the next directory entry */ - entry->d_off = dirent_hash(datap); - - /* Push the next directory entry back to cache */ - dirp->cached = 1; - } else { - /* End of directory stream */ - entry->d_off = (long) ((~0UL) >> 1); - } - - /* Reset other fields */ - entry->d_ino = 0; - entry->d_reclen = sizeof(struct _wdirent); - - /* Set result address */ - *result = entry; - return /*OK*/0; -} - -/* - * Close directory stream opened by opendir() function. This invalidates the - * DIR structure as well as any directory entry read previously by - * _wreaddir(). - */ -static int -_wclosedir(_WDIR *dirp) -{ - if (!dirp) { - dirent_set_errno(EBADF); - return /*failure*/-1; - } - - /* - * Release search handle if we have one. Being able to handle - * partially initialized _WDIR structure allows us to use this - * function to handle errors occuring within _wopendir. - */ - if (dirp->handle != INVALID_HANDLE_VALUE) { - FindClose(dirp->handle); - } - - /* - * Release search pattern. Note that we don't need to care if - * dirp->patt is NULL or not: function free is guaranteed to act - * appropriately. - */ - free(dirp->patt); - - /* Release directory structure */ - free(dirp); - return /*success*/0; -} - -/* - * Rewind directory stream such that _wreaddir() returns the very first - * file name again. - */ -static void _wrewinddir(_WDIR* dirp) -{ - /* Check directory pointer */ - if (!dirp || dirp->handle == INVALID_HANDLE_VALUE || !dirp->patt) - return; - - /* Release existing search handle */ - FindClose(dirp->handle); - - /* Open new search handle */ - dirent_first(dirp); -} - -/* Get first directory entry */ -static WIN32_FIND_DATAW * -dirent_first(_WDIR *dirp) -{ - /* Open directory and retrieve the first entry */ - dirp->handle = FindFirstFileExW( - dirp->patt, FindExInfoStandard, &dirp->data, - FindExSearchNameMatch, NULL, 0); - if (dirp->handle == INVALID_HANDLE_VALUE) - goto error; - - /* A directory entry is now waiting in memory */ - dirp->cached = 1; - return &dirp->data; - -error: - /* Failed to open directory: no directory entry in memory */ - dirp->cached = 0; - dirp->invalid = 1; - - /* Set error code */ - DWORD errorcode = GetLastError(); - switch (errorcode) { - case ERROR_ACCESS_DENIED: - /* No read access to directory */ - dirent_set_errno(EACCES); - break; - - case ERROR_DIRECTORY: - /* Directory name is invalid */ - dirent_set_errno(ENOTDIR); - break; - - case ERROR_PATH_NOT_FOUND: - default: - /* Cannot find the file */ - dirent_set_errno(ENOENT); - } - return NULL; -} - -/* Get next directory entry */ -static WIN32_FIND_DATAW * -dirent_next(_WDIR *dirp) -{ - /* Return NULL if seek position was invalid */ - if (dirp->invalid) - return NULL; - - /* Is the next directory entry already in cache? */ - if (dirp->cached) { - /* Yes, a valid directory entry found in memory */ - dirp->cached = 0; - return &dirp->data; - } - - /* Read the next directory entry from stream */ - if (FindNextFileW(dirp->handle, &dirp->data) == FALSE) { - /* End of directory stream */ - return NULL; - } - - /* Success */ - return &dirp->data; -} - -/* - * Compute 31-bit hash of file name. - * - * See djb2 at http://www.cse.yorku.ca/~oz/hash.html - */ -static long -dirent_hash(WIN32_FIND_DATAW *datap) -{ - unsigned long hash = 5381; - unsigned long c; - const wchar_t *p = datap->cFileName; - const wchar_t *e = p + MAX_PATH; - while (p != e && (c = *p++) != 0) { - hash = (hash << 5) + hash + c; - } - - return (long) (hash & ((~0UL) >> 1)); -} - -/* Open directory stream using plain old C-string */ -static DIR *opendir(const char *dirname) -{ - /* Must have directory name */ - if (dirname == NULL || dirname[0] == '\0') { - dirent_set_errno(ENOENT); - return NULL; - } - - /* Allocate memory for DIR structure */ - struct DIR *dirp = (DIR*) malloc(sizeof(struct DIR)); - if (!dirp) - return NULL; - - /* Convert directory name to wide-character string */ - wchar_t wname[PATH_MAX + 1]; - size_t n; - int error = mbstowcs_s(&n, wname, PATH_MAX + 1, dirname, PATH_MAX+1); - if (error) - goto exit_failure; - - /* Open directory stream using wide-character name */ - dirp->wdirp = _wopendir(wname); - if (!dirp->wdirp) - goto exit_failure; - - /* Success */ - return dirp; - - /* Failure */ -exit_failure: - free(dirp); - return NULL; -} - -/* Read next directory entry */ -static struct dirent * -readdir(DIR *dirp) -{ - /* - * Read directory entry to buffer. We can safely ignore the return - * value as entry will be set to NULL in case of error. - */ - struct dirent *entry; - (void) readdir_r(dirp, &dirp->ent, &entry); - - /* Return pointer to statically allocated directory entry */ - return entry; -} - -/* - * Read next directory entry into called-allocated buffer. - * - * Returns zero on success. If the end of directory stream is reached, then - * sets result to NULL and returns zero. - */ -static int -readdir_r( - DIR *dirp, struct dirent *entry, struct dirent **result) -{ - /* Read next directory entry */ - WIN32_FIND_DATAW *datap = dirent_next(dirp->wdirp); - if (!datap) { - /* No more directory entries */ - *result = NULL; - return /*OK*/0; - } - - /* Attempt to convert file name to multi-byte string */ - size_t n; - int error = wcstombs_s( - &n, entry->d_name, PATH_MAX + 1, - datap->cFileName, PATH_MAX + 1); - - /* - * If the file name cannot be represented by a multi-byte string, then - * attempt to use old 8+3 file name. This allows the program to - * access files although file names may seem unfamiliar to the user. - * - * Be ware that the code below cannot come up with a short file name - * unless the file system provides one. At least VirtualBox shared - * folders fail to do this. - */ - if (error && datap->cAlternateFileName[0] != '\0') { - error = wcstombs_s( - &n, entry->d_name, PATH_MAX + 1, - datap->cAlternateFileName, PATH_MAX + 1); - } - - if (!error) { - /* Length of file name excluding zero terminator */ - entry->d_namlen = n - 1; - - /* Determine file type */ - DWORD attr = datap->dwFileAttributes; - if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) - entry->d_type = DT_CHR; - else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) - entry->d_type = DT_DIR; - else - entry->d_type = DT_REG; - - /* Get offset of next file */ - datap = dirent_next(dirp->wdirp); - if (datap) { - /* Compute 31-bit hash of the next directory entry */ - entry->d_off = dirent_hash(datap); - - /* Push the next directory entry back to cache */ - dirp->wdirp->cached = 1; - } else { - /* End of directory stream */ - entry->d_off = (long) ((~0UL) >> 1); - } - - /* Reset fields */ - entry->d_ino = 0; - entry->d_reclen = sizeof(struct dirent); - } else { - /* - * Cannot convert file name to multi-byte string so construct - * an erroneous directory entry and return that. Note that - * we cannot return NULL as that would stop the processing - * of directory entries completely. - */ - entry->d_name[0] = '?'; - entry->d_name[1] = '\0'; - entry->d_namlen = 1; - entry->d_type = DT_UNKNOWN; - entry->d_ino = 0; - entry->d_off = -1; - entry->d_reclen = 0; - } - - /* Return pointer to directory entry */ - *result = entry; - return /*OK*/0; -} - -/* Close directory stream */ -static int -closedir(DIR *dirp) -{ - int ok; - - if (!dirp) - goto exit_failure; - - /* Close wide-character directory stream */ - ok = _wclosedir(dirp->wdirp); - dirp->wdirp = NULL; - - /* Release multi-byte character version */ - free(dirp); - return ok; - -exit_failure: - /* Invalid directory stream */ - dirent_set_errno(EBADF); - return /*failure*/-1; -} - -/* Rewind directory stream to beginning */ -static void -rewinddir(DIR *dirp) -{ - if (!dirp) - return; - - /* Rewind wide-character string directory stream */ - _wrewinddir(dirp->wdirp); -} - -/* Get position of directory stream */ -static long -_wtelldir(_WDIR *dirp) -{ - if (!dirp || dirp->handle == INVALID_HANDLE_VALUE) { - dirent_set_errno(EBADF); - return /*failure*/-1; - } - - /* Read next file entry */ - WIN32_FIND_DATAW *datap = dirent_next(dirp); - if (!datap) { - /* End of directory stream */ - return (long) ((~0UL) >> 1); - } - - /* Store file entry to cache for readdir() */ - dirp->cached = 1; - - /* Return the 31-bit hash code to be used as stream position */ - return dirent_hash(datap); -} - -/* Get position of directory stream */ -static long -telldir(DIR *dirp) -{ - if (!dirp) { - dirent_set_errno(EBADF); - return -1; - } - - return _wtelldir(dirp->wdirp); -} - -/* Seek directory stream to offset */ -static void -_wseekdir(_WDIR *dirp, long loc) -{ - /* Directory must be open */ - if (!dirp || dirp->handle == INVALID_HANDLE_VALUE) - goto exit_failure; - - /* Ensure that seek position is valid */ - if (loc < 0) - goto exit_failure; - - /* Restart directory stream from the beginning */ - FindClose(dirp->handle); - if (!dirent_first(dirp)) - goto exit_failure; - - /* Reset invalid flag so that we can read from the stream again */ - dirp->invalid = 0; - - /* - * Read directory entries from the beginning until the hash matches a - * file name. Be ware that hash code is only 31 bits longs and - * duplicates are possible: the hash code cannot return the position - * with 100.00% accuracy! Moreover, the method is slow for large - * directories. - */ - long hash; - do { - /* Read next directory entry */ - WIN32_FIND_DATAW *datap = dirent_next(dirp); - if (!datap) { - /* - * End of directory stream was reached before finding - * the requested location. Perhaps the file in - * question was deleted or moved out of the directory. - */ - goto exit_failure; - } - - /* Does the file name match the hash? */ - hash = dirent_hash(datap); - } while (hash != loc); - - /* - * File name matches the hash! Push the directory entry back to cache - * from where next readdir() will return it. - */ - dirp->cached = 1; - dirp->invalid = 0; - return; - -exit_failure: - /* Ensure that readdir will return NULL */ - dirp->invalid = 1; -} - -/* Seek directory stream to offset */ -static void -seekdir(DIR *dirp, long loc) -{ - if (!dirp) - return; - - _wseekdir(dirp->wdirp, loc); -} - -/* Scan directory for entries */ -static int -scandir( - const char *dirname, struct dirent ***namelist, - int (*filter)(const struct dirent*), - int (*compare)(const struct dirent**, const struct dirent**)) -{ - int result; - - /* Open directory stream */ - DIR *dir = opendir(dirname); - if (!dir) { - /* Cannot open directory */ - return /*Error*/ -1; - } - - /* Read directory entries to memory */ - struct dirent *tmp = NULL; - struct dirent **files = NULL; - size_t size = 0; - size_t allocated = 0; - while (1) { - /* Allocate room for a temporary directory entry */ - if (!tmp) { - tmp = (struct dirent*) malloc(sizeof(struct dirent)); - if (!tmp) - goto exit_failure; - } - - /* Read directory entry to temporary area */ - struct dirent *entry; - if (readdir_r(dir, tmp, &entry) != /*OK*/0) - goto exit_failure; - - /* Stop if we already read the last directory entry */ - if (entry == NULL) - goto exit_success; - - /* Determine whether to include the entry in results */ - if (filter && !filter(tmp)) - continue; - - /* Enlarge pointer table to make room for another pointer */ - if (size >= allocated) { - /* Compute number of entries in the new table */ - size_t num_entries = size * 2 + 16; - - /* Allocate new pointer table or enlarge existing */ - void *p = realloc(files, sizeof(void*) * num_entries); - if (!p) - goto exit_failure; - - /* Got the memory */ - files = (dirent**) p; - allocated = num_entries; - } - - /* Store the temporary entry to ptr table */ - files[size++] = tmp; - tmp = NULL; - } - -exit_failure: - /* Release allocated entries */ - for (size_t i = 0; i < size; i++) { - free(files[i]); - } - - /* Release the pointer table */ - free(files); - files = NULL; - - /* Exit with error code */ - result = /*error*/ -1; - goto exit_status; - -exit_success: - /* Sort directory entries */ - qsort(files, size, sizeof(void*), - (int (*) (const void*, const void*)) compare); - - /* Pass pointer table to caller */ - if (namelist) - *namelist = files; - - /* Return the number of directory entries read */ - result = (int) size; - -exit_status: - /* Release temporary directory entry, if we had one */ - free(tmp); - - /* Close directory stream */ - closedir(dir); - return result; -} - -/* Alphabetical sorting */ -static int -alphasort(const struct dirent **a, const struct dirent **b) -{ - return strcoll((*a)->d_name, (*b)->d_name); -} - -/* Sort versions */ -static int -versionsort(const struct dirent **a, const struct dirent **b) -{ - return strverscmp((*a)->d_name, (*b)->d_name); -} - -/* Compare strings */ -static int -strverscmp(const char *a, const char *b) -{ - size_t i = 0; - size_t j; - - /* Find first difference */ - while (a[i] == b[i]) { - if (a[i] == '\0') { - /* No difference */ - return 0; - } - ++i; - } - - /* Count backwards and find the leftmost digit */ - j = i; - while (j > 0 && isdigit(a[j-1])) { - --j; - } - - /* Determine mode of comparison */ - if (a[j] == '0' || b[j] == '0') { - /* Find the next non-zero digit */ - while (a[j] == '0' && a[j] == b[j]) { - j++; - } - - /* String with more digits is smaller, e.g 002 < 01 */ - if (isdigit(a[j])) { - if (!isdigit(b[j])) { - return -1; - } - } else if (isdigit(b[j])) { - return 1; - } - } else if (isdigit(a[j]) && isdigit(b[j])) { - /* Numeric comparison */ - size_t k1 = j; - size_t k2 = j; - - /* Compute number of digits in each string */ - while (isdigit(a[k1])) { - k1++; - } - while (isdigit(b[k2])) { - k2++; - } - - /* Number with more digits is bigger, e.g 999 < 1000 */ - if (k1 < k2) - return -1; - else if (k1 > k2) - return 1; - } - - /* Alphabetical comparison */ - return (int) ((unsigned char) a[i]) - ((unsigned char) b[i]); -} - -/* Convert multi-byte string to wide character string */ -#if !defined(_MSC_VER) || _MSC_VER < 1400 -static int -dirent_mbstowcs_s( - size_t *pReturnValue, wchar_t *wcstr, - size_t sizeInWords, const char *mbstr, size_t count) -{ - /* Older Visual Studio or non-Microsoft compiler */ - size_t n = mbstowcs(wcstr, mbstr, sizeInWords); - if (wcstr && n >= count) - return /*error*/ 1; - - /* Zero-terminate output buffer */ - if (wcstr && sizeInWords) { - if (n >= sizeInWords) - n = sizeInWords - 1; - wcstr[n] = 0; - } - - /* Length of multi-byte string with zero terminator */ - if (pReturnValue) { - *pReturnValue = n + 1; - } - - /* Success */ - return 0; -} -#endif - -/* Convert wide-character string to multi-byte string */ -#if !defined(_MSC_VER) || _MSC_VER < 1400 -static int -dirent_wcstombs_s( - size_t *pReturnValue, char *mbstr, - size_t sizeInBytes, const wchar_t *wcstr, size_t count) -{ - /* Older Visual Studio or non-Microsoft compiler */ - size_t n = wcstombs(mbstr, wcstr, sizeInBytes); - if (mbstr && n >= count) - return /*error*/1; - - /* Zero-terminate output buffer */ - if (mbstr && sizeInBytes) { - if (n >= sizeInBytes) { - n = sizeInBytes - 1; - } - mbstr[n] = '\0'; - } - - /* Length of resulting multi-bytes string WITH zero-terminator */ - if (pReturnValue) { - *pReturnValue = n + 1; - } - - /* Success */ - return 0; -} -#endif - -/* Set errno variable */ -#if !defined(_MSC_VER) || _MSC_VER < 1400 -static void -dirent_set_errno(int error) -{ - /* Non-Microsoft compiler or older Microsoft compiler */ - errno = error; -} -#endif - -#ifdef __cplusplus -} -#endif -#endif /*DIRENT_H*/ -/* -The MIT License (MIT) - -Copyright (c) 1998-2019 Toni Ronkko - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ \ No newline at end of file diff --git a/test/mediaQueryTest.cpp b/test/mediaQueryTest.cpp deleted file mode 100644 index 3e41094a4..000000000 --- a/test/mediaQueryTest.cpp +++ /dev/null @@ -1,242 +0,0 @@ -#include <gtest/gtest.h> - -#include "litehtml.h" - -using namespace litehtml; - -TEST(MediaQueryTest, Check) { - media_query_expression e; - media_features k; - - e = media_query_expression(), e.feature = media_feature_width, e.val = 100; - k = media_features(), k.width = 0; - EXPECT_TRUE(!e.check(k)); - k = media_features(), k.width = 100; - EXPECT_TRUE(e.check(k)); - k = media_features(), k.width = 500; - EXPECT_TRUE(!e.check(k)); - e = media_query_expression(), e.feature = media_feature_min_width, e.val = 100; - k = media_features(), k.width = 0; - EXPECT_TRUE(!e.check(k)); - k = media_features(), k.width = 100; - EXPECT_TRUE(e.check(k)); - k = media_features(), k.width = 500; - EXPECT_TRUE(e.check(k)); - e = media_query_expression(), e.feature = media_feature_max_width, e.val = 100; - k = media_features(), k.width = 0; - EXPECT_TRUE(e.check(k)); - k = media_features(), k.width = 100; - EXPECT_TRUE(e.check(k)); - k = media_features(), k.width = 500; - EXPECT_TRUE(!e.check(k)); - e = media_query_expression(), e.feature = media_feature_height, e.val = 100; - k = media_features(), k.height = 0; - EXPECT_TRUE(!e.check(k)); - k = media_features(), k.height = 100; - EXPECT_TRUE(e.check(k)); - k = media_features(), k.height = 500; - EXPECT_TRUE(!e.check(k)); - e = media_query_expression(), e.feature = media_feature_min_height, e.val = 100; - k = media_features(), k.height = 0; - EXPECT_TRUE(!e.check(k)); - k = media_features(), k.height = 100; - EXPECT_TRUE(e.check(k)); - k = media_features(), k.height = 500; - EXPECT_TRUE(e.check(k)); - e = media_query_expression(), e.feature = media_feature_max_height, e.val = 100; - k = media_features(), k.height = 0; - EXPECT_TRUE(e.check(k)); - k = media_features(), k.height = 100; - EXPECT_TRUE(e.check(k)); - k = media_features(), k.height = 500; - EXPECT_TRUE(!e.check(k)); - - e = media_query_expression(), e.feature = media_feature_device_width, e.val = 100; - k = media_features(), k.device_width = 0; - EXPECT_TRUE(!e.check(k)); - k = media_features(), k.device_width = 100; - EXPECT_TRUE(e.check(k)); - k = media_features(), k.device_width = 500; - EXPECT_TRUE(!e.check(k)); - e = media_query_expression(), e.feature = media_feature_min_device_width, e.val = 100; - k = media_features(), k.device_width = 0; - EXPECT_TRUE(!e.check(k)); - k = media_features(), k.device_width = 100; - EXPECT_TRUE(e.check(k)); - k = media_features(), k.device_width = 500; - EXPECT_TRUE(e.check(k)); - e = media_query_expression(), e.feature = media_feature_max_device_width, e.val = 100; - k = media_features(), k.device_width = 0; - EXPECT_TRUE(e.check(k)); - k = media_features(), k.device_width = 100; - EXPECT_TRUE(e.check(k)); - k = media_features(), k.device_width = 500; - EXPECT_TRUE(!e.check(k)); - e = media_query_expression(), e.feature = media_feature_device_height, e.val = 100; - k = media_features(), k.device_height = 0; - EXPECT_TRUE(!e.check(k)); - k = media_features(), k.device_height = 100; - EXPECT_TRUE(e.check(k)); - k = media_features(), k.device_height = 500; - EXPECT_TRUE(!e.check(k)); - e = media_query_expression(), e.feature = media_feature_min_device_height, e.val = 100; - k = media_features(), k.device_height = 0; - EXPECT_TRUE(!e.check(k)); - k = media_features(), k.device_height = 100; - EXPECT_TRUE(e.check(k)); - k = media_features(), k.device_height = 500; - EXPECT_TRUE(e.check(k)); - e = media_query_expression(), e.feature = media_feature_max_device_height, e.val = 100; - k = media_features(), k.device_height = 0; - EXPECT_TRUE(e.check(k)); - k = media_features(), k.device_height = 100; - EXPECT_TRUE(e.check(k)); - k = media_features(), k.device_height = 500; - EXPECT_TRUE(!e.check(k)); - - e = media_query_expression(), e.feature = media_feature_orientation, e.val = (int)media_orientation_portrait; - k = media_features(), k.width = 0, k.height = 100; - EXPECT_TRUE(e.check(k)); - k = media_features(), k.width = 100, k.height = 100; - EXPECT_TRUE(e.check(k)); - k = media_features(), k.width = 500, k.height = 100; - EXPECT_TRUE(!e.check(k)); - e = media_query_expression(), e.feature = media_feature_orientation, e.val = (int)media_orientation_landscape; - k = media_features(), k.width = 0, k.height = 100; - EXPECT_TRUE(!e.check(k)); - k = media_features(), k.width = 100, k.height = 100; - EXPECT_TRUE(!e.check(k)); - k = media_features(), k.width = 500, k.height = 100; - EXPECT_TRUE(e.check(k)); - e = media_query_expression(), e.feature = media_feature_aspect_ratio, e.val = 100, e.val2 = 100; - k = media_features(), k.width = 0, k.height = 100; - EXPECT_TRUE(!e.check(k)); - k = media_features(), k.width = 100, k.height = 100; - EXPECT_TRUE(e.check(k)); - k = media_features(), k.width = 500, k.height = 100; - EXPECT_TRUE(!e.check(k)); - e = media_query_expression(), e.feature = media_feature_min_aspect_ratio, e.val = 100, e.val2 = 100; - k = media_features(), k.width = 0, k.height = 100; - EXPECT_TRUE(!e.check(k)); - k = media_features(), k.width = 100, k.height = 100; - EXPECT_TRUE(e.check(k)); - k = media_features(), k.width = 500, k.height = 100; - EXPECT_TRUE(e.check(k)); - e = media_query_expression(), e.feature = media_feature_max_aspect_ratio, e.val = 100, e.val2 = 100; - k = media_features(), k.width = 0, k.height = 100; - EXPECT_TRUE(e.check(k)); - k = media_features(), k.width = 100, k.height = 100; - EXPECT_TRUE(e.check(k)); - k = media_features(), k.width = 500, k.height = 100; - EXPECT_TRUE(!e.check(k)); - - e = media_query_expression(), e.feature = media_feature_device_aspect_ratio, e.val = 100, e.val2 = 100; - k = media_features(), k.device_width = 0, k.device_height = 100; - EXPECT_TRUE(!e.check(k)); - k = media_features(), k.device_width = 100, k.device_height = 100; - EXPECT_TRUE(e.check(k)); - k = media_features(), k.device_width = 500, k.device_height = 100; - EXPECT_TRUE(!e.check(k)); - e = media_query_expression(), e.feature = media_feature_min_device_aspect_ratio, e.val = 100, e.val2 = 100; - k = media_features(), k.device_width = 0, k.device_height = 100; - EXPECT_TRUE(!e.check(k)); - k = media_features(), k.device_width = 100, k.device_height = 100; - EXPECT_TRUE(e.check(k)); - k = media_features(), k.device_width = 500, k.device_height = 100; - EXPECT_TRUE(e.check(k)); - e = media_query_expression(), e.feature = media_feature_max_device_aspect_ratio, e.val = 100, e.val2 = 100; - k = media_features(), k.device_width = 0, k.device_height = 100; - EXPECT_TRUE(e.check(k)); - k = media_features(), k.device_width = 100, k.device_height = 100; - EXPECT_TRUE(e.check(k)); - k = media_features(), k.device_width = 500, k.device_height = 100; - EXPECT_TRUE(!e.check(k)); - - e = media_query_expression(), e.feature = media_feature_color, e.val = 100; - k = media_features(), k.color = 0; - EXPECT_TRUE(!e.check(k)); - k = media_features(), k.color = 100; - EXPECT_TRUE(e.check(k)); - k = media_features(), k.color = 500; - EXPECT_TRUE(!e.check(k)); - e = media_query_expression(), e.feature = media_feature_min_color, e.val = 100; - k = media_features(), k.color = 0; - EXPECT_TRUE(!e.check(k)); - k = media_features(), k.color = 100; - EXPECT_TRUE(e.check(k)); - k = media_features(), k.color = 500; - EXPECT_TRUE(e.check(k)); - e = media_query_expression(), e.feature = media_feature_max_color, e.val = 100; - k = media_features(), k.color = 0; - EXPECT_TRUE(e.check(k)); - k = media_features(), k.color = 100; - EXPECT_TRUE(e.check(k)); - k = media_features(), k.color = 500; - EXPECT_TRUE(!e.check(k)); - - e = media_query_expression(), e.feature = media_feature_color_index, e.val = 100; - k = media_features(), k.color_index = 0; - EXPECT_TRUE(!e.check(k)); - k = media_features(), k.color_index = 100; - EXPECT_TRUE(e.check(k)); - k = media_features(), k.color_index = 500; - EXPECT_TRUE(!e.check(k)); - e = media_query_expression(), e.feature = media_feature_min_color_index, e.val = 100; - k = media_features(), k.color_index = 0; - EXPECT_TRUE(!e.check(k)); - k = media_features(), k.color_index = 100; - EXPECT_TRUE(e.check(k)); - k = media_features(), k.color_index = 500; - EXPECT_TRUE(e.check(k)); - e = media_query_expression(), e.feature = media_feature_max_color_index, e.val = 100; - k = media_features(), k.color_index = 0; - EXPECT_TRUE(e.check(k)); - k = media_features(), k.color_index = 100; - EXPECT_TRUE(e.check(k)); - k = media_features(), k.color_index = 500; - EXPECT_TRUE(!e.check(k)); - - e = media_query_expression(), e.feature = media_feature_monochrome, e.val = 100; - k = media_features(), k.monochrome = 0; - EXPECT_TRUE(!e.check(k)); - k = media_features(), k.monochrome = 100; - EXPECT_TRUE(e.check(k)); - k = media_features(), k.monochrome = 500; - EXPECT_TRUE(!e.check(k)); - e = media_query_expression(), e.feature = media_feature_min_monochrome, e.val = 100; - k = media_features(), k.monochrome = 0; - EXPECT_TRUE(!e.check(k)); - k = media_features(), k.monochrome = 100; - EXPECT_TRUE(e.check(k)); - k = media_features(), k.monochrome = 500; - EXPECT_TRUE(e.check(k)); - e = media_query_expression(), e.feature = media_feature_max_monochrome, e.val = 100; - k = media_features(), k.monochrome = 0; - EXPECT_TRUE(e.check(k)); - k = media_features(), k.monochrome = 100; - EXPECT_TRUE(e.check(k)); - k = media_features(), k.monochrome = 500; - EXPECT_TRUE(!e.check(k)); - - e = media_query_expression(), e.feature = media_feature_resolution, e.val = 100; - k = media_features(), k.resolution = 0; - EXPECT_TRUE(!e.check(k)); - k = media_features(), k.resolution = 100; - EXPECT_TRUE(e.check(k)); - k = media_features(), k.resolution = 500; - EXPECT_TRUE(!e.check(k)); - e = media_query_expression(), e.feature = media_feature_min_resolution, e.val = 100; - k = media_features(), k.resolution = 0; - EXPECT_TRUE(!e.check(k)); - k = media_features(), k.resolution = 100; - EXPECT_TRUE(e.check(k)); - k = media_features(), k.resolution = 500; - EXPECT_TRUE(e.check(k)); - e = media_query_expression(), e.feature = media_feature_max_resolution, e.val = 100; - k = media_features(), k.resolution = 0; - EXPECT_TRUE(e.check(k)); - k = media_features(), k.resolution = 100; - EXPECT_TRUE(e.check(k)); - k = media_features(), k.resolution = 500; - EXPECT_TRUE(!e.check(k)); -} diff --git a/test/render/-table-caption.htm b/test/render/-table-caption.htm deleted file mode 100755 index 42609aba0..000000000 --- a/test/render/-table-caption.htm +++ /dev/null @@ -1,36 +0,0 @@ -Table -<style> -th, td { - border: 3px solid black; - border-collapse: collapse; - background-color: red; -} -caption { - background-color: yellow; -} -th, td { - padding: 5px; - text-align: left; -} -table { - display: inline-table; -} -</style> -<table> - <caption style="text-transform: uppercase ">Month savings</caption> - <tr> - <th>Month</th> - <th>Savings</th> - </tr> - <tr> - <td>January</td> - <td>$100</td> - </tr> - <caption style="background-color: blue;">MonthlysavingsMonthly savings!!! Monthly savings!!!</caption> - <tr> - <td>February</td> - <td>$50</td> - </tr> -</table> - -just a table \ No newline at end of file diff --git a/test/render/acid1.htm b/test/render/acid1.htm deleted file mode 100644 index 039a45aa9..000000000 --- a/test/render/acid1.htm +++ /dev/null @@ -1,177 +0,0 @@ -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html> -<head> - <title> - display/box/float/clear test - - - - -
-
- toggle -
-
-
    -
  • - the way -
  • -
  • -

    - the world ends -

    -
    -

    - bang - -

    -

    - whimper - -

    -
    -
  • -
  • - i grow old -
  • -
  • - pluot? -
  • -
-
-
- bar maids, -
-
-

- sing to me, erbarme dich -

-
-
-

- This is a nonsensical document, but syntactically valid HTML 4.0. All 100%-conformant CSS1 agents should be able to render the document elements above this paragraph indistinguishably (to the pixel) from this - reference rendering, - (except font rasterization and form widgets). All discrepancies should be traceable to CSS1 implementation shortcomings. Once you have finished evaluating this test, you can return to the parent page. -

- - diff --git a/test/render/acid1.htm.png b/test/render/acid1.htm.png deleted file mode 100644 index a6e8717a5..000000000 Binary files a/test/render/acid1.htm.png and /dev/null differ diff --git a/test/render/counter.htm b/test/render/counter.htm deleted file mode 100644 index bd24d157c..000000000 --- a/test/render/counter.htm +++ /dev/null @@ -1,79 +0,0 @@ - - - - - css counters - - - - -

HTML/CSS Tutorials

-

HTML

-

CSS

-

Bootstrap

-

W3.CSS

- -

Scripting Tutorials

-

JavaScript

-

jQuery

-

React

- -

Programming Tutorials

-

Python

-

Java

-

C++

-
    -
  1. item
  2. -
  3. item -
      -
    1. item
    2. -
    3. item
    4. -
    5. item -
        -
      1. item
      2. -
      3. item
      4. -
      5. item
      6. -
      -
    6. -
    7. item
    8. -
    -
  4. -
  5. item
  6. -
  7. item
  8. -
- -
    -
  1. item
  2. -
  3. item
  4. -
- - - diff --git a/test/render/counter.htm.png b/test/render/counter.htm.png deleted file mode 100644 index ae151ac44..000000000 Binary files a/test/render/counter.htm.png and /dev/null differ diff --git a/test/render/css-1-line-height.htm b/test/render/css-1-line-height.htm deleted file mode 100644 index 06d8773b6..000000000 --- a/test/render/css-1-line-height.htm +++ /dev/null @@ -1,19 +0,0 @@ - - - - -
normal
normal
-
1
1
-
100%
100%
-
1em
1em
diff --git a/test/render/css-1-line-height.htm.png b/test/render/css-1-line-height.htm.png deleted file mode 100644 index b750a1505..000000000 Binary files a/test/render/css-1-line-height.htm.png and /dev/null differ diff --git a/test/render/css-2-invalid-color.htm b/test/render/css-2-invalid-color.htm deleted file mode 100644 index df396aafd..000000000 --- a/test/render/css-2-invalid-color.htm +++ /dev/null @@ -1,11 +0,0 @@ - - - - -

text \ No newline at end of file diff --git a/test/render/css-2-invalid-color.htm.png b/test/render/css-2-invalid-color.htm.png deleted file mode 100644 index 69dfe83c1..000000000 Binary files a/test/render/css-2-invalid-color.htm.png and /dev/null differ diff --git a/test/render/flex/--flex-minimum-height-flex-items-003.htm b/test/render/flex/--flex-minimum-height-flex-items-003.htm deleted file mode 100644 index dfa7cb563..000000000 --- a/test/render/flex/--flex-minimum-height-flex-items-003.htm +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - CSS Flexible Box Test: Minimum height of flex items - - - - - - - - - -

Test passes if there is a filled green square and no red.

- -
-
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/--flexbox-abspos-child-001a.htm b/test/render/flex/--flexbox-abspos-child-001a.htm deleted file mode 100644 index 44d48db97..000000000 --- a/test/render/flex/--flexbox-abspos-child-001a.htm +++ /dev/null @@ -1,57 +0,0 @@ - - - - - CSS Test: Testing that "min-width", "max-width", "min-height", and "max-height" are applied on absolutely positioned children of a horizontal flex container - - - - - - - -
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/--flexbox-abspos-child-001b.htm b/test/render/flex/--flexbox-abspos-child-001b.htm deleted file mode 100644 index f216dc2e2..000000000 --- a/test/render/flex/--flexbox-abspos-child-001b.htm +++ /dev/null @@ -1,58 +0,0 @@ - - - - - CSS Test: Testing that "min-width", "max-width", "min-height", and "max-height" are applied on absolutely positioned children of a vertical flex container - - - - - - - -
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/--flexbox-abspos-child-002.htm b/test/render/flex/--flexbox-abspos-child-002.htm deleted file mode 100644 index e35c21d58..000000000 --- a/test/render/flex/--flexbox-abspos-child-002.htm +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - CSS Test: Test that "flex-basis" doesn't affect layout of abspos flex child - - - - - - - - - - -
-
-
-
- - -
-
-
-
- - -
-
-
-
- - - - - - \ No newline at end of file diff --git a/test/render/flex/--flexbox-baseline-single-item-001a.htm b/test/render/flex/--flexbox-baseline-single-item-001a.htm deleted file mode 100644 index 6bc2925fc..000000000 --- a/test/render/flex/--flexbox-baseline-single-item-001a.htm +++ /dev/null @@ -1,55 +0,0 @@ - - - - - CSS Test: Testing the baseline of a horizontal flex container with one flex item - - - - - - - - A -
a
-
a
-
a
-
a
-
a
-
- -
abs
-
a
- - -
- - \ No newline at end of file diff --git a/test/render/flex/--flexbox-baseline-single-item-001b.htm b/test/render/flex/--flexbox-baseline-single-item-001b.htm deleted file mode 100644 index 2944a5830..000000000 --- a/test/render/flex/--flexbox-baseline-single-item-001b.htm +++ /dev/null @@ -1,56 +0,0 @@ - - - - - CSS Test: Testing the baseline of a vertical flex container with one flex item - - - - - - - - A -
a
-
a
-
a
-
a
-
a
-
- -
abs
-
a
- - -
- - \ No newline at end of file diff --git a/test/render/flex/--flexbox-flex-basis-content-002a.htm b/test/render/flex/--flexbox-flex-basis-content-002a.htm deleted file mode 100644 index 10306aadf..000000000 --- a/test/render/flex/--flexbox-flex-basis-content-002a.htm +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - CSS Test: Testing "flex-basis: content" in a column-oriented flex container - - - - - - - - - - -
-
a b
-
c
-
-
- -
- - -
-
a b
-
c
-
-
- -
- - -
-
a b
-
c
-
-
- -
- - - - - - diff --git a/test/render/flex/--flexbox-flex-basis-content-002b.htm b/test/render/flex/--flexbox-flex-basis-content-002b.htm deleted file mode 100644 index 4790d20b8..000000000 --- a/test/render/flex/--flexbox-flex-basis-content-002b.htm +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - CSS Test: Testing "flex-basis: content" (set via the "flex" shorthand) - in a column-oriented flex container. - - - - - - - - - - -
-
a b
-
c
-
-
- -
- - -
-
a b
-
c
-
-
- -
- - -
-
a b
-
c
-
-
- -
- - - - - - diff --git a/test/render/flex/--flexbox-flex-basis-content-003a.htm b/test/render/flex/--flexbox-flex-basis-content-003a.htm deleted file mode 100644 index b0e48f201..000000000 --- a/test/render/flex/--flexbox-flex-basis-content-003a.htm +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - CSS Test: Testing that explicit "flex-basis: content" is treated as - "max-content" when calculating flex base size - - - - - - - - - - - -
-
-
- - -
-
- - - -
-
- - -
-
- -
-
- - -
- -
- - -
-
- - - -
-
- - -
-
- - - -
-
- - - - - - \ No newline at end of file diff --git a/test/render/flex/--flexbox-flex-basis-content-003b.htm b/test/render/flex/--flexbox-flex-basis-content-003b.htm deleted file mode 100644 index ad66c82fd..000000000 --- a/test/render/flex/--flexbox-flex-basis-content-003b.htm +++ /dev/null @@ -1,125 +0,0 @@ - - - - - - CSS Test: Testing that used "flex-basis: content" is treated as - "max-content" when calculating flex base size - - - - - - - - - - - -
-
-
- - -
-
- - - -
-
- - -
-
- -
-
- - -
- -
- - -
-
- - - -
-
- - -
-
- - - -
-
- - - - - - \ No newline at end of file diff --git a/test/render/flex/--flexbox-flex-basis-content-004a.htm b/test/render/flex/--flexbox-flex-basis-content-004a.htm deleted file mode 100644 index f7a36d399..000000000 --- a/test/render/flex/--flexbox-flex-basis-content-004a.htm +++ /dev/null @@ -1,130 +0,0 @@ - - - - - - CSS Test: Testing that explicit "flex-basis: content" is treated as - "max-content" when calculating flex base size - - - - - - - - - - - -
-
-
- - -
-
- - - -
-
- - -
-
- -
-
- - -
- -
- - -
-
- - - -
-
- - -
-
- - - -
-
- - - - - - \ No newline at end of file diff --git a/test/render/flex/--flexbox-gap-position-absolute.htm b/test/render/flex/--flexbox-gap-position-absolute.htm deleted file mode 100644 index 5cae70703..000000000 --- a/test/render/flex/--flexbox-gap-position-absolute.htm +++ /dev/null @@ -1,30 +0,0 @@ - - - - - CSS Flexible Box Layout Test: Test flexbox intrinsic inline-size, gap, and absolute-positioned children - - - - - - - - - -
- - - B - C -
- - - - \ No newline at end of file diff --git a/test/render/flex/--flexbox-items-as-stacking-contexts-001.htm b/test/render/flex/--flexbox-items-as-stacking-contexts-001.htm deleted file mode 100644 index b25d73211..000000000 --- a/test/render/flex/--flexbox-items-as-stacking-contexts-001.htm +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - CSS Test: Testing that 'z-index' property makes flex items form stacking contexts - - - - - - - -
-
-
-
- - -
-
-
- -
-
- -
-
-
- - -
-
-
- -
-
- -
-
-
- - -
-
-
- -
-
- -
-
-
- - -
-
-
- -
-
- -
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/--flexbox-mbp-horiz-004.htm b/test/render/flex/--flexbox-mbp-horiz-004.htm deleted file mode 100644 index c85ff5912..000000000 --- a/test/render/flex/--flexbox-mbp-horiz-004.htm +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - CSS Test: Testing percent-valued padding and margin on flex items - - - - - - - -
- - -
- - -
- - -
- - - - \ No newline at end of file diff --git a/test/render/flex/--flexbox-min-height-auto-001.htm b/test/render/flex/--flexbox-min-height-auto-001.htm deleted file mode 100644 index a1d5d1e06..000000000 --- a/test/render/flex/--flexbox-min-height-auto-001.htm +++ /dev/null @@ -1,105 +0,0 @@ - - - - - - CSS Test: Testing min-height:auto - - - - - - - - - - -
-
-
- -
-
-
- -
-
-
- - - -
-
-
- -
-
-
- -
-
-
- - - -
-
-
- -
-
-
- -
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/--flexbox-min-height-auto-003.htm b/test/render/flex/--flexbox-min-height-auto-003.htm deleted file mode 100644 index fe03ffffa..000000000 --- a/test/render/flex/--flexbox-min-height-auto-003.htm +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - CSS Test: Testing min-height:auto & 'overflow' interaction - - - - - - - - -
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/--flexbox-min-height-auto-004.htm b/test/render/flex/--flexbox-min-height-auto-004.htm deleted file mode 100644 index 231da3717..000000000 --- a/test/render/flex/--flexbox-min-height-auto-004.htm +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - CSS Test: Testing min-height:auto & 'overflow' interaction - - - - - - - - -
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/--flexbox-min-width-auto-001.htm b/test/render/flex/--flexbox-min-width-auto-001.htm deleted file mode 100644 index b4024df3c..000000000 --- a/test/render/flex/--flexbox-min-width-auto-001.htm +++ /dev/null @@ -1,103 +0,0 @@ - - - - - - CSS Test: Testing min-width:auto - - - - - - - - - - -
-
-
- -
-
-
- -
-
-
- - - -
-
-
- -
-
-
- -
-
-
- - - -
-
-
- -
-
-
- -
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/--flexbox-min-width-auto-003.htm b/test/render/flex/--flexbox-min-width-auto-003.htm deleted file mode 100644 index d7cf60f84..000000000 --- a/test/render/flex/--flexbox-min-width-auto-003.htm +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - CSS Test: Testing min-width:auto & 'overflow' interaction - - - - - - - - -
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/--flexbox-min-width-auto-004.htm b/test/render/flex/--flexbox-min-width-auto-004.htm deleted file mode 100644 index 6fd481b1e..000000000 --- a/test/render/flex/--flexbox-min-width-auto-004.htm +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - CSS Test: Testing min-width:auto & 'overflow' interaction - - - - - - - - -
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/--flexbox-paint-ordering-001.htm b/test/render/flex/--flexbox-paint-ordering-001.htm deleted file mode 100644 index 9657a9fa3..000000000 --- a/test/render/flex/--flexbox-paint-ordering-001.htm +++ /dev/null @@ -1,92 +0,0 @@ - - - - - - CSS Test: Testing the paint-order of overlapping flex items, with varying tweaks on the container - - - - - - - -
-
-
-
- - -
-
-
-
- - -
-
-
-
- -
- - -
-
-
-
- - -
-
-
-
- -
- - -
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/--flexbox-paint-ordering-002.htm b/test/render/flex/--flexbox-paint-ordering-002.htm deleted file mode 100644 index a8ea21a2f..000000000 --- a/test/render/flex/--flexbox-paint-ordering-002.htm +++ /dev/null @@ -1,164 +0,0 @@ - - - - - - CSS Test: Testing the paint-order of overlapping flex items with 'order' and 'z-index' set - - - - - - - -
-
-
-
- -
- - -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
- - -
-
-
-
-
-
-
-
-
-
-
-
-
- -
- - -
-
-
-
-
-
-
-
-
-
-
-
-
- -
- - -
- -
-
-
- -
-
-
- -
-
-
- -
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/--flexbox-single-line-clamp-3.htm b/test/render/flex/--flexbox-single-line-clamp-3.htm deleted file mode 100644 index 064864cb9..000000000 --- a/test/render/flex/--flexbox-single-line-clamp-3.htm +++ /dev/null @@ -1,42 +0,0 @@ - - - - -CSS Test: Single-line flex containers should clamp their line's height to the container's computed min and max cross-size. - - - - - -
-
-
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/--flexbox-sizing-horiz-001.htm b/test/render/flex/--flexbox-sizing-horiz-001.htm deleted file mode 100644 index 665656bad..000000000 --- a/test/render/flex/--flexbox-sizing-horiz-001.htm +++ /dev/null @@ -1,86 +0,0 @@ - - - - - - CSS Test: Testing sizing of an auto-sized horizontal flex container with min-width and max-width constraints - - - - - - - -
-
-
- - -
-
-
- - -
-
-
- - -
-
-
- - -
-
-
- - -
-
-
- - -
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/--flexbox-sizing-horiz-002.htm b/test/render/flex/--flexbox-sizing-horiz-002.htm deleted file mode 100644 index 6f6f869b2..000000000 --- a/test/render/flex/--flexbox-sizing-horiz-002.htm +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - CSS Test: Testing sizing of an auto-sized horizontal flex container with min-height and max-height constraints - - - - - - - -
-
text
-
- - -
-
text
-
- - -
-
text
-
- - -
-
text
-
- - -
-
text
-
- - -
-
text
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/--flexbox-sizing-vert-001.htm b/test/render/flex/--flexbox-sizing-vert-001.htm deleted file mode 100644 index e0a6542bc..000000000 --- a/test/render/flex/--flexbox-sizing-vert-001.htm +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - CSS Test: Testing sizing of an auto-sized vertical flex container with min-height and max-height constraints - - - - - - - -
-
-
- - -
-
-
- - -
-
-
- - -
-
-
- - -
-
-
- - -
-
-
- - -
-
-
- - -
-
-
- - -
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/--flexbox-sizing-vert-002.htm b/test/render/flex/--flexbox-sizing-vert-002.htm deleted file mode 100644 index 64a451a64..000000000 --- a/test/render/flex/--flexbox-sizing-vert-002.htm +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - CSS Test: Testing sizing of an auto-sized vertical flex container with min-width and max-width constraints - - - - - - - -
-
AB
-
- - -
-
AB
-
- - -
-
AB
-
- - -
-
AB
-
- - -
-
AB
-
- - -
-
AB
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/--flexbox-with-pseudo-elements-003.htm b/test/render/flex/--flexbox-with-pseudo-elements-003.htm deleted file mode 100644 index 5939f15af..000000000 --- a/test/render/flex/--flexbox-with-pseudo-elements-003.htm +++ /dev/null @@ -1,69 +0,0 @@ - - - - - CSS Test: Testing that generated content nodes with table-part display types are wrapped with an anonymous table, which forms a flex item - - - - - - - -
- x -
y
- z -
-
- x -
y
- z -
-
- x -
y
- z -
- - - - - \ No newline at end of file diff --git a/test/render/flex/--flexbox_fbfc.htm b/test/render/flex/--flexbox_fbfc.htm deleted file mode 100644 index 3168b6f9c..000000000 --- a/test/render/flex/--flexbox_fbfc.htm +++ /dev/null @@ -1,35 +0,0 @@ - - - -flexbox | flex formatting context :: float intrusion - - - - - -
filler
- -
-
Yellow box should be below the blue box
-
- - - \ No newline at end of file diff --git a/test/render/flex/--flexbox_fbfc2.htm b/test/render/flex/--flexbox_fbfc2.htm deleted file mode 100644 index 389e29ae9..000000000 --- a/test/render/flex/--flexbox_fbfc2.htm +++ /dev/null @@ -1,33 +0,0 @@ - - - -flexbox | flex formatting context :: float intrusion - - - - - -
float
- -
-
Yellow box yellow box yellow box
-
- - - \ No newline at end of file diff --git a/test/render/flex/--flexbox_flex-formatting-interop.htm b/test/render/flex/--flexbox_flex-formatting-interop.htm deleted file mode 100644 index 3fdfbd8fd..000000000 --- a/test/render/flex/--flexbox_flex-formatting-interop.htm +++ /dev/null @@ -1,41 +0,0 @@ - - - -flexbox | flex formatting context :: negative margins and - border box - - - - - -
float
- -
-
xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx
-
- - - \ No newline at end of file diff --git a/test/render/flex/--flexbox_flex-natural-mixed-basis.htm b/test/render/flex/--flexbox_flex-natural-mixed-basis.htm deleted file mode 100644 index cc902e13d..000000000 --- a/test/render/flex/--flexbox_flex-natural-mixed-basis.htm +++ /dev/null @@ -1,39 +0,0 @@ - - - -flexbox | flex: larger integer, mixed basis - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/--flexbox_flex-natural-variable-zero-basis.htm b/test/render/flex/--flexbox_flex-natural-variable-zero-basis.htm deleted file mode 100644 index df74f95b0..000000000 --- a/test/render/flex/--flexbox_flex-natural-variable-zero-basis.htm +++ /dev/null @@ -1,39 +0,0 @@ - - - -flexbox | flex: larger integer, zero basis - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/--flexbox_flex-none-wrappable-content.htm b/test/render/flex/--flexbox_flex-none-wrappable-content.htm deleted file mode 100644 index 057a770f9..000000000 --- a/test/render/flex/--flexbox_flex-none-wrappable-content.htm +++ /dev/null @@ -1,26 +0,0 @@ - - - -Specifying flex:none on wrappable content should give content its full width - - - - - - - -
-
- XXX XXX XXX -
-
- -
You should see three green rectangles above, all on the same line.
- - - diff --git a/test/render/flex/--flexbox_inline-abspos.htm b/test/render/flex/--flexbox_inline-abspos.htm deleted file mode 100644 index abfa9179f..000000000 --- a/test/render/flex/--flexbox_inline-abspos.htm +++ /dev/null @@ -1,20 +0,0 @@ - - - -flexbox | absolutely positioned inline - - - - - -
FAIL
- - - \ No newline at end of file diff --git a/test/render/flex/--flexbox_inline-float.htm b/test/render/flex/--flexbox_inline-float.htm deleted file mode 100644 index 2631c110f..000000000 --- a/test/render/flex/--flexbox_inline-float.htm +++ /dev/null @@ -1,20 +0,0 @@ - - - -flexbox | floated inline - - - - - -
FAIL
- - - \ No newline at end of file diff --git a/test/render/flex/--flexbox_inline.htm b/test/render/flex/--flexbox_inline.htm deleted file mode 100644 index 2fad137c2..000000000 --- a/test/render/flex/--flexbox_inline.htm +++ /dev/null @@ -1,20 +0,0 @@ - - - -flexbox | inline - - - - - -
HELLOWORLD
- - - \ No newline at end of file diff --git a/test/render/flex/--flexbox_order-abspos-space-around.htm b/test/render/flex/--flexbox_order-abspos-space-around.htm deleted file mode 100644 index 883cd24c5..000000000 --- a/test/render/flex/--flexbox_order-abspos-space-around.htm +++ /dev/null @@ -1,39 +0,0 @@ - - - -flexbox | order; justify-content: space-around - - - - - -
- filler - - filler - filler -
- - - \ No newline at end of file diff --git a/test/render/flex/--flexbox_stf-table-singleline-2.htm b/test/render/flex/--flexbox_stf-table-singleline-2.htm deleted file mode 100644 index fc8065b2e..000000000 --- a/test/render/flex/--flexbox_stf-table-singleline-2.htm +++ /dev/null @@ -1,41 +0,0 @@ - - - -flexbox | singleline flexcontainer versus stf :: table - - - - - - -
-
-

filler

-

filler

-

filler

-

filler

-

filler

-
-
- - - diff --git a/test/render/flex/--flexbox_table-fixed-layout.htm b/test/render/flex/--flexbox_table-fixed-layout.htm deleted file mode 100644 index e369f7349..000000000 --- a/test/render/flex/--flexbox_table-fixed-layout.htm +++ /dev/null @@ -1,58 +0,0 @@ - - - -flexbox | flexcontainers in tables - - - - - - - - - - -
-
-

filler

-

filler

-

filler

-

filler

-

filler

-
-
-
-

filler

-

filler

-

filler

-

filler

-

filler

-
-
- - - \ No newline at end of file diff --git a/test/render/flex/--flexbox_width-overflow.htm b/test/render/flex/--flexbox_width-overflow.htm deleted file mode 100644 index 94175b786..000000000 --- a/test/render/flex/--flexbox_width-overflow.htm +++ /dev/null @@ -1,30 +0,0 @@ - - - -flexbox | overflow - - - - - -
-

one two three four

-

filler

-

filler

-

filler

-

filler

-
- - - \ No newline at end of file diff --git a/test/render/flex/--multiline-shrink-to-fit.htm b/test/render/flex/--multiline-shrink-to-fit.htm deleted file mode 100644 index f14c44a91..000000000 --- a/test/render/flex/--multiline-shrink-to-fit.htm +++ /dev/null @@ -1,77 +0,0 @@ - - - -CSS Flexbox: multiline column flexboxes and shrink-to-fit. - - - - - - -
-
-
-
-
-
-

The grey background should be 100px wide.

- -
-
-
-
-
-
-

The grey background should be 100px wide.

- -
-
-
-
-
-
-
-
-

The grey background should be 100px wide.

- -
-
-
-
-
-
-
-
-

The grey background should be 100px wide and 5px should -stick out the bottom.

- - - - - - \ No newline at end of file diff --git a/test/render/flex/--percentage-heights-008.htm b/test/render/flex/--percentage-heights-008.htm deleted file mode 100644 index 50ca6ebb2..000000000 --- a/test/render/flex/--percentage-heights-008.htm +++ /dev/null @@ -1,39 +0,0 @@ - - - -Fixed indefinite heights - - - - - - - - -

Test passes if there is a filled green square and no red.

- -
-
-
-
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/--percentage-heights-010.htm b/test/render/flex/--percentage-heights-010.htm deleted file mode 100644 index 2edd86989..000000000 --- a/test/render/flex/--percentage-heights-010.htm +++ /dev/null @@ -1,18 +0,0 @@ - - - -A height: 100% descendant should trigger a relayout when stretching. - - - -

Test passes if there is a filled green square and no red.

-
-
-
-
-
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/-align-content-001.htm b/test/render/flex/-align-content-001.htm deleted file mode 100644 index aa73086e7..000000000 --- a/test/render/flex/-align-content-001.htm +++ /dev/null @@ -1,42 +0,0 @@ - - - - - CSS Test: A multi-line flex container with the 'align-content' property set to 'center' - - - - - - - - - -

Test passes if there is no red visible on the page.

-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/-align-content-002.htm b/test/render/flex/-align-content-002.htm deleted file mode 100644 index f3775be9c..000000000 --- a/test/render/flex/-align-content-002.htm +++ /dev/null @@ -1,41 +0,0 @@ - - - - - CSS Test: A multi-line flex container with the 'align-content' property set to 'flex-start' - - - - - - - - -

Test passes if there is no red visible on the page.

-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/-align-content-003.htm b/test/render/flex/-align-content-003.htm deleted file mode 100644 index c18c287e6..000000000 --- a/test/render/flex/-align-content-003.htm +++ /dev/null @@ -1,41 +0,0 @@ - - - - - CSS Test: A multi-line flex container with the 'align-content' property set to 'flex-end' - - - - - - - - -

Test passes if there is no red visible on the page.

-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/-align-content-004.htm b/test/render/flex/-align-content-004.htm deleted file mode 100644 index 3aef07d90..000000000 --- a/test/render/flex/-align-content-004.htm +++ /dev/null @@ -1,43 +0,0 @@ - - - - - CSS Test: A multi-line flex container with the 'align-content' property set to 'space-between' - - - - - - - - -

Test passes if there is no red visible on the page.

-
-
-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/-align-content-005.htm b/test/render/flex/-align-content-005.htm deleted file mode 100644 index dffc1adb9..000000000 --- a/test/render/flex/-align-content-005.htm +++ /dev/null @@ -1,41 +0,0 @@ - - - - - CSS Test: A multi-line flex container with the 'align-content' property set to 'space-around' - - - - - - - - -

Test passes if there is no red visible on the page.

-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/-align-items-001.htm b/test/render/flex/-align-items-001.htm deleted file mode 100644 index 6847c0170..000000000 --- a/test/render/flex/-align-items-001.htm +++ /dev/null @@ -1,39 +0,0 @@ - - - - - CSS Test: A flex container with the 'align-items' property set to 'center' - - - - - - - - - -

Test passes if there is no red visible on the page.

-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/-align-items-002.htm b/test/render/flex/-align-items-002.htm deleted file mode 100644 index 202703771..000000000 --- a/test/render/flex/-align-items-002.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - - - CSS Test: A flex container with the 'align-items' property set to 'flex-start' - - - - - - - - - -

Test passes if there is no red visible on the page.

-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/-align-items-003.htm b/test/render/flex/-align-items-003.htm deleted file mode 100644 index 40f3807e2..000000000 --- a/test/render/flex/-align-items-003.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - - - CSS Test: A flex container with the 'align-items' property set to 'flex-end' - - - - - - - - - -

Test passes if there is no red visible on the page.

-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/-align-items-004.htm b/test/render/flex/-align-items-004.htm deleted file mode 100644 index a5e90f0ef..000000000 --- a/test/render/flex/-align-items-004.htm +++ /dev/null @@ -1,53 +0,0 @@ - - - - - CSS Test: A flex container with the 'align-items' property set to 'baseline' - - - - - - - - - - -

Test passes if there is no red visible on the page.

-
-
d1
-
d2
-
d3
-
d4
-
d5
-
d6
-
d7
-
d8
-
- - - - - diff --git a/test/render/flex/-contain-layout-baseline-002.htm b/test/render/flex/-contain-layout-baseline-002.htm deleted file mode 100644 index b4774a2f5..000000000 --- a/test/render/flex/-contain-layout-baseline-002.htm +++ /dev/null @@ -1,39 +0,0 @@ - - - - -CSS Containment Test: Layout containment supress baseline in flex items - - - - - - -

Test passes if there is a filled green square and no red.

-
- -
item
-
- - - \ No newline at end of file diff --git a/test/render/flex/-contain-layout-suppress-baseline-002.htm b/test/render/flex/-contain-layout-suppress-baseline-002.htm deleted file mode 100644 index 7e3b59746..000000000 --- a/test/render/flex/-contain-layout-suppress-baseline-002.htm +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - CSS Test: 'contain: layout' should make elements behave as if they have no baseline - - - - - - - -
- - - - - - -
- - -
- - - -
- -
- - - -
- -
- - - -
- -
- - - -
- - - - - \ No newline at end of file diff --git a/test/render/flex/-content-height-with-scrollbars.htm b/test/render/flex/-content-height-with-scrollbars.htm deleted file mode 100644 index 505d54a31..000000000 --- a/test/render/flex/-content-height-with-scrollbars.htm +++ /dev/null @@ -1,47 +0,0 @@ - -Ensure flexbox content-size excludes scrollbar. - - - - - - - -

This tests that when setting the height of a flex item to a percentage -height, we use the content height with scrollbars. The content should not be -scrollable in any of the test cases below.

- -
-
-
- -
-
-
- -
-
-
-
- -
-
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/-cross-axis-scrollbar.htm b/test/render/flex/-cross-axis-scrollbar.htm deleted file mode 100644 index 1c3ed5c86..000000000 --- a/test/render/flex/-cross-axis-scrollbar.htm +++ /dev/null @@ -1,147 +0,0 @@ - - - -CSS Flexbox: Scrollbars and flex-direction. - - - - - - - - -This test passes if no red is showing. - -
- -
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
- -
- - - - - - - diff --git a/test/render/flex/-css-box-justify-content.htm b/test/render/flex/-css-box-justify-content.htm deleted file mode 100644 index 5f0a96e61..000000000 --- a/test/render/flex/-css-box-justify-content.htm +++ /dev/null @@ -1,34 +0,0 @@ - -flexbox |css-box-justify-content - - - - - - -

This test passes if the DIV5's position in the end and the div is Horizontal layout

-
-
DIV1
-   -
DIV2
-   -
DIV3
-   -
DIV4
-   -
DIV5
-
- \ No newline at end of file diff --git a/test/render/flex/-css-flexbox-row-reverse-wrap-reverse.htm b/test/render/flex/-css-flexbox-row-reverse-wrap-reverse.htm deleted file mode 100644 index ad22c18f0..000000000 --- a/test/render/flex/-css-flexbox-row-reverse-wrap-reverse.htm +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - CSS Flexbox Test: flex direction: row, writing mode vertical - - - - - - - - - - - - - -

Pass condition: 4 rectangles, with colors in clockwise order starting from top-left: grey, orange, blue, yellow. - -

-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/-css-flexbox-row-reverse-wrap.htm b/test/render/flex/-css-flexbox-row-reverse-wrap.htm deleted file mode 100644 index b486bc427..000000000 --- a/test/render/flex/-css-flexbox-row-reverse-wrap.htm +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - CSS Flexbox Test: flex direction: row, writing mode vertical - - - - - - - - - - - - - -

Pass condition: 4 rectangles, with colors in clockwise order starting from top-left: grey, orange, blue, yellow. - -

-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/-css-flexbox-row-reverse.htm b/test/render/flex/-css-flexbox-row-reverse.htm deleted file mode 100644 index 9eb276549..000000000 --- a/test/render/flex/-css-flexbox-row-reverse.htm +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - CSS Flexbox Test: flex direction: row, writing mode vertical - - - - - - - - - - - - - -

Pass condition: 4 rectangles, with colors in clockwise order starting from top-left: grey, orange, blue, yellow. -

-
- -
- -
-
- -
- -
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/-css-flexbox-row-wrap-reverse.htm b/test/render/flex/-css-flexbox-row-wrap-reverse.htm deleted file mode 100644 index e0e2529ec..000000000 --- a/test/render/flex/-css-flexbox-row-wrap-reverse.htm +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - CSS Flexbox Test: flex direction: row, writing mode vertical - - - - - - - - - - - - - -

Pass condition: 4 rectangles, with colors in clockwise order starting from top-left: grey, orange, blue, yellow. -

-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/-css-flexbox-row-wrap.htm b/test/render/flex/-css-flexbox-row-wrap.htm deleted file mode 100644 index f2c5a5d04..000000000 --- a/test/render/flex/-css-flexbox-row-wrap.htm +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - CSS Flexbox Test: flex direction: row, writing mode vertical - - - - - - - - - - - - - -

Pass condition: 4 rectangles, with colors in clockwise order starting from top-left: grey, orange, blue, yellow. - -

-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/-css-flexbox-row.htm b/test/render/flex/-css-flexbox-row.htm deleted file mode 100644 index 31903c847..000000000 --- a/test/render/flex/-css-flexbox-row.htm +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - CSS Flexbox Test: flex direction: row, writing mode vertical - - - - - - - - - - - - - -

Pass condition: 4 rectangles, with colors in clockwise order starting from top-left: grey, orange, blue, yellow. -

-
- -
- -
-
- -
- -
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/-css-flexbox-test1.htm b/test/render/flex/-css-flexbox-test1.htm deleted file mode 100644 index e5d205776..000000000 --- a/test/render/flex/-css-flexbox-test1.htm +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - CSS Flexbox Test: flex direction: row, writing mode vertical - - - - - - - - - - - - -

The test passes if you see a tall green box with pairs of the digits 1-9 listed top to bottom in two columns.

- -
-
-
123
123
-
456
456
-
789
789
-
- - - - - - \ No newline at end of file diff --git a/test/render/flex/-direction-upright-002.htm b/test/render/flex/-direction-upright-002.htm deleted file mode 100644 index 2b5a232aa..000000000 --- a/test/render/flex/-direction-upright-002.htm +++ /dev/null @@ -1,142 +0,0 @@ - - - -'text-orientation: upright' forces used 'direction' to LTR in vertical typographic modes - - - - - - - - -

Test passes if both rows of boxes are identical (coloring, order, orientation, and arrangement of contents). - - - -

- - - -
ABC -
-
A B
-
A B
-
A Bb CcDd Ee
-
-
- - - -
ABC -
-
A B
-
A B
-
A Bb CcDd Ee
-
- - - -
- - - -
ABC -
-
A B
-
A B
-
A Bb CcDd Ee
-
-
- - - -
ABC -
-
A B
-
A B
-
A Bb CcDd Ee
-
-
- - - -
ABC -
-
A B
-
A B
-
A Bb CcDd Ee
-
- -
- - - -
- - - -
ABC -
-
A B
-
A B
-
A Bb CcDd Ee
-
-
- - - -
ABC -
-
A B
-
A B
-
A Bb CcDd Ee
-
- -
- - - -
ABC -
-
A B
-
A B
-
A Bb CcDd Ee
-
-
- - - -
ABC -
-
A B
-
A B
-
A Bb CcDd Ee
-
-
- - - -
ABC -
-
A B
-
A B
-
A Bb CcDd Ee
-
- - \ No newline at end of file diff --git a/test/render/flex/-flex-002.htm b/test/render/flex/-flex-002.htm deleted file mode 100644 index 401e6cdf7..000000000 --- a/test/render/flex/-flex-002.htm +++ /dev/null @@ -1,53 +0,0 @@ - - - CSS Test: The 'flex' shorthand adjusting the 'flex-shrink' sub-property - - - - - - - - -

Test passes if there is a single blue rectangle on the left, a single orange rectangle directly to its right, and there is no red visible on the page.

-
-
-
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/-flex-003.htm b/test/render/flex/-flex-003.htm deleted file mode 100644 index 05f942644..000000000 --- a/test/render/flex/-flex-003.htm +++ /dev/null @@ -1,54 +0,0 @@ - - - CSS Test: Comparing two different elements using different values for the 'flex-grow' sub-property on the 'flex' shorthand - - - - - - - - -

Test passes if there is a single blue rectangle on the left, a single orange rectangle directly to its right, and there is no red visible on the page.

-
-
-
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/-flex-004.htm b/test/render/flex/-flex-004.htm deleted file mode 100644 index 61c597959..000000000 --- a/test/render/flex/-flex-004.htm +++ /dev/null @@ -1,54 +0,0 @@ - - - CSS Test: Comparing two different elements using different values for the 'flex-shrink' sub-property on the 'flex' shorthand - - - - - - - - -

Test passes if there is a single blue rectangle on the left, a single orange rectangle directly to its right, and there is no red visible on the page.

-
-
-
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/-flex-aspect-ratio-019.htm b/test/render/flex/-flex-aspect-ratio-019.htm deleted file mode 100644 index e18be574a..000000000 --- a/test/render/flex/-flex-aspect-ratio-019.htm +++ /dev/null @@ -1,18 +0,0 @@ - - - -CSS aspect-ratio: Row flexbox main size with flex-basis:content - - - - - -

Test passes if there is a filled green square and no red.

- -
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/-flex-aspect-ratio-020.htm b/test/render/flex/-flex-aspect-ratio-020.htm deleted file mode 100644 index 71022297e..000000000 --- a/test/render/flex/-flex-aspect-ratio-020.htm +++ /dev/null @@ -1,18 +0,0 @@ - - - -CSS aspect-ratio: Flex item main size with flex-basis:content in Column flex container - - - - - -

Test passes if there is a filled green square and no red.

- -
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/-flex-aspect-ratio-021.htm b/test/render/flex/-flex-aspect-ratio-021.htm deleted file mode 100644 index ba626ecd7..000000000 --- a/test/render/flex/-flex-aspect-ratio-021.htm +++ /dev/null @@ -1,18 +0,0 @@ - - - -CSS aspect-ratio: Flex item main size with flex-basis:content and width in Row flex container - - - - - -

Test passes if there is a filled green square and no red.

- -
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/-flex-aspect-ratio-022.htm b/test/render/flex/-flex-aspect-ratio-022.htm deleted file mode 100644 index aff31c8cf..000000000 --- a/test/render/flex/-flex-aspect-ratio-022.htm +++ /dev/null @@ -1,18 +0,0 @@ - - - -CSS aspect-ratio: Column flexbox main size with flex-basis:content and height - - - - - -

Test passes if there is a filled green square and no red.

- -
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/-flex-direction-row-002-visual.htm b/test/render/flex/-flex-direction-row-002-visual.htm deleted file mode 100644 index 83c5969f8..000000000 --- a/test/render/flex/-flex-direction-row-002-visual.htm +++ /dev/null @@ -1,45 +0,0 @@ - - - - - CSS Test: flex-direction:row axis matches that of writing mode inline axis - - - - - - - - -

Test passes if both the lines below are identical.

-
- ABC -
-
- CBA -
- - - - - \ No newline at end of file diff --git a/test/render/flex/-flex-direction-row-vertical.htm b/test/render/flex/-flex-direction-row-vertical.htm deleted file mode 100644 index c643f8141..000000000 --- a/test/render/flex/-flex-direction-row-vertical.htm +++ /dev/null @@ -1,51 +0,0 @@ - - - - - CSS Test: flex-direction:row has the same orientation as inline axis - - - - - - - - - -

Test passes if both the two columns below are identical.

-
-
- ABC -
-
- CBA -
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/-flex-inline.htm b/test/render/flex/-flex-inline.htm deleted file mode 100644 index 45d22c8c8..000000000 --- a/test/render/flex/-flex-inline.htm +++ /dev/null @@ -1,33 +0,0 @@ - - - - - CSS Flexible Box Test: display proprety - inline-flex - - - - - - - -

The test passed if you see a green block which its text is 'Success!'.

-
- -
Success!
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/-flex-minimum-height-flex-items-022.htm b/test/render/flex/-flex-minimum-height-flex-items-022.htm deleted file mode 100644 index b685c3054..000000000 --- a/test/render/flex/-flex-minimum-height-flex-items-022.htm +++ /dev/null @@ -1,15 +0,0 @@ - -Minimum height of a replaced element with borders - - - - -

Test passes if there is a filled green square and no red.

- -
-
- -
-
-
- \ No newline at end of file diff --git a/test/render/flex/-flex-minimum-width-flex-items-001.htm b/test/render/flex/-flex-minimum-width-flex-items-001.htm deleted file mode 100644 index ed98e4c53..000000000 --- a/test/render/flex/-flex-minimum-width-flex-items-001.htm +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - CSS Flexible Box Test: Minimum width of flex items - - - - - - - - - -

Test passes if there is a filled green square and no red.

- -
-
-
IT E
-
- - - - \ No newline at end of file diff --git a/test/render/flex/-flex-minimum-width-flex-items-003.htm b/test/render/flex/-flex-minimum-width-flex-items-003.htm deleted file mode 100644 index 3bac0f066..000000000 --- a/test/render/flex/-flex-minimum-width-flex-items-003.htm +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - CSS Flexible Box Test: Minimum width of flex items - - - - - - - - - -

Test passes if there is a filled green square and no red.

- -
-
-
IT E
-
- - - - \ No newline at end of file diff --git a/test/render/flex/-flex-minimum-width-flex-items-009.htm b/test/render/flex/-flex-minimum-width-flex-items-009.htm deleted file mode 100644 index 4f2848ac3..000000000 --- a/test/render/flex/-flex-minimum-width-flex-items-009.htm +++ /dev/null @@ -1,32 +0,0 @@ - -CSS Flexible Box Test: Minimum width of flex items - - - - - - -

Test passes if there is a filled green square and no red.

- -
-
- -
- \ No newline at end of file diff --git a/test/render/flex/-flexbox-abspos-child-001b.htm b/test/render/flex/-flexbox-abspos-child-001b.htm deleted file mode 100644 index 80998abb6..000000000 --- a/test/render/flex/-flexbox-abspos-child-001b.htm +++ /dev/null @@ -1,54 +0,0 @@ - - - CSS Test: Testing that "min-width", "max-width", "min-height", and "max-height" are applied on absolutely positioned children of a vertical flex container - - - - - - - -
-
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-abspos-child-002.htm b/test/render/flex/-flexbox-abspos-child-002.htm deleted file mode 100644 index 43e833c59..000000000 --- a/test/render/flex/-flexbox-abspos-child-002.htm +++ /dev/null @@ -1,61 +0,0 @@ - - - - - CSS Test: Test that "flex-basis" doesn't affect layout of abspos flex child - - - - - - - - - - -
-
-
-
- - -
-
-
-
- - -
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-align-self-baseline-horiz-006.htm b/test/render/flex/-flexbox-align-self-baseline-horiz-006.htm deleted file mode 100644 index 464752f45..000000000 --- a/test/render/flex/-flexbox-align-self-baseline-horiz-006.htm +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - CSS Test: Baseline alignment of block flex items with 'baseline' value for 'align-items' / 'align-self' against non-parallel writing-modes. - - - - - - -
-
ortho
-
one line
-
two
lines
-
offset
-
-
-
ortho
-
one line
-
two
lines
-
offset
-
- - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-align-self-baseline-horiz-008.htm b/test/render/flex/-flexbox-align-self-baseline-horiz-008.htm deleted file mode 100644 index 0dc1cb1fe..000000000 --- a/test/render/flex/-flexbox-align-self-baseline-horiz-008.htm +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - CSS Test: Baseline alignment of block flex items with 'baseline' and 'last-baseline' values for 'align-self' against each other. - - - - - - -
-
one line (first)
-
one line (last)
-
two
lines and offset (last)
-
offset (first)
-
-
-
one line (first)
-
one line (last)
-
two
lines and offset (last)
-
offset (first)
-
- - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-align-self-horiz-001-table.htm b/test/render/flex/-flexbox-align-self-horiz-001-table.htm deleted file mode 100644 index 6449e5c4d..000000000 --- a/test/render/flex/-flexbox-align-self-horiz-001-table.htm +++ /dev/null @@ -1,101 +0,0 @@ - - - - - - CSS Test: Testing the various 'align-self' property values on flex items that are tables - - - - - - -
-
start
-
a b c d e f
-
end
-
a b c d e f
-
center
-
a b c d e f
-
base
-
abc
-
stretch
-
a b c d e f
-
auto
-
unspec
-
initial
-
inherit
-
- - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-align-self-horiz-002.htm b/test/render/flex/-flexbox-align-self-horiz-002.htm deleted file mode 100644 index 647659d08..000000000 --- a/test/render/flex/-flexbox-align-self-horiz-002.htm +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - CSS Test: Testing the behavior of 'align-self' with a horizontal flex container, with margin/padding/border on the items - - - - - - -
-
start
-
a b c d e f
-
end
-
a b c d e f
-
center
-
a b c d e f
-
-
-
-
base
-
abc
-
stretch
-
a b c d e f
-
-
-
-
self-start
-
a b c d e f
-
self-end
-
a b c d e f
-
- - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-align-self-vert-002.htm b/test/render/flex/-flexbox-align-self-vert-002.htm deleted file mode 100644 index 658b01b27..000000000 --- a/test/render/flex/-flexbox-align-self-vert-002.htm +++ /dev/null @@ -1,96 +0,0 @@ - - - - - - CSS Test: Testing the behavior of 'align-self' with a vertical flex container, with margin/padding/border on the items - - - - - - -
-
start
-
a b c d e f
-
end
-
a b c d e f
-
center
-
a b c d e f
-
-
-
base
-
abc
-
stretch
-
a b c d e f
-
-
-
self-start
-
a b c d e f
-
self-end
-
a b c d e f
-
- - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-align-self-vert-rtl-001.htm b/test/render/flex/-flexbox-align-self-vert-rtl-001.htm deleted file mode 100644 index 25bbe1970..000000000 --- a/test/render/flex/-flexbox-align-self-vert-rtl-001.htm +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - CSS Test: Testing the behavior of 'align-self' property values on flex items that are blocks, in a vertical flex container with 'direction: rtl' - - - - - - -
-
start
-
a b c d e f
-
end
-
a b c d e f
-
center
-
a b c d e f
-
base
-
abc
-
stretch
-
a b c d e f
-
auto
-
unspec
-
initial
-
inherit
-
- - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-align-self-vert-rtl-002.htm b/test/render/flex/-flexbox-align-self-vert-rtl-002.htm deleted file mode 100644 index 03ca3ef9f..000000000 --- a/test/render/flex/-flexbox-align-self-vert-rtl-002.htm +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - CSS Test: Testing the behavior of 'align-self' with a vertical flex container, with margin/padding/border on the items and with 'direction: rtl' - - - - - - -
-
start
-
a b c d e f
-
end
-
a b c d e f
-
center
-
a b c d e f
-
-
-
base
-
abc
-
stretch
-
a b c d e f
-
- - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-align-self-vert-rtl-003.htm b/test/render/flex/-flexbox-align-self-vert-rtl-003.htm deleted file mode 100644 index 69eea46e7..000000000 --- a/test/render/flex/-flexbox-align-self-vert-rtl-003.htm +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - CSS Test: Testing the behavior of 'align-self' with a vertical flex container that's skinnier than its items and with 'direction: rtl' - - - - - - -
-
start
-
a b
-
end
-
a b
-
center
-
a b
-
base
-
abc
-
stretch
-
a b
-
- - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-align-self-vert-rtl-004.htm b/test/render/flex/-flexbox-align-self-vert-rtl-004.htm deleted file mode 100644 index b8c9566c5..000000000 --- a/test/render/flex/-flexbox-align-self-vert-rtl-004.htm +++ /dev/null @@ -1,90 +0,0 @@ - - - - - - CSS Test: Testing the behavior of 'align-self' with a vertical flex container that's skinnier than its items, with margin/padding/border on the items and with 'direction: rtl' - - - - - - - -
-
start
-
a b
-
stretch
-
a b
-
center
-
a b
-
-
-
base
-
abc
-
end
-
a b
-
- - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-align-self-vert-rtl-005.htm b/test/render/flex/-flexbox-align-self-vert-rtl-005.htm deleted file mode 100644 index 9d7600e9b..000000000 --- a/test/render/flex/-flexbox-align-self-vert-rtl-005.htm +++ /dev/null @@ -1,94 +0,0 @@ - - - - - - CSS Test: Testing the behavior of 'align-self' with a vertical flex container, with margin/padding/border on the items and with 'direction:rtl' - - - - - - -
-
start
-
a b c d e f
-
end
-
a b c d e f
-
center
-
a b c d e f
-
-
-
base
-
abc
-
stretch
-
a b c d e f
-
-
-
self-start
-
a b c d e f
-
self-end
-
a b c d e f
-
- - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-anonymous-items-001.htm b/test/render/flex/-flexbox-anonymous-items-001.htm deleted file mode 100644 index b48a4553a..000000000 --- a/test/render/flex/-flexbox-anonymous-items-001.htm +++ /dev/null @@ -1,25 +0,0 @@ - - - CSS Test: Testing that we gracefully handle cases where two anonymous flex items become adjacent due to "order" reordering - - - - - - - -
- a a -
x x
- b b -
- - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-basic-block-horiz-001v.htm b/test/render/flex/-flexbox-basic-block-horiz-001v.htm deleted file mode 100644 index 09e29057c..000000000 --- a/test/render/flex/-flexbox-basic-block-horiz-001v.htm +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - CSS Test: Testing flexbox layout algorithm property on block flex items in a horizontal flex container - (with various writing-modes on the flex items). - - - - - - - -
-
-
-
-
-
-
-
-
- -
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-basic-block-vert-001v.htm b/test/render/flex/-flexbox-basic-block-vert-001v.htm deleted file mode 100644 index bd3318a73..000000000 --- a/test/render/flex/-flexbox-basic-block-vert-001v.htm +++ /dev/null @@ -1,77 +0,0 @@ - - - - - - - CSS Test: Testing flexbox layout algorithm property on block flex items in a vertical flex container - (with various writing-modes on the flex items). - - - - - - - -
-
-
-
-
-
-
-
-
- -
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-collapsed-item-baseline-001.htm b/test/render/flex/-flexbox-collapsed-item-baseline-001.htm deleted file mode 100644 index adedd02cd..000000000 --- a/test/render/flex/-flexbox-collapsed-item-baseline-001.htm +++ /dev/null @@ -1,57 +0,0 @@ - - - - - CSS Test: Testing that a collapsed flex item participates in baseline alignment only for the purpose of establishing container's cross size - - - - - - - - -
-
a
-
b
-
- - -
-
a
-
b
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-collapsed-item-horiz-001.htm b/test/render/flex/-flexbox-collapsed-item-horiz-001.htm deleted file mode 100644 index 6bc91e105..000000000 --- a/test/render/flex/-flexbox-collapsed-item-horiz-001.htm +++ /dev/null @@ -1,101 +0,0 @@ - - - - - CSS Test: Testing that visibility:collapse on a flex item in a single-line flex container maintains the containers's cross size, but doesn't otherwise impact flex layout - - - - - - - - - -
-
-
- -
- - - -
-
-
-
- -
-
-
-
- -
- - - -
-
-
-
- -
-
-
-
- -
- - - -
-
-
-
- -
-
-
-
- - - - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-collapsed-item-horiz-002.htm b/test/render/flex/-flexbox-collapsed-item-horiz-002.htm deleted file mode 100644 index 74b24f5da..000000000 --- a/test/render/flex/-flexbox-collapsed-item-horiz-002.htm +++ /dev/null @@ -1,114 +0,0 @@ - - - - - CSS Test: Testing that visibility:collapse on a flex item in a multi-line flex container creates struts, and that they can migrate between lines - - - - - - - - - -
-
-
-
-
- -
-
-
-
-
- -
- - - -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
- - - -
-
-
-
-
- -
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-collapsed-item-horiz-003.htm b/test/render/flex/-flexbox-collapsed-item-horiz-003.htm deleted file mode 100644 index b151b3e09..000000000 --- a/test/render/flex/-flexbox-collapsed-item-horiz-003.htm +++ /dev/null @@ -1,59 +0,0 @@ - - - - - CSS Test: Testing that strut formation (from visibility:collapse) happens *after* lines have been stretched - - - - - - - - -
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-flex-basis-content-001a.htm b/test/render/flex/-flexbox-flex-basis-content-001a.htm deleted file mode 100644 index ffb5c3aed..000000000 --- a/test/render/flex/-flexbox-flex-basis-content-001a.htm +++ /dev/null @@ -1,86 +0,0 @@ - - - - - - CSS Test: Testing "flex-basis: content" in a row-oriented flex container - - - - - - - - - - -
-
a b
-
c
-
-
- -
- - -
-
a b
-
c
-
-
- -
- - -
-
a b
-
c
-
-
- -
- - - - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-flex-basis-content-001b.htm b/test/render/flex/-flexbox-flex-basis-content-001b.htm deleted file mode 100644 index 8b4992931..000000000 --- a/test/render/flex/-flexbox-flex-basis-content-001b.htm +++ /dev/null @@ -1,86 +0,0 @@ - - - - - - CSS Test: Testing "flex-basis: content" (set via the "flex" shorthand) - in a row-oriented flex container. - - - - - - - - - - -
-
a b
-
c
-
-
- -
- - -
-
a b
-
c
-
-
- -
- - -
-
a b
-
c
-
-
- -
- - - - - - diff --git a/test/render/flex/-flexbox-flex-basis-content-004b.htm b/test/render/flex/-flexbox-flex-basis-content-004b.htm deleted file mode 100644 index 38b352941..000000000 --- a/test/render/flex/-flexbox-flex-basis-content-004b.htm +++ /dev/null @@ -1,131 +0,0 @@ - - - - - - CSS Test: Testing that used "flex-basis: content" is treated as - "max-content" when calculating flex base size - - - - - - - - - - - -
-
-
- - -
-
- - - -
-
- - -
-
- -
-
- - -
- -
- - -
-
- - - -
-
- - -
-
- - - -
-
- - - - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-flex-wrap-default.htm b/test/render/flex/-flexbox-flex-wrap-default.htm deleted file mode 100644 index 170481d09..000000000 --- a/test/render/flex/-flexbox-flex-wrap-default.htm +++ /dev/null @@ -1,39 +0,0 @@ - - - CSS Flexbox Test: Flex-wrap defaults to nowrap - - - - - - - - - -

The test passes if there is a green square and no red.

-
-
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-flex-wrap-vert-001.htm b/test/render/flex/-flexbox-flex-wrap-vert-001.htm deleted file mode 100644 index 15a76cc73..000000000 --- a/test/render/flex/-flexbox-flex-wrap-vert-001.htm +++ /dev/null @@ -1,98 +0,0 @@ - - - CSS Test: Testing flex-wrap in vertical flex containers - - - - - - - - - -
-
-
- - -
-
-
- - -
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
- - -
-
-
- - -
-
-
-
- - -
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-flex-wrap-vert-002.htm b/test/render/flex/-flexbox-flex-wrap-vert-002.htm deleted file mode 100644 index fcdcba043..000000000 --- a/test/render/flex/-flexbox-flex-wrap-vert-002.htm +++ /dev/null @@ -1,60 +0,0 @@ - - - CSS Test: Ensure that min-height is honored for vertical multi-line flex containers - - - - - - - - - -
-
- - -
-
-
- - -
-
-
-
-
-
-
- - -
-
-
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-flex-wrap-wrap-reverse.htm b/test/render/flex/-flexbox-flex-wrap-wrap-reverse.htm deleted file mode 100644 index 2fa0c0719..000000000 --- a/test/render/flex/-flexbox-flex-wrap-wrap-reverse.htm +++ /dev/null @@ -1,60 +0,0 @@ - - - - - CSS Flexbox Test: Flex-wrap = wrap-reverse - - - - - - - - - -

The test passes if there is a 3x3 grid of green squares, numbered 1-9 left-to-right and top-to-bottom, and there is no red.

-
-
7
8
9
-
4
5
6
-
1
2
3
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-justify-content-wmvert-001.htm b/test/render/flex/-flexbox-justify-content-wmvert-001.htm deleted file mode 100644 index 85486134d..000000000 --- a/test/render/flex/-flexbox-justify-content-wmvert-001.htm +++ /dev/null @@ -1,144 +0,0 @@ - - - - - - CSS Test: Testing 'justify-content' in a vertical writing mode flex container - - - - - - - - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-mbp-horiz-001-rtl-reverse.htm b/test/render/flex/-flexbox-mbp-horiz-001-rtl-reverse.htm deleted file mode 100644 index 70f6d61eb..000000000 --- a/test/render/flex/-flexbox-mbp-horiz-001-rtl-reverse.htm +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - CSS Test: Testing borders on flex items in a row-reverse horizontal flex container, with 'direction: rtl' - - - - - - -
-
-
-
-
-
-
-
-
- -
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-mbp-horiz-001-rtl.htm b/test/render/flex/-flexbox-mbp-horiz-001-rtl.htm deleted file mode 100644 index dbee82b29..000000000 --- a/test/render/flex/-flexbox-mbp-horiz-001-rtl.htm +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - CSS Test: Testing borders on flex items in a horizontal flex container with 'direction: rtl' - - - - - - -
-
-
-
-
-
-
-
-
- -
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-mbp-horiz-002v.htm b/test/render/flex/-flexbox-mbp-horiz-002v.htm deleted file mode 100644 index 5e30e0e2e..000000000 --- a/test/render/flex/-flexbox-mbp-horiz-002v.htm +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - - CSS Test: Testing margins, borders, and padding on flex items in a horizontal flex container - (with a vertical writing-mode on the flex items). - - - - - - - -
-
-
-
-
-
-
-
-
- -
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-mbp-horiz-003v.htm b/test/render/flex/-flexbox-mbp-horiz-003v.htm deleted file mode 100644 index 48eeab28e..000000000 --- a/test/render/flex/-flexbox-mbp-horiz-003v.htm +++ /dev/null @@ -1,81 +0,0 @@ - - - - - - - CSS Test: Testing borders and padding on a horizontal flex container and its flex items - (with a vertical writing-mode on the flex items). - - - - - - - -
-
-
-
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-min-width-auto-002a.htm b/test/render/flex/-flexbox-min-width-auto-002a.htm deleted file mode 100644 index 0a23667de..000000000 --- a/test/render/flex/-flexbox-min-width-auto-002a.htm +++ /dev/null @@ -1,66 +0,0 @@ - - - - CSS Test: Testing min-width:auto - - - - - - - - - - - -
- blue square -
- -
- blue square -
- -
- blue square -
- - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-min-width-auto-002b.htm b/test/render/flex/-flexbox-min-width-auto-002b.htm deleted file mode 100644 index e62227047..000000000 --- a/test/render/flex/-flexbox-min-width-auto-002b.htm +++ /dev/null @@ -1,66 +0,0 @@ - - - - CSS Test: Testing min-width:auto - - - - - - - - - - - -
- blue square -
- -
- blue square -
- -
- blue square -
- - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-min-width-auto-002c.htm b/test/render/flex/-flexbox-min-width-auto-002c.htm deleted file mode 100644 index 1f19a017c..000000000 --- a/test/render/flex/-flexbox-min-width-auto-002c.htm +++ /dev/null @@ -1,68 +0,0 @@ - - - - CSS Test: Testing min-width:auto - - - - - - - - - - - -
- blue square -
- -
- blue square -
- -
- blue square -
- - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-min-width-auto-005.htm b/test/render/flex/-flexbox-min-width-auto-005.htm deleted file mode 100644 index f4321ab99..000000000 --- a/test/render/flex/-flexbox-min-width-auto-005.htm +++ /dev/null @@ -1,38 +0,0 @@ - -CSS Flexible Box Test: Aspect ratio handling of images - - - - - - - -

Test passes if there is no red visible on the page.

- -
-
- -
- -
- -
-
-
- -
-
- \ No newline at end of file diff --git a/test/render/flex/-flexbox-min-width-auto-006.htm b/test/render/flex/-flexbox-min-width-auto-006.htm deleted file mode 100644 index 662543787..000000000 --- a/test/render/flex/-flexbox-min-width-auto-006.htm +++ /dev/null @@ -1,45 +0,0 @@ - -CSS Flexible Box Test: Aspect ratio handling of images - - - - - - - -

Test passes if there are a (vertically centered) 20x20 and a 60x100 green boxes enclosed on each 100x100 square.

- -
-
- -
-
- -
- -
-
- -
-
\ No newline at end of file diff --git a/test/render/flex/-flexbox-overflow-horiz-001.htm.htm b/test/render/flex/-flexbox-overflow-horiz-001.htm.htm deleted file mode 100644 index 291b337bb..000000000 --- a/test/render/flex/-flexbox-overflow-horiz-001.htm.htm +++ /dev/null @@ -1,54 +0,0 @@ - - - CSS Test: Testing 'overflow' property on a horizontal flex container, with contents not overflowing - - - - - - -
-
-
-
- -
-
-
-
-
-
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-whitespace-handling-001a.htm b/test/render/flex/-flexbox-whitespace-handling-001a.htm deleted file mode 100644 index f70b68dd9..000000000 --- a/test/render/flex/-flexbox-whitespace-handling-001a.htm +++ /dev/null @@ -1,54 +0,0 @@ - - - - CSS Test: Test that anonymous flex items aren't created for pure-whitespace inline content - - - - - - - -
- - -
- - -
-
- - \ No newline at end of file diff --git a/test/render/flex/-flexbox-whitespace-handling-001b.htm b/test/render/flex/-flexbox-whitespace-handling-001b.htm deleted file mode 100644 index 59adb90b5..000000000 --- a/test/render/flex/-flexbox-whitespace-handling-001b.htm +++ /dev/null @@ -1,42 +0,0 @@ - - - - CSS Test: Test that flex items are created correctly - - - - - - -
- -
- -
- - \ No newline at end of file diff --git a/test/render/flex/-flexbox-writing-mode-001.htm b/test/render/flex/-flexbox-writing-mode-001.htm deleted file mode 100644 index 466b0b7f6..000000000 --- a/test/render/flex/-flexbox-writing-mode-001.htm +++ /dev/null @@ -1,81 +0,0 @@ - - - - - CSS Test: Try various flex-flow values, with 'direction: ltr' and 'writing-mode: horizontal-tb' - - - - - - - -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - - - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-writing-mode-002.htm b/test/render/flex/-flexbox-writing-mode-002.htm deleted file mode 100644 index 34b11ec0a..000000000 --- a/test/render/flex/-flexbox-writing-mode-002.htm +++ /dev/null @@ -1,81 +0,0 @@ - - - - - CSS Test: Try various flex-flow values, with 'direction: ltr' and 'writing-mode: vertical-rl' - - - - - - - -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - - - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-writing-mode-003.htm b/test/render/flex/-flexbox-writing-mode-003.htm deleted file mode 100644 index af3f20adc..000000000 --- a/test/render/flex/-flexbox-writing-mode-003.htm +++ /dev/null @@ -1,81 +0,0 @@ - - - - - CSS Test: Try various flex-flow values, with 'direction: ltr' and 'writing-mode: vertical-lr' - - - - - - - -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - - - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-writing-mode-004.htm b/test/render/flex/-flexbox-writing-mode-004.htm deleted file mode 100644 index 7332a2a61..000000000 --- a/test/render/flex/-flexbox-writing-mode-004.htm +++ /dev/null @@ -1,81 +0,0 @@ - - - - - CSS Test: Try various flex-flow values, with 'direction: rtl' and 'writing-mode: horizontal-tb' - - - - - - - -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - - - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-writing-mode-005.htm b/test/render/flex/-flexbox-writing-mode-005.htm deleted file mode 100644 index 65ee1e3ec..000000000 --- a/test/render/flex/-flexbox-writing-mode-005.htm +++ /dev/null @@ -1,81 +0,0 @@ - - - - - CSS Test: Try various flex-flow values, with 'direction: rtl' and 'writing-mode: vertical-rl' - - - - - - - -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - - - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-writing-mode-006.htm b/test/render/flex/-flexbox-writing-mode-006.htm deleted file mode 100644 index 0389a1530..000000000 --- a/test/render/flex/-flexbox-writing-mode-006.htm +++ /dev/null @@ -1,81 +0,0 @@ - - - - - CSS Test: Try various flex-flow values, with 'direction: rtl' and 'writing-mode: vertical-lr' - - - - - - - -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - - - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-writing-mode-007.htm b/test/render/flex/-flexbox-writing-mode-007.htm deleted file mode 100644 index 11e44f206..000000000 --- a/test/render/flex/-flexbox-writing-mode-007.htm +++ /dev/null @@ -1,78 +0,0 @@ - - - - - CSS Test: Verify that explicit sizes are honored on flex items whose writing-mode may differ from the flex container's writing-mode - - - - - - - - -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-writing-mode-008.htm b/test/render/flex/-flexbox-writing-mode-008.htm deleted file mode 100644 index f32db5b0a..000000000 --- a/test/render/flex/-flexbox-writing-mode-008.htm +++ /dev/null @@ -1,78 +0,0 @@ - - - - - CSS Test: Verify that explicit sizes are honored on flex items whose writing-mode may differ from the flex container's writing-mode - - - - - - - - -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-writing-mode-009.htm b/test/render/flex/-flexbox-writing-mode-009.htm deleted file mode 100644 index 6145779d0..000000000 --- a/test/render/flex/-flexbox-writing-mode-009.htm +++ /dev/null @@ -1,78 +0,0 @@ - - - - - CSS Test: Verify that explicit sizes are honored on flex items whose writing-mode may differ from the flex container's writing-mode - - - - - - - - -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-writing-mode-010.htm b/test/render/flex/-flexbox-writing-mode-010.htm deleted file mode 100644 index ca1f38d54..000000000 --- a/test/render/flex/-flexbox-writing-mode-010.htm +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - CSS Test: Testing a mix of flex items with various values for - 'writing-mode' / 'direction' in a horizontal row-oriented flex container. - - - - - - - - - - - -
- p b c - p e - p b c - p e - p b c - p e -
-
- p b c - p e - p b c - p e - p b c - p e -
-
- p b c - p e - p b c - p e - p b c - p e -
-
- p b c - p e - p b c - p e - p b c - p e -
- - - - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-writing-mode-011.htm b/test/render/flex/-flexbox-writing-mode-011.htm deleted file mode 100644 index 971f54a25..000000000 --- a/test/render/flex/-flexbox-writing-mode-011.htm +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - CSS Test: Testing a mix of flex items with various values for - 'writing-mode' / 'direction' in a vertical row-oriented flex container. - - - - - - - - - - - -
- p b c - p e - p b c - p e - p b c - p e -
-
- p b c - p e - p b c - p e - p b c - p e -
-
- p b c - p e - p b c - p e - p b c - p e -
-
- p b c - p e - p b c - p e - p b c - p e -
- - - - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-writing-mode-012.htm b/test/render/flex/-flexbox-writing-mode-012.htm deleted file mode 100644 index 048784758..000000000 --- a/test/render/flex/-flexbox-writing-mode-012.htm +++ /dev/null @@ -1,89 +0,0 @@ - - - - - - CSS Test: Testing a mix of flex items with various values for - 'writing-mode' / 'direction' in a vertical row-oriented flex container - with 'direction' flipped. - - - - - - - - - - - -
- p b c - p e - p b c - p e - p b c - p e -
-
- p b c - p e - p b c - p e - p b c - p e -
-
- p b c - p e - p b c - p e - p b c - p e -
-
- p b c - p e - p b c - p e - p b c - p e -
- - - - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-writing-mode-013.htm b/test/render/flex/-flexbox-writing-mode-013.htm deleted file mode 100644 index b3b7f99d6..000000000 --- a/test/render/flex/-flexbox-writing-mode-013.htm +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - CSS Test: Testing a mix of flex items with various values for - 'writing-mode' / 'direction' in a horizontal column-oriented flex container. - - - - - - - - - - - -
- p b c - p e - p b c - p e - p b c - p e -
-
- p b c - p e - p b c - p e - p b c - p e -
-
- p b c - p e - p b c - p e - p b c - p e -
-
- p b c - p e - p b c - p e - p b c - p e -
- - - - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-writing-mode-014.htm b/test/render/flex/-flexbox-writing-mode-014.htm deleted file mode 100644 index 3a8755bff..000000000 --- a/test/render/flex/-flexbox-writing-mode-014.htm +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - CSS Test: Testing a mix of flex items with various values for - 'writing-mode' / 'direction' in a vertical column-oriented flex container. - - - - - - - - - - - -
- p b c - p e - p b c - p e - p b c - p e -
-
- p b c - p e - p b c - p e - p b c - p e -
-
- p b c - p e - p b c - p e - p b c - p e -
-
- p b c - p e - p b c - p e - p b c - p e -
- - - - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-writing-mode-015.htm b/test/render/flex/-flexbox-writing-mode-015.htm deleted file mode 100644 index a01975a70..000000000 --- a/test/render/flex/-flexbox-writing-mode-015.htm +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - CSS Test: Testing a mix of flex items with various values for - 'writing-mode' / 'direction' in a vertical column-oriented flex container - with 'direction' flipped. - - - - - - - - - - - -
- p b c - p e - p b c - p e - p b c - p e -
-
- p b c - p e - p b c - p e - p b c - p e -
-
- p b c - p e - p b c - p e - p b c - p e -
-
- p b c - p e - p b c - p e - p b c - p e -
- - - - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox-writing-mode-016.htm b/test/render/flex/-flexbox-writing-mode-016.htm deleted file mode 100644 index afbcb55dd..000000000 --- a/test/render/flex/-flexbox-writing-mode-016.htm +++ /dev/null @@ -1,147 +0,0 @@ - - - - - - CSS Test: Testing auto-sized flex containers - with various 'writing-mode' values - and various padding amounts on flex items. - - - - - - - - - - -
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox_align-items-stretch-writing-modes.htm b/test/render/flex/-flexbox_align-items-stretch-writing-modes.htm deleted file mode 100644 index dd019adb8..000000000 --- a/test/render/flex/-flexbox_align-items-stretch-writing-modes.htm +++ /dev/null @@ -1,62 +0,0 @@ - - - - - CSS Test: Flexbox align-items: stretch with writing-mode vertical-lr and vertical-rl - - - - - - - - - -

The test passes if you see a green rectangle and no red.

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/-flexbox_block.htm b/test/render/flex/-flexbox_block.htm deleted file mode 100644 index afa26b405..000000000 --- a/test/render/flex/-flexbox_block.htm +++ /dev/null @@ -1,19 +0,0 @@ - - - -flexbox | block - - - - - -
FAIL
- - - \ No newline at end of file diff --git a/test/render/flex/-flexbox_columns-flexitems-2.htm b/test/render/flex/-flexbox_columns-flexitems-2.htm deleted file mode 100644 index 101188a77..000000000 --- a/test/render/flex/-flexbox_columns-flexitems-2.htm +++ /dev/null @@ -1,32 +0,0 @@ - -flexbox | multicol on flexbox items - - - - - - - -
-

- one two three four five - one two three four five - one two three four five -

-
- \ No newline at end of file diff --git a/test/render/flex/-flexbox_columns-flexitems.htm b/test/render/flex/-flexbox_columns-flexitems.htm deleted file mode 100644 index 22665e289..000000000 --- a/test/render/flex/-flexbox_columns-flexitems.htm +++ /dev/null @@ -1,30 +0,0 @@ - - - -flexbox | multicol on flexbox items - - - - - -
-

one two three four five

-
- - - \ No newline at end of file diff --git a/test/render/flex/-flexbox_rowspan-overflow-automatic.htm b/test/render/flex/-flexbox_rowspan-overflow-automatic.htm deleted file mode 100644 index 6209c6a85..000000000 --- a/test/render/flex/-flexbox_rowspan-overflow-automatic.htm +++ /dev/null @@ -1,69 +0,0 @@ - - - -flexbox | flexcontainers in cells with rowspan - - - - - - - - - - - - - -
-
-

filler

-

filler

-

filler

-

filler

-

filler

-
-
-
-

filler

-

filler

-

filler

-

filler

-

filler

-
-
-
-

filler

-

filler

-

filler

-

filler

-

filler

-
-
- - - \ No newline at end of file diff --git a/test/render/flex/-flexbox_rowspan-overflow.htm b/test/render/flex/-flexbox_rowspan-overflow.htm deleted file mode 100644 index 281ba4ec9..000000000 --- a/test/render/flex/-flexbox_rowspan-overflow.htm +++ /dev/null @@ -1,68 +0,0 @@ - - - -flexbox | flexcontainers in cells with rowspan - - - - - - - - - - - - - -
-
-

filler

-

filler

-

filler

-

filler

-

filler

-
-
-
-

filler

-

filler

-

filler

-

filler

-

filler

-
-
-
-

filler

-

filler

-

filler

-

filler

-

filler

-
-
- - - \ No newline at end of file diff --git a/test/render/flex/-flexbox_rowspan.htm b/test/render/flex/-flexbox_rowspan.htm deleted file mode 100644 index f99fd379e..000000000 --- a/test/render/flex/-flexbox_rowspan.htm +++ /dev/null @@ -1,68 +0,0 @@ - - - -flexbox | flexcontainers in cells with rowspan - - - - - - - - - - - - - -
-
-

 

-

 

-

 

-

 

-

 

-
-
-
-

 

-

 

-

 

-

 

-

 

-
-
-
-

 

-

 

-

 

-

 

-

 

-
-
- - - \ No newline at end of file diff --git a/test/render/flex/-flexbox_rtl-direction.htm b/test/render/flex/-flexbox_rtl-direction.htm deleted file mode 100644 index 91c501aaf..000000000 --- a/test/render/flex/-flexbox_rtl-direction.htm +++ /dev/null @@ -1,38 +0,0 @@ - - - -flexbox | flex-direction: column-reverse | rtl - - - - - - -
- filler - filler - filler - filler -
- - - \ No newline at end of file diff --git a/test/render/flex/-flexbox_rtl-flow-reverse.htm b/test/render/flex/-flexbox_rtl-flow-reverse.htm deleted file mode 100644 index 67828d5e1..000000000 --- a/test/render/flex/-flexbox_rtl-flow-reverse.htm +++ /dev/null @@ -1,38 +0,0 @@ - - - -flexbox | flex-flow: column wrap-reverse | rtl - - - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/-flexbox_rtl-flow.htm b/test/render/flex/-flexbox_rtl-flow.htm deleted file mode 100644 index d4d122e2c..000000000 --- a/test/render/flex/-flexbox_rtl-flow.htm +++ /dev/null @@ -1,38 +0,0 @@ - - - -flexbox | flex-flow: column wrap | rtl - - - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/-flexbox_rtl-order.htm b/test/render/flex/-flexbox_rtl-order.htm deleted file mode 100644 index c4581db25..000000000 --- a/test/render/flex/-flexbox_rtl-order.htm +++ /dev/null @@ -1,57 +0,0 @@ - - - -flexbox | flex-flow: column-reverse wrap-reverse; order | rtl - - - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/-flexbox_stf-table-singleline.htm b/test/render/flex/-flexbox_stf-table-singleline.htm deleted file mode 100644 index 8f5db709d..000000000 --- a/test/render/flex/-flexbox_stf-table-singleline.htm +++ /dev/null @@ -1,38 +0,0 @@ - - - -flexbox | singleline flexcontainer versus stf :: table - - - - - - -
-
-

filler

-

filler

-

filler

-

filler

-

filler

-
-
- - - diff --git a/test/render/flex/-flexbox_writing_mode_vertical_lays_out_contents_from_top_to_bottom.htm b/test/render/flex/-flexbox_writing_mode_vertical_lays_out_contents_from_top_to_bottom.htm deleted file mode 100644 index 1b1fc849f..000000000 --- a/test/render/flex/-flexbox_writing_mode_vertical_lays_out_contents_from_top_to_bottom.htm +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - CSS Flexbox Test: Align content flex-start with writing mode vertical-rl. - - - - - - - -

The test passes if you see green and red top, blue and yellow bottom.

- -
-
-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/-justify-content-001.htm b/test/render/flex/-justify-content-001.htm deleted file mode 100644 index 1115dc76e..000000000 --- a/test/render/flex/-justify-content-001.htm +++ /dev/null @@ -1,41 +0,0 @@ - - - - - CSS Test: A flex container with 'justify-content' property set to 'center' - - - - - - - - -

Test passes if there is a single blue rectangle on the left, a single orange rectangle directly to its right, and there is no red visible on the page.

-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/-justify-content-002.htm b/test/render/flex/-justify-content-002.htm deleted file mode 100644 index d6ff8aec4..000000000 --- a/test/render/flex/-justify-content-002.htm +++ /dev/null @@ -1,38 +0,0 @@ - - - - - CSS Test: A flex container with the 'justify-content' property set to 'flex-start' - - - - - - - - -

Test passes if there is a single blue rectangle on the left, a single orange rectangle directly to its right, and there is no red visible on the page.

-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/-justify-content-003.htm b/test/render/flex/-justify-content-003.htm deleted file mode 100644 index b1a83a9e8..000000000 --- a/test/render/flex/-justify-content-003.htm +++ /dev/null @@ -1,37 +0,0 @@ - - - - - CSS Test: A flex container with the 'justify-content' property set to 'flex-end' - - - - - - - - -

Test passes if there is a single blue rectangle on the left, a single orange rectangle directly to its right, and there is no red visible on the page.

-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/-justify-content-004.htm b/test/render/flex/-justify-content-004.htm deleted file mode 100644 index fcab4fbea..000000000 --- a/test/render/flex/-justify-content-004.htm +++ /dev/null @@ -1,41 +0,0 @@ - - - - - CSS Test: A flex container with the 'justify-content' property set to 'space-between' - - - - - - - - -

Test passes if there is a single blue rectangle on the left, a single orange rectangle directly to its right, and there is no red visible on the page.

-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/-justify-content-005.htm b/test/render/flex/-justify-content-005.htm deleted file mode 100644 index f9748dfb1..000000000 --- a/test/render/flex/-justify-content-005.htm +++ /dev/null @@ -1,41 +0,0 @@ - - - - - CSS Test: A flex container with the 'justify-content' property set to 'space-around' - - - - - - - - -

Test passes if there is a single blue rectangle on the left, a single orange rectangle directly to its right, and there is no red visible on the page.

-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/-negative-flex-margins-crash.htm b/test/render/flex/-negative-flex-margins-crash.htm deleted file mode 100644 index c6cdcfe62..000000000 --- a/test/render/flex/-negative-flex-margins-crash.htm +++ /dev/null @@ -1,26 +0,0 @@ - - - -CSS Flexbox: Crash caused by negative width in flex box - - - - - - -
-
-
PASS if we don't assert
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/-nested-orthogonal-flexbox-relayout.htm b/test/render/flex/-nested-orthogonal-flexbox-relayout.htm deleted file mode 100644 index fb5f085ac..000000000 --- a/test/render/flex/-nested-orthogonal-flexbox-relayout.htm +++ /dev/null @@ -1,34 +0,0 @@ - -CSS Flexbox: nested orthogonal children on relayout. - - - - - -
-
-
This text should not overflow its box
-
-
- - \ No newline at end of file diff --git a/test/render/flex/-percentage-size-subitems-001.htm b/test/render/flex/-percentage-size-subitems-001.htm deleted file mode 100644 index 3d610b9b1..000000000 --- a/test/render/flex/-percentage-size-subitems-001.htm +++ /dev/null @@ -1,98 +0,0 @@ - - - - -CSS Flexbox Test: Percentage size on child of a flex item with margin, border, padding and scrollbar - - - - - - -

The test passes if in the different examples you see scrollbars but there's no overflow, so you cannot actually scroll.

- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/-table-as-item-fixed-min-width-3.htm b/test/render/flex/-table-as-item-fixed-min-width-3.htm deleted file mode 100644 index 2dc259d70..000000000 --- a/test/render/flex/-table-as-item-fixed-min-width-3.htm +++ /dev/null @@ -1,25 +0,0 @@ - - - -table is flex item - - - - - - -

Test passes if there is a filled green square and no red.

- - - -
-
-
-
-
-
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/-table-as-item-flex-cross-size.htm b/test/render/flex/-table-as-item-flex-cross-size.htm deleted file mode 100644 index 12118e145..000000000 --- a/test/render/flex/-table-as-item-flex-cross-size.htm +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - -

Test passes if there is a filled green square and no red.

-
- - - - -
-
- - - \ No newline at end of file diff --git a/test/render/flex/-table-as-item-inflexible-in-column-1.htm b/test/render/flex/-table-as-item-inflexible-in-column-1.htm deleted file mode 100644 index c525aa0ed..000000000 --- a/test/render/flex/-table-as-item-inflexible-in-column-1.htm +++ /dev/null @@ -1,19 +0,0 @@ - - - -Table as a flex item in column-oriented flex container - - - - - - - - -

Test passes if there is a filled green square.

-
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/-table-as-item-inflexible-in-column-2.htm b/test/render/flex/-table-as-item-inflexible-in-column-2.htm deleted file mode 100644 index 97f0caaa0..000000000 --- a/test/render/flex/-table-as-item-inflexible-in-column-2.htm +++ /dev/null @@ -1,22 +0,0 @@ - - - -Table as a flex item in column-oriented flex container - - - - - - - - -

Test passes if there is a filled green square.

-
- - -
-
- - - \ No newline at end of file diff --git a/test/render/flex/-table-as-item-inflexible-in-row-1.htm b/test/render/flex/-table-as-item-inflexible-in-row-1.htm deleted file mode 100644 index b1b7574fc..000000000 --- a/test/render/flex/-table-as-item-inflexible-in-row-1.htm +++ /dev/null @@ -1,19 +0,0 @@ - - - -Table as a flex item in row-oriented flex container - - - - - - - - -

Test passes if there is a filled green square.

-
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/-table-as-item-inflexible-in-row-2.htm b/test/render/flex/-table-as-item-inflexible-in-row-2.htm deleted file mode 100644 index a4e391d06..000000000 --- a/test/render/flex/-table-as-item-inflexible-in-row-2.htm +++ /dev/null @@ -1,22 +0,0 @@ - - - -Table as a flex item in row-oriented flex container - - - - - - - - -

Test passes if there is a filled green square.

-
- - -
-
- - - \ No newline at end of file diff --git a/test/render/flex/-table-as-item-narrow-content-2.htm b/test/render/flex/-table-as-item-narrow-content-2.htm deleted file mode 100644 index c6ec74022..000000000 --- a/test/render/flex/-table-as-item-narrow-content-2.htm +++ /dev/null @@ -1,19 +0,0 @@ - - - -CSS Flexbox Test: Flex item as table with narrow content - - - - - -

Test passes if there is a filled green square.

-
-
-
-
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/-table-as-item-percent-width-cell-001.htm b/test/render/flex/-table-as-item-percent-width-cell-001.htm deleted file mode 100644 index 434aebe53..000000000 --- a/test/render/flex/-table-as-item-percent-width-cell-001.htm +++ /dev/null @@ -1,41 +0,0 @@ - - - - -Flexbox Test: display:table flex items with percent-width cells and content keywords for used flex-basis - - - - - - -
- -
12
-
-
-
12
-
-
-
12
-
-
-
12
-
- - - - \ No newline at end of file diff --git a/test/render/flex/-table-as-item-specified-height.htm b/test/render/flex/-table-as-item-specified-height.htm deleted file mode 100644 index d90ed789e..000000000 --- a/test/render/flex/-table-as-item-specified-height.htm +++ /dev/null @@ -1,20 +0,0 @@ - - - -table is flex item - - - - - - - - -

Test passes if there is a filled green square.

- -
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/-table-as-item-specified-width.htm b/test/render/flex/-table-as-item-specified-width.htm deleted file mode 100644 index df4b62c2d..000000000 --- a/test/render/flex/-table-as-item-specified-width.htm +++ /dev/null @@ -1,21 +0,0 @@ - - - -table is flex item - - - - - - -

Test passes if there is a filled green square.

- - - -
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/-table-as-item-stretch-cross-size-2.htm b/test/render/flex/-table-as-item-stretch-cross-size-2.htm deleted file mode 100644 index 303ae80e0..000000000 --- a/test/render/flex/-table-as-item-stretch-cross-size-2.htm +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - -

Test passes if there is a filled green square and no red.

-
- - - - -
-
- - - \ No newline at end of file diff --git a/test/render/flex/-table-as-item-stretch-cross-size-5.htm b/test/render/flex/-table-as-item-stretch-cross-size-5.htm deleted file mode 100644 index e153a3328..000000000 --- a/test/render/flex/-table-as-item-stretch-cross-size-5.htm +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - -

Test passes if there is a filled green square and no red.

-
- - -
-
-
-
-
-
-
-
-
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/-table-as-item-stretch-cross-size.htm b/test/render/flex/-table-as-item-stretch-cross-size.htm deleted file mode 100644 index af582df4f..000000000 --- a/test/render/flex/-table-as-item-stretch-cross-size.htm +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - -

Test passes if there is a filled green square and no red.

-
- - - - -
-
- - - \ No newline at end of file diff --git a/test/render/flex/abspos-autopos-htb-ltr.htm b/test/render/flex/abspos-autopos-htb-ltr.htm deleted file mode 100644 index bdfbe2cf0..000000000 --- a/test/render/flex/abspos-autopos-htb-ltr.htm +++ /dev/null @@ -1,39 +0,0 @@ - - - -Absolutely positioned child with auto position in vertical-rl ltr flexbox - - - - - -

Test passes if there is a filled green square and no red.

-
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/abspos-autopos-htb-ltr.htm.png b/test/render/flex/abspos-autopos-htb-ltr.htm.png deleted file mode 100644 index 21d20656f..000000000 Binary files a/test/render/flex/abspos-autopos-htb-ltr.htm.png and /dev/null differ diff --git a/test/render/flex/abspos-autopos-htb-rtl.htm b/test/render/flex/abspos-autopos-htb-rtl.htm deleted file mode 100644 index f3a5af1ce..000000000 --- a/test/render/flex/abspos-autopos-htb-rtl.htm +++ /dev/null @@ -1,39 +0,0 @@ - - - -Absolutely positioned child with auto position in vertical-rl ltr flexbox - - - - - -

Test passes if there is a filled green square and no red.

-
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/abspos-autopos-htb-rtl.htm.png b/test/render/flex/abspos-autopos-htb-rtl.htm.png deleted file mode 100644 index 21d20656f..000000000 Binary files a/test/render/flex/abspos-autopos-htb-rtl.htm.png and /dev/null differ diff --git a/test/render/flex/abspos-autopos-vlr-ltr.htm b/test/render/flex/abspos-autopos-vlr-ltr.htm deleted file mode 100644 index bcb71d975..000000000 --- a/test/render/flex/abspos-autopos-vlr-ltr.htm +++ /dev/null @@ -1,39 +0,0 @@ - - - -Absolutely positioned child with auto position in vertical-rl ltr flexbox - - - - - -

Test passes if there is a filled green square and no red.

-
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/abspos-autopos-vlr-ltr.htm.png b/test/render/flex/abspos-autopos-vlr-ltr.htm.png deleted file mode 100644 index 21d20656f..000000000 Binary files a/test/render/flex/abspos-autopos-vlr-ltr.htm.png and /dev/null differ diff --git a/test/render/flex/abspos-autopos-vlr-rtl.htm b/test/render/flex/abspos-autopos-vlr-rtl.htm deleted file mode 100644 index b369dad94..000000000 --- a/test/render/flex/abspos-autopos-vlr-rtl.htm +++ /dev/null @@ -1,39 +0,0 @@ - - - -Absolutely positioned child with auto position in vertical-rl ltr flexbox - - - - - -

Test passes if there is a filled green square and no red.

-
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/abspos-autopos-vlr-rtl.htm.png b/test/render/flex/abspos-autopos-vlr-rtl.htm.png deleted file mode 100644 index 21d20656f..000000000 Binary files a/test/render/flex/abspos-autopos-vlr-rtl.htm.png and /dev/null differ diff --git a/test/render/flex/abspos-autopos-vrl-ltr.htm b/test/render/flex/abspos-autopos-vrl-ltr.htm deleted file mode 100644 index 8987cea54..000000000 --- a/test/render/flex/abspos-autopos-vrl-ltr.htm +++ /dev/null @@ -1,39 +0,0 @@ - - - -Absolutely positioned child with auto position in vertical-rl ltr flexbox - - - - - -

Test passes if there is a filled green square and no red.

-
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/abspos-autopos-vrl-ltr.htm.png b/test/render/flex/abspos-autopos-vrl-ltr.htm.png deleted file mode 100644 index 21d20656f..000000000 Binary files a/test/render/flex/abspos-autopos-vrl-ltr.htm.png and /dev/null differ diff --git a/test/render/flex/abspos-autopos-vrl-rtl.htm b/test/render/flex/abspos-autopos-vrl-rtl.htm deleted file mode 100644 index cb46539dd..000000000 --- a/test/render/flex/abspos-autopos-vrl-rtl.htm +++ /dev/null @@ -1,39 +0,0 @@ - - - -Absolutely positioned child with auto position in vertical-rl ltr flexbox - - - - - -

Test passes if there is a filled green square and no red.

-
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/abspos-autopos-vrl-rtl.htm.png b/test/render/flex/abspos-autopos-vrl-rtl.htm.png deleted file mode 100644 index 21d20656f..000000000 Binary files a/test/render/flex/abspos-autopos-vrl-rtl.htm.png and /dev/null differ diff --git a/test/render/flex/align-baseline.htm b/test/render/flex/align-baseline.htm deleted file mode 100644 index 85db4d717..000000000 --- a/test/render/flex/align-baseline.htm +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - -
-

This text

-

should be left aligned.

-
- -
-

This text

-

should be right aligned.

-
- - - - - - \ No newline at end of file diff --git a/test/render/flex/align-baseline.htm.png b/test/render/flex/align-baseline.htm.png deleted file mode 100644 index 360c0ad17..000000000 Binary files a/test/render/flex/align-baseline.htm.png and /dev/null differ diff --git a/test/render/flex/align-content-006.htm b/test/render/flex/align-content-006.htm deleted file mode 100644 index 81e279664..000000000 --- a/test/render/flex/align-content-006.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - - - CSS Test: A multi-line flex container with the 'align-content' property set to 'stretch' - - - - - - - - -

Test passes if there is no red visible on the page.

-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/align-content-006.htm.png b/test/render/flex/align-content-006.htm.png deleted file mode 100644 index b5d9e9910..000000000 Binary files a/test/render/flex/align-content-006.htm.png and /dev/null differ diff --git a/test/render/flex/align-content_center.htm b/test/render/flex/align-content_center.htm deleted file mode 100644 index 8bf9ba678..000000000 --- a/test/render/flex/align-content_center.htm +++ /dev/null @@ -1,31 +0,0 @@ - - - - - CSS Flexible Box Test: align-content_center - - - - - - - - - -

Test passes if:
- 1. the rectangle 1, 2, 3 show up in a vertical column in a red rectangle and no gap between them.
- 2. the rectangle 1, 2, 3 appear in middle left of red rectangle.

-
1
2
3
- - - - - diff --git a/test/render/flex/align-content_center.htm.png b/test/render/flex/align-content_center.htm.png deleted file mode 100644 index e278bf455..000000000 Binary files a/test/render/flex/align-content_center.htm.png and /dev/null differ diff --git a/test/render/flex/align-content_flex-end.htm b/test/render/flex/align-content_flex-end.htm deleted file mode 100644 index 4b55b24eb..000000000 --- a/test/render/flex/align-content_flex-end.htm +++ /dev/null @@ -1,31 +0,0 @@ - - - - - CSS Flexible Box Test: align-content_flex-end - - - - - - - - - -

Test passes if:
- 1. the rectangle 1, 2, 3 show up in a vertical column in a red rectangle and no gap between them.
- 2. the rectangle 1, 2, 3 appear in bottom left of red rectangle.

-
1
2
3
- - - - - diff --git a/test/render/flex/align-content_flex-end.htm.png b/test/render/flex/align-content_flex-end.htm.png deleted file mode 100644 index 4da94059e..000000000 Binary files a/test/render/flex/align-content_flex-end.htm.png and /dev/null differ diff --git a/test/render/flex/align-content_flex-start.htm b/test/render/flex/align-content_flex-start.htm deleted file mode 100644 index eaf4ff0cb..000000000 --- a/test/render/flex/align-content_flex-start.htm +++ /dev/null @@ -1,32 +0,0 @@ - - - - - CSS Flexible Box Test: align-content_flex-start - - - - - - - - - -

Test passes if:
- 1. the rectangle 1, 2, 3 show up in a vertical column in a red rectangle and no gap between them.
- 2. the rectangle 1, 2, 3 appear in upper left of red rectangle.

-
-
1
2
3
- - - - - diff --git a/test/render/flex/align-content_flex-start.htm.png b/test/render/flex/align-content_flex-start.htm.png deleted file mode 100644 index f0368ef0a..000000000 Binary files a/test/render/flex/align-content_flex-start.htm.png and /dev/null differ diff --git a/test/render/flex/align-content_space-around.htm b/test/render/flex/align-content_space-around.htm deleted file mode 100644 index ece853a59..000000000 --- a/test/render/flex/align-content_space-around.htm +++ /dev/null @@ -1,31 +0,0 @@ - - - - - CSS Flexible Box Test: align-content_space-around - - - - - - - - - -

Test passes if:
- 1. the rectangle 1, 2, 3 show up in a vertical column in a red rectangle.
- 2. the rectangle 1, 2, 3 are distributed such that the empty space between any two adjacent rectangle is the same, and the empty space of the column before the first and after the last rectangle are half the size of the other empty spaces.

-
1
2
3
- - - - - diff --git a/test/render/flex/align-content_space-around.htm.png b/test/render/flex/align-content_space-around.htm.png deleted file mode 100644 index 704ecfefd..000000000 Binary files a/test/render/flex/align-content_space-around.htm.png and /dev/null differ diff --git a/test/render/flex/align-content_space-between.htm b/test/render/flex/align-content_space-between.htm deleted file mode 100644 index da23b9ee2..000000000 --- a/test/render/flex/align-content_space-between.htm +++ /dev/null @@ -1,31 +0,0 @@ - - - - - CSS Flexible Box Test: align-content_space-between - - - - - - - - - -

Test passes if:
- 1. the rectangle 1, 2, 3 show up in a vertical column in a red rectangle.
- 2. No gap between the top of red rectangle and the top of rectangle 1, no gap between the bottom of red rectangle and the bottom of rectangle 3 too, and rectangle 2 is distributed so that the empty space between rectangle 1 and rectangle 3 is the same.

-
1
2
3
- - - - - diff --git a/test/render/flex/align-content_space-between.htm.png b/test/render/flex/align-content_space-between.htm.png deleted file mode 100644 index c1b913418..000000000 Binary files a/test/render/flex/align-content_space-between.htm.png and /dev/null differ diff --git a/test/render/flex/align-content_stretch.htm b/test/render/flex/align-content_stretch.htm deleted file mode 100644 index c7380ab4f..000000000 --- a/test/render/flex/align-content_stretch.htm +++ /dev/null @@ -1,31 +0,0 @@ - - - - - CSS Flexible Box Test: align-content_stretch - - - - - - - - - -

Test passes if:
- 1. the rectangle 1, 2, 3 show up in a vertical column in a red rectangle.
- 2. No gap between the top of red rectangle and the top of rectangle 1, and rectangle 1 , 2, 3 are distributed so that the empty space in the column between 1 , 2 , 3 is the same. -

1
2
3
- - - - - \ No newline at end of file diff --git a/test/render/flex/align-content_stretch.htm.png b/test/render/flex/align-content_stretch.htm.png deleted file mode 100644 index d8487e454..000000000 Binary files a/test/render/flex/align-content_stretch.htm.png and /dev/null differ diff --git a/test/render/flex/align-items-004.htm.png b/test/render/flex/align-items-004.htm.png deleted file mode 100644 index b5d9e9910..000000000 Binary files a/test/render/flex/align-items-004.htm.png and /dev/null differ diff --git a/test/render/flex/align-items-005.htm b/test/render/flex/align-items-005.htm deleted file mode 100644 index 75524b9a1..000000000 --- a/test/render/flex/align-items-005.htm +++ /dev/null @@ -1,39 +0,0 @@ - - - - - CSS Test: A flex container with the 'align-items' property set to 'stretch' - - - - - - - - - -

Test passes if there is no red visible on the page.

-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/align-items-005.htm.png b/test/render/flex/align-items-005.htm.png deleted file mode 100644 index b5d9e9910..000000000 Binary files a/test/render/flex/align-items-005.htm.png and /dev/null differ diff --git a/test/render/flex/align-items-006.htm b/test/render/flex/align-items-006.htm deleted file mode 100644 index 7dd0bb8b8..000000000 --- a/test/render/flex/align-items-006.htm +++ /dev/null @@ -1,42 +0,0 @@ - - - - - CSS Test: A flex container with 'column' direction and 'align-items' property set to 'flex-start' - - - - - - - - - -

Test passes if there is no red visible on the page.

-
-
-
-
-
XXXX
-
- - - - - diff --git a/test/render/flex/align-items-006.htm.png b/test/render/flex/align-items-006.htm.png deleted file mode 100644 index b5d9e9910..000000000 Binary files a/test/render/flex/align-items-006.htm.png and /dev/null differ diff --git a/test/render/flex/align-self-001.htm b/test/render/flex/align-self-001.htm deleted file mode 100644 index 00f3a63e5..000000000 --- a/test/render/flex/align-self-001.htm +++ /dev/null @@ -1,45 +0,0 @@ - - - - -CSS Flexbox Test: align-self - flex-start - - - - - - - - - -

Test passes if there is a filled green square and no red.

-
-
-
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/align-self-001.htm.png b/test/render/flex/align-self-001.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/align-self-001.htm.png and /dev/null differ diff --git a/test/render/flex/align-self-002.htm b/test/render/flex/align-self-002.htm deleted file mode 100644 index 1b7fca208..000000000 --- a/test/render/flex/align-self-002.htm +++ /dev/null @@ -1,45 +0,0 @@ - - - - -CSS Flexbox Test: align-self - flex-end - - - - - - - - - -

Test passes if there is a filled green square and no red.

-
-
-
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/align-self-002.htm.png b/test/render/flex/align-self-002.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/align-self-002.htm.png and /dev/null differ diff --git a/test/render/flex/align-self-003.htm b/test/render/flex/align-self-003.htm deleted file mode 100644 index b984225a4..000000000 --- a/test/render/flex/align-self-003.htm +++ /dev/null @@ -1,52 +0,0 @@ - - - - -CSS Flexbox Test: align-self - center - - - - - - - - - -

Test passes if there is a filled green square and no red.

-
-
-
-
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/align-self-003.htm.png b/test/render/flex/align-self-003.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/align-self-003.htm.png and /dev/null differ diff --git a/test/render/flex/align-self-004.htm b/test/render/flex/align-self-004.htm deleted file mode 100644 index a4bde718a..000000000 --- a/test/render/flex/align-self-004.htm +++ /dev/null @@ -1,39 +0,0 @@ - - - - -CSS Flexbox Test: align-self - stretch - - - - - - - - - -

Test passes if there is a filled green square and no red.

-
-
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/align-self-004.htm.png b/test/render/flex/align-self-004.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/align-self-004.htm.png and /dev/null differ diff --git a/test/render/flex/align-self-005.htm b/test/render/flex/align-self-005.htm deleted file mode 100644 index c2624e089..000000000 --- a/test/render/flex/align-self-005.htm +++ /dev/null @@ -1,46 +0,0 @@ - - - - -CSS Flexbox Test: align-self - stretch (height: number) - - - - - - - - - -

Test passes if there is a filled green square and no red.

-
-
-
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/align-self-005.htm.png b/test/render/flex/align-self-005.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/align-self-005.htm.png and /dev/null differ diff --git a/test/render/flex/align-self-006.htm b/test/render/flex/align-self-006.htm deleted file mode 100644 index 439048881..000000000 --- a/test/render/flex/align-self-006.htm +++ /dev/null @@ -1,52 +0,0 @@ - - - - -CSS Flexbox Test: align-self - baseline - - - - - - - - -

Test passes if the underline of all 'a' characters within black border box is horizontal and no breaking.

-
- - - - -
- - - - \ No newline at end of file diff --git a/test/render/flex/align-self-006.htm.png b/test/render/flex/align-self-006.htm.png deleted file mode 100644 index e877c3605..000000000 Binary files a/test/render/flex/align-self-006.htm.png and /dev/null differ diff --git a/test/render/flex/align-self-007.htm b/test/render/flex/align-self-007.htm deleted file mode 100644 index 01cd936d7..000000000 --- a/test/render/flex/align-self-007.htm +++ /dev/null @@ -1,47 +0,0 @@ - - - - -CSS Flexbox Test: align-self - auto and align-items - flex-start - - - - - - - - - - -

Test passes if there is a filled green square and no red.

-
-
-
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/align-self-007.htm.png b/test/render/flex/align-self-007.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/align-self-007.htm.png and /dev/null differ diff --git a/test/render/flex/align-self-008.htm b/test/render/flex/align-self-008.htm deleted file mode 100644 index c9e25efb6..000000000 --- a/test/render/flex/align-self-008.htm +++ /dev/null @@ -1,46 +0,0 @@ - - - - -CSS Flexbox Test: align-self - auto and align-items - flex-end - - - - - - - - - -

Test passes if there is a filled green square and no red.

-
-
-
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/align-self-008.htm.png b/test/render/flex/align-self-008.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/align-self-008.htm.png and /dev/null differ diff --git a/test/render/flex/align-self-009.htm b/test/render/flex/align-self-009.htm deleted file mode 100644 index 10e3fed24..000000000 --- a/test/render/flex/align-self-009.htm +++ /dev/null @@ -1,53 +0,0 @@ - - - - -CSS Flexbox Test: align-self - auto and align-items - center - - - - - - - - - -

Test passes if there is a filled green square and no red.

-
-
-
-
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/align-self-009.htm.png b/test/render/flex/align-self-009.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/align-self-009.htm.png and /dev/null differ diff --git a/test/render/flex/align-self-010.htm b/test/render/flex/align-self-010.htm deleted file mode 100644 index e134f85eb..000000000 --- a/test/render/flex/align-self-010.htm +++ /dev/null @@ -1,53 +0,0 @@ - - - - -CSS Flexbox Test: align-self - auto and align-items - baseline - - - - - - - - -

Test passes if the underline of all 'a' characters within black border box is horizontal and no breaking.

-
- - - - -
- - - - \ No newline at end of file diff --git a/test/render/flex/align-self-010.htm.png b/test/render/flex/align-self-010.htm.png deleted file mode 100644 index e877c3605..000000000 Binary files a/test/render/flex/align-self-010.htm.png and /dev/null differ diff --git a/test/render/flex/align-self-011.htm b/test/render/flex/align-self-011.htm deleted file mode 100644 index bf6392462..000000000 --- a/test/render/flex/align-self-011.htm +++ /dev/null @@ -1,39 +0,0 @@ - - - - -CSS Flexbox Test: align-self - auto and align-items - stretch - - - - - - - - - -

Test passes if there is a filled green square and no red.

-
-
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/align-self-011.htm.png b/test/render/flex/align-self-011.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/align-self-011.htm.png and /dev/null differ diff --git a/test/render/flex/align-self-012.htm b/test/render/flex/align-self-012.htm deleted file mode 100644 index 373c09cba..000000000 --- a/test/render/flex/align-self-012.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - - -CSS Flexbox Test: align-self - auto (initial value) - - - - - - - - - -

Test passes if there is a filled green square and no red.

-
-
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/align-self-012.htm.png b/test/render/flex/align-self-012.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/align-self-012.htm.png and /dev/null differ diff --git a/test/render/flex/align-self-013.htm b/test/render/flex/align-self-013.htm deleted file mode 100644 index 1933336ba..000000000 --- a/test/render/flex/align-self-013.htm +++ /dev/null @@ -1,46 +0,0 @@ - - - - -CSS Flexbox Test: align-self - invalid if applied to flex container - - - - - - - - - -

Test passes if there is a filled green square and no red.

-
-
-
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/align-self-013.htm.png b/test/render/flex/align-self-013.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/align-self-013.htm.png and /dev/null differ diff --git a/test/render/flex/anonymous-block.htm b/test/render/flex/anonymous-block.htm deleted file mode 100644 index 006c609c6..000000000 --- a/test/render/flex/anonymous-block.htm +++ /dev/null @@ -1,17 +0,0 @@ - - - -CSS Flexbox: anonymous block - - - - - -

This tests that text nodes that have a flexbox as a parent are wrapped in -anonymous blocks.

-
This text should be visible.
- - - - - diff --git a/test/render/flex/anonymous-block.htm.png b/test/render/flex/anonymous-block.htm.png deleted file mode 100644 index a004783c3..000000000 Binary files a/test/render/flex/anonymous-block.htm.png and /dev/null differ diff --git a/test/render/flex/auto-height-column-with-border-and-padding.htm b/test/render/flex/auto-height-column-with-border-and-padding.htm deleted file mode 100644 index 826ded447..000000000 --- a/test/render/flex/auto-height-column-with-border-and-padding.htm +++ /dev/null @@ -1,17 +0,0 @@ - - - -CSS Flexbox: auto-height with border and padding - - - - -Tests that auto-height column flexboxes with border and padding correctly size their height to their content. -
-
-
-
-
-
- - diff --git a/test/render/flex/auto-height-column-with-border-and-padding.htm.png b/test/render/flex/auto-height-column-with-border-and-padding.htm.png deleted file mode 100644 index d06e4fa6d..000000000 Binary files a/test/render/flex/auto-height-column-with-border-and-padding.htm.png and /dev/null differ diff --git a/test/render/flex/auto-height-with-flex.htm b/test/render/flex/auto-height-with-flex.htm deleted file mode 100644 index d2cf59030..000000000 --- a/test/render/flex/auto-height-with-flex.htm +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - -
-
Header
-
Flexible content
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/auto-height-with-flex.htm.png b/test/render/flex/auto-height-with-flex.htm.png deleted file mode 100644 index 5f7be4df3..000000000 Binary files a/test/render/flex/auto-height-with-flex.htm.png and /dev/null differ diff --git a/test/render/flex/contain-layout-baseline-002.htm.png b/test/render/flex/contain-layout-baseline-002.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/contain-layout-baseline-002.htm.png and /dev/null differ diff --git a/test/render/flex/css-box-justify-content.htm b/test/render/flex/css-box-justify-content.htm deleted file mode 100644 index 4325e9263..000000000 --- a/test/render/flex/css-box-justify-content.htm +++ /dev/null @@ -1,38 +0,0 @@ - - - -flexbox |css-box-justify-content - - - - - - -

This test passes if the DIV5's position in the end and the div is Horizontal layout

-
-
DIV1
-   -
DIV2
-   -
DIV3
-   -
DIV4
-   -
DIV5
-
- - - \ No newline at end of file diff --git a/test/render/flex/css-box-justify-content.htm.png b/test/render/flex/css-box-justify-content.htm.png deleted file mode 100644 index 12d5e1e6c..000000000 Binary files a/test/render/flex/css-box-justify-content.htm.png and /dev/null differ diff --git a/test/render/flex/display-flex-001.htm b/test/render/flex/display-flex-001.htm deleted file mode 100644 index 06bdbcd98..000000000 --- a/test/render/flex/display-flex-001.htm +++ /dev/null @@ -1,36 +0,0 @@ - - - - - CSS Test: An element with the 'display' property set to 'flex' establishes a new block-level flex container - - - - - - - - -

Test passes if there is no red visible on the page.

-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/display-flex-001.htm.png b/test/render/flex/display-flex-001.htm.png deleted file mode 100644 index b5d9e9910..000000000 Binary files a/test/render/flex/display-flex-001.htm.png and /dev/null differ diff --git a/test/render/flex/flex-001.htm b/test/render/flex/flex-001.htm deleted file mode 100644 index a605fc148..000000000 --- a/test/render/flex/flex-001.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - - - CSS Test: The 'flex' shorthand adjusting the 'flex-grow' sub-property - - - - - - - - -

Test passes if there is a single blue rectangle on the left, a single orange rectangle directly to its right, and there is no red visible on the page.

-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flex-001.htm.png b/test/render/flex/flex-001.htm.png deleted file mode 100644 index 156126b1f..000000000 Binary files a/test/render/flex/flex-001.htm.png and /dev/null differ diff --git a/test/render/flex/flex-002.htm b/test/render/flex/flex-002.htm deleted file mode 100644 index dd5406c01..000000000 --- a/test/render/flex/flex-002.htm +++ /dev/null @@ -1,57 +0,0 @@ - - - - - CSS Test: The 'flex' shorthand adjusting the 'flex-shrink' sub-property - - - - - - - - -

Test passes if there is a single blue rectangle on the left, a single orange rectangle directly to its right, and there is no red visible on the page.

-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flex-002.htm.png b/test/render/flex/flex-002.htm.png deleted file mode 100644 index 156126b1f..000000000 Binary files a/test/render/flex/flex-002.htm.png and /dev/null differ diff --git a/test/render/flex/flex-003.htm b/test/render/flex/flex-003.htm deleted file mode 100644 index 7a31f350b..000000000 --- a/test/render/flex/flex-003.htm +++ /dev/null @@ -1,58 +0,0 @@ - - - - - CSS Test: Comparing two different elements using different values for the 'flex-grow' sub-property on the 'flex' shorthand - - - - - - - - -

Test passes if there is a single blue rectangle on the left, a single orange rectangle directly to its right, and there is no red visible on the page.

-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flex-003.htm.png b/test/render/flex/flex-003.htm.png deleted file mode 100644 index 156126b1f..000000000 Binary files a/test/render/flex/flex-003.htm.png and /dev/null differ diff --git a/test/render/flex/flex-004.htm b/test/render/flex/flex-004.htm deleted file mode 100644 index a190d70fd..000000000 --- a/test/render/flex/flex-004.htm +++ /dev/null @@ -1,58 +0,0 @@ - - - - - CSS Test: Comparing two different elements using different values for the 'flex-shrink' sub-property on the 'flex' shorthand - - - - - - - - -

Test passes if there is a single blue rectangle on the left, a single orange rectangle directly to its right, and there is no red visible on the page.

-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flex-004.htm.png b/test/render/flex/flex-004.htm.png deleted file mode 100644 index 156126b1f..000000000 Binary files a/test/render/flex/flex-004.htm.png and /dev/null differ diff --git a/test/render/flex/flex-align-content-center.htm b/test/render/flex/flex-align-content-center.htm deleted file mode 100644 index 53aad11a9..000000000 --- a/test/render/flex/flex-align-content-center.htm +++ /dev/null @@ -1,45 +0,0 @@ - - - - - CSS Flexible Box Test: align-content property - center - - - - - - - -

The test passed if you see a centered 2*2 table.

-
- first - second - third - forth -
- - - - - \ No newline at end of file diff --git a/test/render/flex/flex-align-content-center.htm.png b/test/render/flex/flex-align-content-center.htm.png deleted file mode 100644 index 8e3aa9a61..000000000 Binary files a/test/render/flex/flex-align-content-center.htm.png and /dev/null differ diff --git a/test/render/flex/flex-align-content-end.htm b/test/render/flex/flex-align-content-end.htm deleted file mode 100644 index a01e6f069..000000000 --- a/test/render/flex/flex-align-content-end.htm +++ /dev/null @@ -1,45 +0,0 @@ - - - - - CSS Flexible Box Test: align-content property - flex-end - - - - - - - -

The test passed if you see a 2*2 table and all the cells are at the bottom of container.

-
- first - second - third - forth -
- - - - - \ No newline at end of file diff --git a/test/render/flex/flex-align-content-end.htm.png b/test/render/flex/flex-align-content-end.htm.png deleted file mode 100644 index 6d023c546..000000000 Binary files a/test/render/flex/flex-align-content-end.htm.png and /dev/null differ diff --git a/test/render/flex/flex-align-content-space-around.htm b/test/render/flex/flex-align-content-space-around.htm deleted file mode 100644 index 542fa5cbc..000000000 --- a/test/render/flex/flex-align-content-space-around.htm +++ /dev/null @@ -1,45 +0,0 @@ - - - - - CSS Flexible Box Test: align-content property - space-around - - - - - - - -

The test passed if you see a 2*2 table and all the cells are at the bottom of container.

-
- first - second - third - forth -
- - - - - \ No newline at end of file diff --git a/test/render/flex/flex-align-content-space-around.htm.png b/test/render/flex/flex-align-content-space-around.htm.png deleted file mode 100644 index f0d2c297d..000000000 Binary files a/test/render/flex/flex-align-content-space-around.htm.png and /dev/null differ diff --git a/test/render/flex/flex-align-content-space-between.htm b/test/render/flex/flex-align-content-space-between.htm deleted file mode 100644 index eff222a33..000000000 --- a/test/render/flex/flex-align-content-space-between.htm +++ /dev/null @@ -1,45 +0,0 @@ - - - - - CSS Flexible Box Test: align-content property - space-between - - - - - - - -

The test passed if you see a 2*2 table and all the cells are spaced equally apart.

-
- first - second - third - forth -
- - - - - \ No newline at end of file diff --git a/test/render/flex/flex-align-content-space-between.htm.png b/test/render/flex/flex-align-content-space-between.htm.png deleted file mode 100644 index cca50c4be..000000000 Binary files a/test/render/flex/flex-align-content-space-between.htm.png and /dev/null differ diff --git a/test/render/flex/flex-align-content-start.htm b/test/render/flex/flex-align-content-start.htm deleted file mode 100644 index 4df16c45a..000000000 --- a/test/render/flex/flex-align-content-start.htm +++ /dev/null @@ -1,45 +0,0 @@ - - - - - CSS Flexible Box Test: align-content property - flex-start - - - - - - - -

The test passed if you see a 2*2 table and all the cells are at the top of the container.

-
- first - second - third - forth -
- - - - - \ No newline at end of file diff --git a/test/render/flex/flex-align-content-start.htm.png b/test/render/flex/flex-align-content-start.htm.png deleted file mode 100644 index 43f61cfd2..000000000 Binary files a/test/render/flex/flex-align-content-start.htm.png and /dev/null differ diff --git a/test/render/flex/flex-base.htm b/test/render/flex/flex-base.htm deleted file mode 100644 index 2819dd25c..000000000 --- a/test/render/flex/flex-base.htm +++ /dev/null @@ -1,35 +0,0 @@ - - - - - CSS Flexible Box Test: display proprety - flex - - - - - - - -

The test passed if you can't find red color.

-
- Hello -
- - - - - \ No newline at end of file diff --git a/test/render/flex/flex-base.htm.png b/test/render/flex/flex-base.htm.png deleted file mode 100644 index cb278f1f1..000000000 Binary files a/test/render/flex/flex-base.htm.png and /dev/null differ diff --git a/test/render/flex/flex-basis-001.htm b/test/render/flex/flex-basis-001.htm deleted file mode 100644 index d04b23288..000000000 --- a/test/render/flex/flex-basis-001.htm +++ /dev/null @@ -1,39 +0,0 @@ - - - - -CSS Flexbox Test: flex-basis - positive number - - - - - - - - -

Test passes if there is a filled green square and no red.

-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flex-basis-001.htm.png b/test/render/flex/flex-basis-001.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/flex-basis-001.htm.png and /dev/null differ diff --git a/test/render/flex/flex-basis-002.htm b/test/render/flex/flex-basis-002.htm deleted file mode 100644 index b12a8305f..000000000 --- a/test/render/flex/flex-basis-002.htm +++ /dev/null @@ -1,47 +0,0 @@ - - - - -CSS Flexbox Test: flex-basis - positive number - - - - - - - - -

Test passes if there is a filled green square and no red.

-
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flex-basis-002.htm.png b/test/render/flex/flex-basis-002.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/flex-basis-002.htm.png and /dev/null differ diff --git a/test/render/flex/flex-basis-003.htm b/test/render/flex/flex-basis-003.htm deleted file mode 100644 index 7a2ba7a7b..000000000 --- a/test/render/flex/flex-basis-003.htm +++ /dev/null @@ -1,47 +0,0 @@ - - - - -CSS Flexbox Test: flex-basis - negative number(width not specified) - - - - - - - - -

Test passes if there is a filled green square and no red.

-
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flex-basis-003.htm.png b/test/render/flex/flex-basis-003.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/flex-basis-003.htm.png and /dev/null differ diff --git a/test/render/flex/flex-basis-004.htm b/test/render/flex/flex-basis-004.htm deleted file mode 100644 index af773c2f0..000000000 --- a/test/render/flex/flex-basis-004.htm +++ /dev/null @@ -1,49 +0,0 @@ - - - - -CSS Flexbox Test: flex-basis - negative number(width specified) - - - - - - - - -

Test passes if there is a filled green square and no red.

-
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flex-basis-004.htm.png b/test/render/flex/flex-basis-004.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/flex-basis-004.htm.png and /dev/null differ diff --git a/test/render/flex/flex-basis-005.htm b/test/render/flex/flex-basis-005.htm deleted file mode 100644 index 35e050896..000000000 --- a/test/render/flex/flex-basis-005.htm +++ /dev/null @@ -1,34 +0,0 @@ - - - - -CSS Flexbox Test: flex-basis - 0 - - - - - - - - -

Test passes if there is a filled green square and no red.

-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flex-basis-005.htm.png b/test/render/flex/flex-basis-005.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/flex-basis-005.htm.png and /dev/null differ diff --git a/test/render/flex/flex-basis-006.htm b/test/render/flex/flex-basis-006.htm deleted file mode 100644 index 3c8de53f1..000000000 --- a/test/render/flex/flex-basis-006.htm +++ /dev/null @@ -1,34 +0,0 @@ - - - - -CSS Flexbox Test: flex-basis - 0% - - - - - - - - -

Test passes if there is a filled green square and no red.

-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flex-basis-006.htm.png b/test/render/flex/flex-basis-006.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/flex-basis-006.htm.png and /dev/null differ diff --git a/test/render/flex/flex-basis-007.htm b/test/render/flex/flex-basis-007.htm deleted file mode 100644 index ea3387976..000000000 --- a/test/render/flex/flex-basis-007.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - - -CSS Flexbox Test: flex-basis - auto - - - - - - - - -

Test passes if there is a filled green square and no red.

-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flex-basis-007.htm.png b/test/render/flex/flex-basis-007.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/flex-basis-007.htm.png and /dev/null differ diff --git a/test/render/flex/flex-basis-008.htm b/test/render/flex/flex-basis-008.htm deleted file mode 100644 index d85669a9d..000000000 --- a/test/render/flex/flex-basis-008.htm +++ /dev/null @@ -1,39 +0,0 @@ - - - - -CSS Flexbox Test: flex-basis - 50% - - - - - - - - -

Test passes if there is a filled green square and no red.

-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flex-basis-008.htm.png b/test/render/flex/flex-basis-008.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/flex-basis-008.htm.png and /dev/null differ diff --git a/test/render/flex/flex-basis-010.htm b/test/render/flex/flex-basis-010.htm deleted file mode 100644 index ba82b33a5..000000000 --- a/test/render/flex/flex-basis-010.htm +++ /dev/null @@ -1,36 +0,0 @@ - - - - -CSS Flexbox Test: Indefinite % flex-basis should cause height to be ignored - - - - - -

Test passes if there is a filled green square and no red.

-
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flex-basis-010.htm.png b/test/render/flex/flex-basis-010.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/flex-basis-010.htm.png and /dev/null differ diff --git a/test/render/flex/flex-basis-011.htm b/test/render/flex/flex-basis-011.htm deleted file mode 100644 index d7e98c2ab..000000000 --- a/test/render/flex/flex-basis-011.htm +++ /dev/null @@ -1,30 +0,0 @@ - - - - -CSS Flexbox Test: % flex-basis should not cause engines to treat items as percentage sized - - - - - - -

Test PASS if there are two boxes with blue borders vertically stretched to fit their contents.

-
-
-
-
AAA
-
-
-
BBB
-
-
-
- - - diff --git a/test/render/flex/flex-basis-011.htm.png b/test/render/flex/flex-basis-011.htm.png deleted file mode 100644 index 618758e92..000000000 Binary files a/test/render/flex/flex-basis-011.htm.png and /dev/null differ diff --git a/test/render/flex/flex-box-wrap.htm b/test/render/flex/flex-box-wrap.htm deleted file mode 100644 index 17305b23c..000000000 --- a/test/render/flex/flex-box-wrap.htm +++ /dev/null @@ -1,51 +0,0 @@ - - - - - CSS Flexbox Test: flex-wrap: wrap - - - - - - - - -

There should be a green block with no red.

- -
    -
  • width: 120px
  • -
  • width: 120px
  • -
  • -
- - - - - - \ No newline at end of file diff --git a/test/render/flex/flex-box-wrap.htm.png b/test/render/flex/flex-box-wrap.htm.png deleted file mode 100644 index c40d31f86..000000000 Binary files a/test/render/flex/flex-box-wrap.htm.png and /dev/null differ diff --git a/test/render/flex/flex-container-margin.htm b/test/render/flex/flex-container-margin.htm deleted file mode 100644 index 0c43b2db0..000000000 --- a/test/render/flex/flex-container-margin.htm +++ /dev/null @@ -1,34 +0,0 @@ - - - - -CSS Test: flex-container-margin-not-collapse-with-content-margin - - - - - - - -
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flex-container-margin.htm.png b/test/render/flex/flex-container-margin.htm.png deleted file mode 100644 index c05913f93..000000000 Binary files a/test/render/flex/flex-container-margin.htm.png and /dev/null differ diff --git a/test/render/flex/flex-direction-column-001-visual.htm b/test/render/flex/flex-direction-column-001-visual.htm deleted file mode 100644 index fdbfd84bb..000000000 --- a/test/render/flex/flex-direction-column-001-visual.htm +++ /dev/null @@ -1,29 +0,0 @@ - - - - - CSS Flexible Box Test: flex-direction_column - - - - - - - - -

Test passes if:
- 1. the rectangle 1, 2, 3 show up in a vertical column in a red rectangle and no gap between them.
- 2. They are all appear in upper left of the red rectangle.

-
1
2
3
- - - - - \ No newline at end of file diff --git a/test/render/flex/flex-direction-column-001-visual.htm.png b/test/render/flex/flex-direction-column-001-visual.htm.png deleted file mode 100644 index 0cc9c6207..000000000 Binary files a/test/render/flex/flex-direction-column-001-visual.htm.png and /dev/null differ diff --git a/test/render/flex/flex-direction-column-reverse-001-visual.htm b/test/render/flex/flex-direction-column-reverse-001-visual.htm deleted file mode 100644 index 2d77d6666..000000000 --- a/test/render/flex/flex-direction-column-reverse-001-visual.htm +++ /dev/null @@ -1,29 +0,0 @@ - - - - - CSS Flexible Box Test: flex-direction_column-reverse - - - - - - - - -

Test passes if:
- 1. the rectangle 1, 2, 3 show up in a vertical column in a red rectangle and no gap between them.
- 2. the rectangle 1, 2, 3 appear in bottom left of red rectangle and from top to bottom of the column: 3, 2, 1.

-
1
2
3
- - - - - \ No newline at end of file diff --git a/test/render/flex/flex-direction-column-reverse-001-visual.htm.png b/test/render/flex/flex-direction-column-reverse-001-visual.htm.png deleted file mode 100644 index 0790dcc73..000000000 Binary files a/test/render/flex/flex-direction-column-reverse-001-visual.htm.png and /dev/null differ diff --git a/test/render/flex/flex-direction-column-reverse-002-visual.htm b/test/render/flex/flex-direction-column-reverse-002-visual.htm deleted file mode 100644 index 09ce44c44..000000000 --- a/test/render/flex/flex-direction-column-reverse-002-visual.htm +++ /dev/null @@ -1,43 +0,0 @@ - - - - - CSS Test: flex-direction: column-reverse swaps main start and end directions - - - - - - - - -

Test passes if both the two columns below are identical.

-
- ABC -
-
- CBA -
- - - - - \ No newline at end of file diff --git a/test/render/flex/flex-direction-column-reverse-002-visual.htm.png b/test/render/flex/flex-direction-column-reverse-002-visual.htm.png deleted file mode 100644 index 5ce25dfea..000000000 Binary files a/test/render/flex/flex-direction-column-reverse-002-visual.htm.png and /dev/null differ diff --git a/test/render/flex/flex-direction-column-reverse.htm b/test/render/flex/flex-direction-column-reverse.htm deleted file mode 100644 index a251d1c57..000000000 --- a/test/render/flex/flex-direction-column-reverse.htm +++ /dev/null @@ -1,42 +0,0 @@ - - - - - CSS Flexible Box Test: flex-direction proprety - column-reverse - - - - - - - -

The test passed if you see all the cells are arraged vertically and reversed.

-
- first - second - third - forth -
- - - - - \ No newline at end of file diff --git a/test/render/flex/flex-direction-column-reverse.htm.png b/test/render/flex/flex-direction-column-reverse.htm.png deleted file mode 100644 index c6f43f235..000000000 Binary files a/test/render/flex/flex-direction-column-reverse.htm.png and /dev/null differ diff --git a/test/render/flex/flex-direction-column.htm b/test/render/flex/flex-direction-column.htm deleted file mode 100644 index fb108514a..000000000 --- a/test/render/flex/flex-direction-column.htm +++ /dev/null @@ -1,42 +0,0 @@ - - - - - CSS Flexible Box Test: flex-direction proprety - column - - - - - - - -

The test passed if you see all the cells are arraged vertically.

-
- first - second - third - forth -
- - - - - \ No newline at end of file diff --git a/test/render/flex/flex-direction-column.htm.png b/test/render/flex/flex-direction-column.htm.png deleted file mode 100644 index ed9a5315c..000000000 Binary files a/test/render/flex/flex-direction-column.htm.png and /dev/null differ diff --git a/test/render/flex/flex-direction-row-001-visual.htm b/test/render/flex/flex-direction-row-001-visual.htm deleted file mode 100644 index 9347b13cf..000000000 --- a/test/render/flex/flex-direction-row-001-visual.htm +++ /dev/null @@ -1,29 +0,0 @@ - - - - - CSS Flexible Box Test: flex-direction_row - - - - - - - - -

Test passes if:
- 1. the rectangle 1, 2, 3 show up in a row in a red rectangle and no gap between them.
- 2. the rectangle 1, 2, 3 appear in upper left of red rectangle.

-
1
2
3
- - - - - \ No newline at end of file diff --git a/test/render/flex/flex-direction-row-001-visual.htm.png b/test/render/flex/flex-direction-row-001-visual.htm.png deleted file mode 100644 index a1cd17629..000000000 Binary files a/test/render/flex/flex-direction-row-001-visual.htm.png and /dev/null differ diff --git a/test/render/flex/flex-direction-row-reverse-001-visual.htm b/test/render/flex/flex-direction-row-reverse-001-visual.htm deleted file mode 100644 index da02934a0..000000000 --- a/test/render/flex/flex-direction-row-reverse-001-visual.htm +++ /dev/null @@ -1,29 +0,0 @@ - - - - - CSS Flexible Box Test: flex-direction_row-reverse - - - - - - - - -

Test passes if:
- 1. the rectangle 1, 2, 3 show up in a row in a red rectangle and no gap between them.
- 2. the rectangle 1, 2, 3 appear in upper right of red rectangle and from left to right of the row: 3, 2, 1.

-
1
2
3
- - - - - \ No newline at end of file diff --git a/test/render/flex/flex-direction-row-reverse-001-visual.htm.png b/test/render/flex/flex-direction-row-reverse-001-visual.htm.png deleted file mode 100644 index d60d26826..000000000 Binary files a/test/render/flex/flex-direction-row-reverse-001-visual.htm.png and /dev/null differ diff --git a/test/render/flex/flex-direction-row-reverse-002-visual.htm b/test/render/flex/flex-direction-row-reverse-002-visual.htm deleted file mode 100644 index 222b3983b..000000000 --- a/test/render/flex/flex-direction-row-reverse-002-visual.htm +++ /dev/null @@ -1,42 +0,0 @@ - - - - - CSS Test: flow-direction:row-reverse swaps main start and end directions - - - - - - - - -

Test passes if both the lines below are identical.

-
- ABC -
-
- CBA -
- - - - - \ No newline at end of file diff --git a/test/render/flex/flex-direction-row-reverse-002-visual.htm.png b/test/render/flex/flex-direction-row-reverse-002-visual.htm.png deleted file mode 100644 index 301ee0d44..000000000 Binary files a/test/render/flex/flex-direction-row-reverse-002-visual.htm.png and /dev/null differ diff --git a/test/render/flex/flex-direction-row-reverse.htm b/test/render/flex/flex-direction-row-reverse.htm deleted file mode 100644 index 9f3e8b9fc..000000000 --- a/test/render/flex/flex-direction-row-reverse.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - - - CSS Flexible Box Test: flex-direction proprety - row-reverse - - - - - - - -

The test passed if you see all the cells are arranged horizontally and the order of cells are reversed.

-
- first - second - third - forth -
- - - - - \ No newline at end of file diff --git a/test/render/flex/flex-direction-row-reverse.htm.png b/test/render/flex/flex-direction-row-reverse.htm.png deleted file mode 100644 index a066389b5..000000000 Binary files a/test/render/flex/flex-direction-row-reverse.htm.png and /dev/null differ diff --git a/test/render/flex/flex-direction.htm b/test/render/flex/flex-direction.htm deleted file mode 100644 index f69174012..000000000 --- a/test/render/flex/flex-direction.htm +++ /dev/null @@ -1,58 +0,0 @@ - - - - -CSS Test: flex flow direction - - - - - - - -

flex-direction:row

-
1
2
3
- -

flex-direction:row-reverse

-
1
2
3
- -

flex-direction:column

-
1
2
3
- -

flex-direction:column-reverse

-
1
2
3
- - - - - \ No newline at end of file diff --git a/test/render/flex/flex-direction.htm.png b/test/render/flex/flex-direction.htm.png deleted file mode 100644 index b5bdb27a0..000000000 Binary files a/test/render/flex/flex-direction.htm.png and /dev/null differ diff --git a/test/render/flex/flex-flexitem-childmargin.htm b/test/render/flex/flex-flexitem-childmargin.htm deleted file mode 100644 index 5a7b361ba..000000000 --- a/test/render/flex/flex-flexitem-childmargin.htm +++ /dev/null @@ -1,52 +0,0 @@ - - - - - flex item child margin - - - - - - - - -
-
-

- a -

-
-
-

- b -

-
-
- - - - - - \ No newline at end of file diff --git a/test/render/flex/flex-flexitem-childmargin.htm.png b/test/render/flex/flex-flexitem-childmargin.htm.png deleted file mode 100644 index a356877ca..000000000 Binary files a/test/render/flex/flex-flexitem-childmargin.htm.png and /dev/null differ diff --git a/test/render/flex/flex-flexitem-percentage-prescation.htm b/test/render/flex/flex-flexitem-percentage-prescation.htm deleted file mode 100644 index 32881e105..000000000 --- a/test/render/flex/flex-flexitem-percentage-prescation.htm +++ /dev/null @@ -1,37 +0,0 @@ - - - - - flex item size prescation - - - - - - - - -
-

d

-

d

-
- - - - - - \ No newline at end of file diff --git a/test/render/flex/flex-flexitem-percentage-prescation.htm.png b/test/render/flex/flex-flexitem-percentage-prescation.htm.png deleted file mode 100644 index 4b61e2a8b..000000000 Binary files a/test/render/flex/flex-flexitem-percentage-prescation.htm.png and /dev/null differ diff --git a/test/render/flex/flex-flow-001.htm b/test/render/flex/flex-flow-001.htm deleted file mode 100644 index 909e04b6a..000000000 --- a/test/render/flex/flex-flow-001.htm +++ /dev/null @@ -1,39 +0,0 @@ - - - - -CSS Flexbox Test: flex-flow - row nowrap - - - - - - - - - - -

Test passes if there is a filled green rectangle whose width is greater than height - and the number within rectangle is '1 2 3 4' from left to right.

-
-
1
-
2
-
3
-
4
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flex-flow-001.htm.png b/test/render/flex/flex-flow-001.htm.png deleted file mode 100644 index 906103297..000000000 Binary files a/test/render/flex/flex-flow-001.htm.png and /dev/null differ diff --git a/test/render/flex/flex-flow-002.htm b/test/render/flex/flex-flow-002.htm deleted file mode 100644 index 2eb583107..000000000 --- a/test/render/flex/flex-flow-002.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - - -CSS Flexbox Test: flex-flow - row wrap - - - - - - - - - - -

Test passes if there is a filled green square and no red, the number within square is '1 2 3 4' - from left to right, top to bottom.

-
-
1
-
2
-
3
-
4
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flex-flow-002.htm.png b/test/render/flex/flex-flow-002.htm.png deleted file mode 100644 index dd3f391cd..000000000 Binary files a/test/render/flex/flex-flow-002.htm.png and /dev/null differ diff --git a/test/render/flex/flex-flow-003.htm b/test/render/flex/flex-flow-003.htm deleted file mode 100644 index 7a9066a04..000000000 --- a/test/render/flex/flex-flow-003.htm +++ /dev/null @@ -1,41 +0,0 @@ - - - - -CSS Flexbox Test: flex-flow - row wrap-reverse - - - - - - - - - - -

Test passes if there is a filled green square and no red, the number within square is '1 2 3 4' - from left to right, top to bottom.

-
-
3
-
4
-
1
-
2
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flex-flow-003.htm.png b/test/render/flex/flex-flow-003.htm.png deleted file mode 100644 index dd3f391cd..000000000 Binary files a/test/render/flex/flex-flow-003.htm.png and /dev/null differ diff --git a/test/render/flex/flex-flow-004.htm b/test/render/flex/flex-flow-004.htm deleted file mode 100644 index de5919760..000000000 --- a/test/render/flex/flex-flow-004.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - - -CSS Flexbox Test: flex-flow - row-reverse nowrap - - - - - - - - - - -

Test passes if there is a filled green rectangle whose width is greater than height - and the number within rectangle is '1 2 3 4' from left to right.

-
-
4
-
3
-
2
-
1
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flex-flow-004.htm.png b/test/render/flex/flex-flow-004.htm.png deleted file mode 100644 index 906103297..000000000 Binary files a/test/render/flex/flex-flow-004.htm.png and /dev/null differ diff --git a/test/render/flex/flex-flow-005.htm b/test/render/flex/flex-flow-005.htm deleted file mode 100644 index 51f6c6f14..000000000 --- a/test/render/flex/flex-flow-005.htm +++ /dev/null @@ -1,41 +0,0 @@ - - - - -CSS Flexbox Test: flex-flow - row-reverse wrap - - - - - - - - - - -

Test passes if there is a filled green square and no red, the number within square is '1 2 3 4' - from left to right, top to bottom.

-
-
2
-
1
-
4
-
3
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flex-flow-005.htm.png b/test/render/flex/flex-flow-005.htm.png deleted file mode 100644 index dd3f391cd..000000000 Binary files a/test/render/flex/flex-flow-005.htm.png and /dev/null differ diff --git a/test/render/flex/flex-flow-006.htm b/test/render/flex/flex-flow-006.htm deleted file mode 100644 index 8ecbb9761..000000000 --- a/test/render/flex/flex-flow-006.htm +++ /dev/null @@ -1,41 +0,0 @@ - - - - -CSS Flexbox Test: flex-flow - row-reverse wrap-reverse - - - - - - - - - - -

Test passes if there is a filled green square and no red, the number within square is '1 2 3 4' - from left to right, top to bottom.

-
-
4
-
3
-
2
-
1
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flex-flow-006.htm.png b/test/render/flex/flex-flow-006.htm.png deleted file mode 100644 index dd3f391cd..000000000 Binary files a/test/render/flex/flex-flow-006.htm.png and /dev/null differ diff --git a/test/render/flex/flex-flow-007.htm b/test/render/flex/flex-flow-007.htm deleted file mode 100644 index 55487def6..000000000 --- a/test/render/flex/flex-flow-007.htm +++ /dev/null @@ -1,39 +0,0 @@ - - - - -CSS Flexbox Test: flex-flow - column nowrap - - - - - - - - - - -

Test passes if there is a filled green square and no red, the number within square is '1 2 3 4' from top to bottom.

-
-
1
-
2
-
3
-
4
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flex-flow-007.htm.png b/test/render/flex/flex-flow-007.htm.png deleted file mode 100644 index 0b9ed1f42..000000000 Binary files a/test/render/flex/flex-flow-007.htm.png and /dev/null differ diff --git a/test/render/flex/flex-flow-008.htm b/test/render/flex/flex-flow-008.htm deleted file mode 100644 index c1e8c7db2..000000000 --- a/test/render/flex/flex-flow-008.htm +++ /dev/null @@ -1,41 +0,0 @@ - - - - -CSS Flexbox Test: flex-flow - column wrap - - - - - - - - - - -

Test passes if there is a filled green square and no red, the number within square is '1 2 3 4' - from left to right, top to bottom.

-
-
1
-
3
-
2
-
4
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flex-flow-008.htm.png b/test/render/flex/flex-flow-008.htm.png deleted file mode 100644 index dd3f391cd..000000000 Binary files a/test/render/flex/flex-flow-008.htm.png and /dev/null differ diff --git a/test/render/flex/flex-flow-009.htm b/test/render/flex/flex-flow-009.htm deleted file mode 100644 index 5aa71c010..000000000 --- a/test/render/flex/flex-flow-009.htm +++ /dev/null @@ -1,41 +0,0 @@ - - - - -CSS Flexbox Test: flex-flow - column wrap-reverse - - - - - - - - - - -

Test passes if there is a filled green square and no red, the number within square is '1 2 3 4' - from left to right, top to bottom.

-
-
2
-
4
-
1
-
3
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flex-flow-009.htm.png b/test/render/flex/flex-flow-009.htm.png deleted file mode 100644 index dd3f391cd..000000000 Binary files a/test/render/flex/flex-flow-009.htm.png and /dev/null differ diff --git a/test/render/flex/flex-flow-010.htm b/test/render/flex/flex-flow-010.htm deleted file mode 100644 index 3d46f868b..000000000 --- a/test/render/flex/flex-flow-010.htm +++ /dev/null @@ -1,39 +0,0 @@ - - - - -CSS Flexbox Test: flex-flow - column-reverse nowrap - - - - - - - - - - -

Test passes if there is a filled green square and no red, the number within square is '1 2 3 4' from top to bottom.

-
-
4
-
3
-
2
-
1
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flex-flow-010.htm.png b/test/render/flex/flex-flow-010.htm.png deleted file mode 100644 index 0b9ed1f42..000000000 Binary files a/test/render/flex/flex-flow-010.htm.png and /dev/null differ diff --git a/test/render/flex/flex-flow-011.htm b/test/render/flex/flex-flow-011.htm deleted file mode 100644 index d8491c9fa..000000000 --- a/test/render/flex/flex-flow-011.htm +++ /dev/null @@ -1,41 +0,0 @@ - - - - -CSS Flexbox Test: flex-flow - column-reverse wrap - - - - - - - - - - -

Test passes if there is a filled green square and no red, the number within square is '1 2 3 4' - from left to right, top to bottom.

-
-
3
-
1
-
4
-
2
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flex-flow-011.htm.png b/test/render/flex/flex-flow-011.htm.png deleted file mode 100644 index dd3f391cd..000000000 Binary files a/test/render/flex/flex-flow-011.htm.png and /dev/null differ diff --git a/test/render/flex/flex-flow-012.htm b/test/render/flex/flex-flow-012.htm deleted file mode 100644 index 412310a46..000000000 --- a/test/render/flex/flex-flow-012.htm +++ /dev/null @@ -1,41 +0,0 @@ - - - - -CSS Flexbox Test: flex-flow - column-reverse wrap-reverse - - - - - - - - - - -

Test passes if there is a filled green square and no red, the number within square is '1 2 3 4' - from left to right, top to bottom.

-
-
4
-
2
-
3
-
1
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flex-flow-012.htm.png b/test/render/flex/flex-flow-012.htm.png deleted file mode 100644 index dd3f391cd..000000000 Binary files a/test/render/flex/flex-flow-012.htm.png and /dev/null differ diff --git a/test/render/flex/flex-grow-001.htm b/test/render/flex/flex-grow-001.htm deleted file mode 100644 index 37013daa9..000000000 --- a/test/render/flex/flex-grow-001.htm +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - CSS Test: Flex-grow Property of Block-level Flex Items - - - - - - - - -
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flex-grow-001.htm.png b/test/render/flex/flex-grow-001.htm.png deleted file mode 100644 index 03d0ab889..000000000 Binary files a/test/render/flex/flex-grow-001.htm.png and /dev/null differ diff --git a/test/render/flex/flex-grow-002.htm b/test/render/flex/flex-grow-002.htm deleted file mode 100644 index a5ebea4ba..000000000 --- a/test/render/flex/flex-grow-002.htm +++ /dev/null @@ -1,50 +0,0 @@ - - - - -CSS Flexbox Test: flex-grow - 0(initial value) - - - - - - - - -

Test passes if there is a filled green square and no red.

-
-
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flex-grow-002.htm.png b/test/render/flex/flex-grow-002.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/flex-grow-002.htm.png and /dev/null differ diff --git a/test/render/flex/flex-grow-003.htm b/test/render/flex/flex-grow-003.htm deleted file mode 100644 index aabafd877..000000000 --- a/test/render/flex/flex-grow-003.htm +++ /dev/null @@ -1,49 +0,0 @@ - - - - -CSS Flexbox Test: flex-grow - negative number - - - - - - - - -

Test passes if there is a filled green square and no red.

-
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flex-grow-003.htm.png b/test/render/flex/flex-grow-003.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/flex-grow-003.htm.png and /dev/null differ diff --git a/test/render/flex/flex-grow-004.htm b/test/render/flex/flex-grow-004.htm deleted file mode 100644 index 5784a26de..000000000 --- a/test/render/flex/flex-grow-004.htm +++ /dev/null @@ -1,49 +0,0 @@ - - - - -CSS Flexbox Test: flex-grow - (invalid when no space distributed) - - - - - - - - -

Test passes if there is a filled green square and no red.

-
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flex-grow-004.htm.png b/test/render/flex/flex-grow-004.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/flex-grow-004.htm.png and /dev/null differ diff --git a/test/render/flex/flex-grow-005.htm b/test/render/flex/flex-grow-005.htm deleted file mode 100644 index dc22e780f..000000000 --- a/test/render/flex/flex-grow-005.htm +++ /dev/null @@ -1,43 +0,0 @@ - - - - -CSS Flexbox Test: flex-grow - (invalid when applied to flex container) - - - - - - - - -

Test passes if there is a filled green square and no red.

-
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flex-grow-005.htm.png b/test/render/flex/flex-grow-005.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/flex-grow-005.htm.png and /dev/null differ diff --git a/test/render/flex/flex-grow-006.htm b/test/render/flex/flex-grow-006.htm deleted file mode 100644 index df9eb0699..000000000 --- a/test/render/flex/flex-grow-006.htm +++ /dev/null @@ -1,42 +0,0 @@ - - - - -CSS Flexbox Test: flex-grow - positive number(fill all space) - - - - - - - - -

Test passes if there is a filled green square and no red.

-
-
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flex-grow-006.htm.png b/test/render/flex/flex-grow-006.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/flex-grow-006.htm.png and /dev/null differ diff --git a/test/render/flex/flex-grow-007.htm b/test/render/flex/flex-grow-007.htm deleted file mode 100644 index 43cbed696..000000000 --- a/test/render/flex/flex-grow-007.htm +++ /dev/null @@ -1,49 +0,0 @@ - - - - -CSS Flexbox Test: flex-grow - less than one - - - - - - -

Test passes if there is a filled green square and no red.

-
-
-
-
-
-
-
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flex-grow-007.htm.png b/test/render/flex/flex-grow-007.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/flex-grow-007.htm.png and /dev/null differ diff --git a/test/render/flex/flex-margin-no-collapse.htm b/test/render/flex/flex-margin-no-collapse.htm deleted file mode 100644 index 93e204bda..000000000 --- a/test/render/flex/flex-margin-no-collapse.htm +++ /dev/null @@ -1,58 +0,0 @@ - - - - - CSS Flexible Box Test: flex item margins - - - - - - - - -

The test passes if there are two green boxes and no red.

-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flex-margin-no-collapse.htm.png b/test/render/flex/flex-margin-no-collapse.htm.png deleted file mode 100644 index 7748b717c..000000000 Binary files a/test/render/flex/flex-margin-no-collapse.htm.png and /dev/null differ diff --git a/test/render/flex/flex-minimum-height-flex-items-001.htm b/test/render/flex/flex-minimum-height-flex-items-001.htm deleted file mode 100644 index bce73166e..000000000 --- a/test/render/flex/flex-minimum-height-flex-items-001.htm +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - CSS Flexible Box Test: Minimum height of flex items - - - - - - - - -

Test passes if there is a filled green square and no red.

- -
-
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flex-minimum-height-flex-items-001.htm.png b/test/render/flex/flex-minimum-height-flex-items-001.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/flex-minimum-height-flex-items-001.htm.png and /dev/null differ diff --git a/test/render/flex/flex-minimum-height-flex-items-002.htm b/test/render/flex/flex-minimum-height-flex-items-002.htm deleted file mode 100644 index cc8bfe167..000000000 --- a/test/render/flex/flex-minimum-height-flex-items-002.htm +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - CSS Flexible Box Test: Minimum height of flex items - - - - - - - -

Test passes if there is a filled green square and no red.

- -
-
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flex-minimum-height-flex-items-002.htm.png b/test/render/flex/flex-minimum-height-flex-items-002.htm.png deleted file mode 100644 index 6b9fa9aa3..000000000 Binary files a/test/render/flex/flex-minimum-height-flex-items-002.htm.png and /dev/null differ diff --git a/test/render/flex/flex-minimum-height-flex-items-011.htm b/test/render/flex/flex-minimum-height-flex-items-011.htm deleted file mode 100644 index 6591a43ff..000000000 --- a/test/render/flex/flex-minimum-height-flex-items-011.htm +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - CSS Flexible Box Test: Minimum height of flex items - - - - - - - - -

Test passes if there is a filled green square and no red.

- -
-
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flex-minimum-height-flex-items-011.htm.png b/test/render/flex/flex-minimum-height-flex-items-011.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/flex-minimum-height-flex-items-011.htm.png and /dev/null differ diff --git a/test/render/flex/flex-minimum-width-flex-items-002.htm b/test/render/flex/flex-minimum-width-flex-items-002.htm deleted file mode 100644 index e4e2abec2..000000000 --- a/test/render/flex/flex-minimum-width-flex-items-002.htm +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - CSS Flexible Box Test: Minimum width of flex items - - - - - - - -

Test passes if there is a filled green square and no red.

- -
-
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flex-minimum-width-flex-items-002.htm.png b/test/render/flex/flex-minimum-width-flex-items-002.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/flex-minimum-width-flex-items-002.htm.png and /dev/null differ diff --git a/test/render/flex/flex-order.htm b/test/render/flex/flex-order.htm deleted file mode 100644 index 023c949f5..000000000 --- a/test/render/flex/flex-order.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - - - CSS Test: flex order - - - - - - - -
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flex-order.htm.png b/test/render/flex/flex-order.htm.png deleted file mode 100644 index ea4d0fc25..000000000 Binary files a/test/render/flex/flex-order.htm.png and /dev/null differ diff --git a/test/render/flex/flex-shrink-001.htm b/test/render/flex/flex-shrink-001.htm deleted file mode 100644 index 1e675532c..000000000 --- a/test/render/flex/flex-shrink-001.htm +++ /dev/null @@ -1,47 +0,0 @@ - - - - -CSS Flexbox Test: flex-shrink - number(positive) - - - - - - - - -

Test passes if there is a filled green square and no red.

-
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flex-shrink-001.htm.png b/test/render/flex/flex-shrink-001.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/flex-shrink-001.htm.png and /dev/null differ diff --git a/test/render/flex/flex-shrink-002.htm b/test/render/flex/flex-shrink-002.htm deleted file mode 100644 index 8975611a9..000000000 --- a/test/render/flex/flex-shrink-002.htm +++ /dev/null @@ -1,47 +0,0 @@ - - - - -CSS Flexbox Test: flex-shrink - number(negative) - - - - - - - - -

Test passes if there is a filled green square and no red.

-
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flex-shrink-002.htm.png b/test/render/flex/flex-shrink-002.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/flex-shrink-002.htm.png and /dev/null differ diff --git a/test/render/flex/flex-shrink-003.htm b/test/render/flex/flex-shrink-003.htm deleted file mode 100644 index a211308b7..000000000 --- a/test/render/flex/flex-shrink-003.htm +++ /dev/null @@ -1,44 +0,0 @@ - - - - -CSS Flexbox Test: flex-shrink - 1(initial value) - - - - - - - - -

Test passes if there is a filled green square and no red.

-
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flex-shrink-003.htm.png b/test/render/flex/flex-shrink-003.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/flex-shrink-003.htm.png and /dev/null differ diff --git a/test/render/flex/flex-shrink-004.htm b/test/render/flex/flex-shrink-004.htm deleted file mode 100644 index 4773c03d0..000000000 --- a/test/render/flex/flex-shrink-004.htm +++ /dev/null @@ -1,48 +0,0 @@ - - - - -CSS Flexbox Test: flex-shrink - number(flex container has enough space) - - - - - - - - -

Test passes if there is a filled green square and no red.

-
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flex-shrink-004.htm.png b/test/render/flex/flex-shrink-004.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/flex-shrink-004.htm.png and /dev/null differ diff --git a/test/render/flex/flex-shrink-005.htm b/test/render/flex/flex-shrink-005.htm deleted file mode 100644 index 7f12d4b2a..000000000 --- a/test/render/flex/flex-shrink-005.htm +++ /dev/null @@ -1,47 +0,0 @@ - - - - -CSS Flexbox Test: flex-shrink - 0 - - - - - - - - -

Test passes if there is a filled green square and no red.

-
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flex-shrink-005.htm.png b/test/render/flex/flex-shrink-005.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/flex-shrink-005.htm.png and /dev/null differ diff --git a/test/render/flex/flex-shrink-006.htm b/test/render/flex/flex-shrink-006.htm deleted file mode 100644 index 488ecd12f..000000000 --- a/test/render/flex/flex-shrink-006.htm +++ /dev/null @@ -1,51 +0,0 @@ - - - - -CSS Flexbox Test: flex-shrink - 0(one of flex-shrinks sets 0, another not) - - - - - - - - -

Test passes if there is a filled green square and no red.

-
-
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flex-shrink-006.htm.png b/test/render/flex/flex-shrink-006.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/flex-shrink-006.htm.png and /dev/null differ diff --git a/test/render/flex/flex-shrink-007.htm b/test/render/flex/flex-shrink-007.htm deleted file mode 100644 index 4f9b04633..000000000 --- a/test/render/flex/flex-shrink-007.htm +++ /dev/null @@ -1,44 +0,0 @@ - - - - -CSS Flexbox Test: flex-shrink - applied to flex container - - - - - - - - -

Test passes if there is a filled green square and no red.

-
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flex-shrink-007.htm.png b/test/render/flex/flex-shrink-007.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/flex-shrink-007.htm.png and /dev/null differ diff --git a/test/render/flex/flex-shrink-008.htm b/test/render/flex/flex-shrink-008.htm deleted file mode 100644 index ec2bf7ecd..000000000 --- a/test/render/flex/flex-shrink-008.htm +++ /dev/null @@ -1,49 +0,0 @@ - - - - -CSS Flexbox Test: flex-shrink - less than one - - - - - - -

Test passes if there is a filled green square and no red.

-
-
-
-
-
-
-
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flex-shrink-008.htm.png b/test/render/flex/flex-shrink-008.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/flex-shrink-008.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-abspos-child-001a.htm.png b/test/render/flex/flexbox-abspos-child-001a.htm.png deleted file mode 100644 index 31c053b0c..000000000 Binary files a/test/render/flex/flexbox-abspos-child-001a.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-abspos-child-001b.htm.png b/test/render/flex/flexbox-abspos-child-001b.htm.png deleted file mode 100644 index 31c053b0c..000000000 Binary files a/test/render/flex/flexbox-abspos-child-001b.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-align-items-center-nested-001.htm b/test/render/flex/flexbox-align-items-center-nested-001.htm deleted file mode 100644 index 7e27baa1e..000000000 --- a/test/render/flex/flexbox-align-items-center-nested-001.htm +++ /dev/null @@ -1,52 +0,0 @@ - - - - -CSS Test: Flexbox nested containers with align-items: center and flexible items - - - - - -
-
-
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-align-items-center-nested-001.htm.png b/test/render/flex/flexbox-align-items-center-nested-001.htm.png deleted file mode 100644 index 3f110032d..000000000 Binary files a/test/render/flex/flexbox-align-items-center-nested-001.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-align-self-baseline-horiz-001a.htm b/test/render/flex/flexbox-align-self-baseline-horiz-001a.htm deleted file mode 100644 index f17fc1960..000000000 --- a/test/render/flex/flexbox-align-self-baseline-horiz-001a.htm +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - CSS Test: Baseline alignment of block flex items with 'baseline' value for 'align-items' / 'align-self' - - - - - - -
-
blk_1line
-
blk
2lines
-
super
-
sub
-
big
text
3lines
- ital
ic
-
-
-
blk_1line
-
blk
2lines
-
super
-
sub
-
big
text
3lines
- ital
ic
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-align-self-baseline-horiz-001a.htm.png b/test/render/flex/flexbox-align-self-baseline-horiz-001a.htm.png deleted file mode 100644 index b6a570651..000000000 Binary files a/test/render/flex/flexbox-align-self-baseline-horiz-001a.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-align-self-baseline-horiz-001b.htm b/test/render/flex/flexbox-align-self-baseline-horiz-001b.htm deleted file mode 100644 index 589373625..000000000 --- a/test/render/flex/flexbox-align-self-baseline-horiz-001b.htm +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - CSS Test: Baseline alignment of block flex items with 'baseline' value for 'align-items' / 'align-self' in a wrap-reverse flex container - - - - - - -
-
blk_1line
-
blk
2lines
-
super
-
sub
-
big
text
3lines
- ital
ic
-
-
-
blk_1line
-
blk
2lines
-
super
-
sub
-
big
text
3lines
- ital
ic
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-align-self-baseline-horiz-001b.htm.png b/test/render/flex/flexbox-align-self-baseline-horiz-001b.htm.png deleted file mode 100644 index b6a570651..000000000 Binary files a/test/render/flex/flexbox-align-self-baseline-horiz-001b.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-align-self-baseline-horiz-002.htm b/test/render/flex/flexbox-align-self-baseline-horiz-002.htm deleted file mode 100644 index d235a481a..000000000 --- a/test/render/flex/flexbox-align-self-baseline-horiz-002.htm +++ /dev/null @@ -1,92 +0,0 @@ - - - - - - CSS Test: Baseline alignment of flex items in fixed-size single-line flex container - - - - - - - - -
-
a
-
- - - -
- - -
-
a
-
- - -
-
a
-
- - -
-
a
-
- - -
-
a
-
- - - -
- - -
-
a
-
- - -
-
a
-
- - -
-
a
-
- - -
-
a
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-align-self-baseline-horiz-002.htm.png b/test/render/flex/flexbox-align-self-baseline-horiz-002.htm.png deleted file mode 100644 index da39c4295..000000000 Binary files a/test/render/flex/flexbox-align-self-baseline-horiz-002.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-align-self-baseline-horiz-003.htm b/test/render/flex/flexbox-align-self-baseline-horiz-003.htm deleted file mode 100644 index 682296a86..000000000 --- a/test/render/flex/flexbox-align-self-baseline-horiz-003.htm +++ /dev/null @@ -1,94 +0,0 @@ - - - - - - CSS Test: Baseline alignment of flex items in fixed-size single-line flex container, with cross axis reversed - - - - - - - - -
-
a
-
- - - -
- - -
-
a
-
- - -
-
a
-
- - -
-
a
-
- - -
-
a
-
- - - -
- - -
-
a
-
- - -
-
a
-
- - -
-
a
-
- - -
-
a
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-align-self-baseline-horiz-003.htm.png b/test/render/flex/flexbox-align-self-baseline-horiz-003.htm.png deleted file mode 100644 index b9801e05b..000000000 Binary files a/test/render/flex/flexbox-align-self-baseline-horiz-003.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-align-self-baseline-horiz-004.htm b/test/render/flex/flexbox-align-self-baseline-horiz-004.htm deleted file mode 100644 index da2ce3433..000000000 --- a/test/render/flex/flexbox-align-self-baseline-horiz-004.htm +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - CSS Test: Baseline alignment of block flex items with 'baseline' value for 'align-items' / 'align-self' in a multi-line flex container - - - - - - -
- -
a
-
b
-
c
- - -
d
-
e
-
f
- - -
g
-
h
-
i
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-align-self-baseline-horiz-004.htm.png b/test/render/flex/flexbox-align-self-baseline-horiz-004.htm.png deleted file mode 100644 index 79bcc64cf..000000000 Binary files a/test/render/flex/flexbox-align-self-baseline-horiz-004.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-align-self-baseline-horiz-005.htm b/test/render/flex/flexbox-align-self-baseline-horiz-005.htm deleted file mode 100644 index dfc3d6061..000000000 --- a/test/render/flex/flexbox-align-self-baseline-horiz-005.htm +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - CSS Test: Baseline alignment of block flex items with 'baseline' value for 'align-items' / 'align-self' in a multi-line flex container - - - - - - -
- -
a
-
b
-
c
- - -
d
-
e
-
f
- - -
g
-
h
-
i
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-align-self-baseline-horiz-005.htm.png b/test/render/flex/flexbox-align-self-baseline-horiz-005.htm.png deleted file mode 100644 index 1a7afa48d..000000000 Binary files a/test/render/flex/flexbox-align-self-baseline-horiz-005.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-align-self-baseline-horiz-007.htm b/test/render/flex/flexbox-align-self-baseline-horiz-007.htm deleted file mode 100644 index c67816784..000000000 --- a/test/render/flex/flexbox-align-self-baseline-horiz-007.htm +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - CSS Test: Baseline alignment of block flex items with 'baseline' and 'last-baseline' values for 'align-self' against each other. - - - - - - -
-
one line (first)
-
one line (last)
-
two
lines and offset (last)
-
offset (first)
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-align-self-baseline-horiz-007.htm.png b/test/render/flex/flexbox-align-self-baseline-horiz-007.htm.png deleted file mode 100644 index f36a198e0..000000000 Binary files a/test/render/flex/flexbox-align-self-baseline-horiz-007.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-align-self-horiz-001-block.htm b/test/render/flex/flexbox-align-self-horiz-001-block.htm deleted file mode 100644 index 3664bce13..000000000 --- a/test/render/flex/flexbox-align-self-horiz-001-block.htm +++ /dev/null @@ -1,99 +0,0 @@ - - - - - - CSS Test: Testing the behavior of 'align-self' property values on flex items that are blocks, in a horizontal flex container - - - - - - -
-
start
-
a b c d e f
-
end
-
a b c d e f
-
center
-
a b c d e f
-
base
-
abc
-
stretch
-
a b c d e f
-
auto
-
unspec
-
initial
-
inherit
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-align-self-horiz-001-block.htm.png b/test/render/flex/flexbox-align-self-horiz-001-block.htm.png deleted file mode 100644 index dfeb7b76e..000000000 Binary files a/test/render/flex/flexbox-align-self-horiz-001-block.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-align-self-horiz-003.htm b/test/render/flex/flexbox-align-self-horiz-003.htm deleted file mode 100644 index 247a6e1b3..000000000 --- a/test/render/flex/flexbox-align-self-horiz-003.htm +++ /dev/null @@ -1,99 +0,0 @@ - - - - - - CSS Test: Testing the behavior of 'align-self' with a horizontal flex container that's shorter than its items - - - - - - -
-
start
-
a b c d e f
-
end
-
a b c d e f
-
center
-
a b c d e f
-
base
-
abc
-
stretch
-
a b c d e f
-
auto
-
unspec
-
initial
-
inherit
-
normal
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-align-self-horiz-003.htm.png b/test/render/flex/flexbox-align-self-horiz-003.htm.png deleted file mode 100644 index 2d7feff59..000000000 Binary files a/test/render/flex/flexbox-align-self-horiz-003.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-align-self-horiz-004.htm b/test/render/flex/flexbox-align-self-horiz-004.htm deleted file mode 100644 index 83b09cb0e..000000000 --- a/test/render/flex/flexbox-align-self-horiz-004.htm +++ /dev/null @@ -1,86 +0,0 @@ - - - - - - CSS Test: Testing the behavior of 'align-self' with a horizontal flex container that's shorter than its items, with margin/padding/border on the items - - - - - - -
-
start
-
a b c d e f
-
end
-
a b c d e f
-
center
-
a b c d e f
-
-
-
-
base
-
abc
-
stretch
-
a b c d e f
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-align-self-horiz-004.htm.png b/test/render/flex/flexbox-align-self-horiz-004.htm.png deleted file mode 100644 index 56487add5..000000000 Binary files a/test/render/flex/flexbox-align-self-horiz-004.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-align-self-horiz-005.htm b/test/render/flex/flexbox-align-self-horiz-005.htm deleted file mode 100644 index c005a8be6..000000000 --- a/test/render/flex/flexbox-align-self-horiz-005.htm +++ /dev/null @@ -1,106 +0,0 @@ - - - - - - CSS Test: Testing the behavior of 'align-self' with auto margins in play, in a horizontal flex container - - - - - - -
-
start
-
a b c d e f
-
end
-
a b c d e f
-
center
-
a b c d e f
-
base
-
a b c d e f
-
stretch
-
a b c d e f
-
- -
-
start
-
a b c d e f
-
end
-
a b c d e f
-
center
-
a b c d e f
-
base
-
a b c d e f
-
stretch
-
a b c d e f
-
- -
-
start
-
a b c d e f
-
end
-
a b c d e f
-
center
-
a b c d e f
-
base
-
a b c d e f
-
stretch
-
a b c d e f
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-align-self-horiz-005.htm.png b/test/render/flex/flexbox-align-self-horiz-005.htm.png deleted file mode 100644 index e547e5e60..000000000 Binary files a/test/render/flex/flexbox-align-self-horiz-005.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-align-self-stretch-vert-001.htm b/test/render/flex/flexbox-align-self-stretch-vert-001.htm deleted file mode 100644 index 10855ab78..000000000 --- a/test/render/flex/flexbox-align-self-stretch-vert-001.htm +++ /dev/null @@ -1,52 +0,0 @@ - - - - - CSS Test: Testing the sizing of a stretched horizontal flex container in a vertical flex container - - - - - - - - - -
-
-
-
-
A B C
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-align-self-stretch-vert-001.htm.png b/test/render/flex/flexbox-align-self-stretch-vert-001.htm.png deleted file mode 100644 index 45101bf3a..000000000 Binary files a/test/render/flex/flexbox-align-self-stretch-vert-001.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-align-self-stretch-vert-002.htm b/test/render/flex/flexbox-align-self-stretch-vert-002.htm deleted file mode 100644 index 1b9375226..000000000 --- a/test/render/flex/flexbox-align-self-stretch-vert-002.htm +++ /dev/null @@ -1,38 +0,0 @@ - - - - - CSS Test: Testing the sizing of stretched flex items in a vertical multi-line flex container - - - - - - - - -
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-align-self-stretch-vert-002.htm.png b/test/render/flex/flexbox-align-self-stretch-vert-002.htm.png deleted file mode 100644 index 6467f563a..000000000 Binary files a/test/render/flex/flexbox-align-self-stretch-vert-002.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-align-self-vert-001.htm b/test/render/flex/flexbox-align-self-vert-001.htm deleted file mode 100644 index d7e7e4395..000000000 --- a/test/render/flex/flexbox-align-self-vert-001.htm +++ /dev/null @@ -1,94 +0,0 @@ - - - - - - CSS Test: Testing the behavior of 'align-self' property values on flex items that are blocks, in a vertical flex container - - - - - - -
-
start
-
a b c d e f
-
end
-
a b c d e f
-
center
-
a b c d e f
-
base
-
abc
-
stretch
-
a b c d e f
-
auto
-
unspec
-
initial
-
inherit
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-align-self-vert-001.htm.png b/test/render/flex/flexbox-align-self-vert-001.htm.png deleted file mode 100644 index 4acd0dc8b..000000000 Binary files a/test/render/flex/flexbox-align-self-vert-001.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-align-self-vert-003.htm b/test/render/flex/flexbox-align-self-vert-003.htm deleted file mode 100644 index 68538801b..000000000 --- a/test/render/flex/flexbox-align-self-vert-003.htm +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - CSS Test: Testing the behavior of 'align-self' with a vertical flex container that's skinnier than its items - - - - - - -
-
start
-
a b
-
end
-
a b
-
center
-
a b
-
base
-
abc
-
stretch
-
a b
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-align-self-vert-003.htm.png b/test/render/flex/flexbox-align-self-vert-003.htm.png deleted file mode 100644 index 6246abda9..000000000 Binary files a/test/render/flex/flexbox-align-self-vert-003.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-align-self-vert-004.htm b/test/render/flex/flexbox-align-self-vert-004.htm deleted file mode 100644 index 1927d1807..000000000 --- a/test/render/flex/flexbox-align-self-vert-004.htm +++ /dev/null @@ -1,81 +0,0 @@ - - - - - - CSS Test: Testing the behavior of 'align-self' with a vertical flex container that's skinnier than its items, with margin/padding/border on the items - - - - - - -
-
start
-
a b
-
end
-
a b
-
center
-
a b
-
-
-
base
-
abc
-
stretch
-
a b
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-align-self-vert-004.htm.png b/test/render/flex/flexbox-align-self-vert-004.htm.png deleted file mode 100644 index c1a180ca0..000000000 Binary files a/test/render/flex/flexbox-align-self-vert-004.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-anonymous-items-001.htm b/test/render/flex/flexbox-anonymous-items-001.htm deleted file mode 100644 index 614d75101..000000000 --- a/test/render/flex/flexbox-anonymous-items-001.htm +++ /dev/null @@ -1,29 +0,0 @@ - - - - - CSS Test: Testing that we gracefully handle cases where two anonymous flex items become adjacent due to "order" reordering - - - - - - - -
- a a -
x x
- b b -
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-anonymous-items-001.htm.png b/test/render/flex/flexbox-anonymous-items-001.htm.png deleted file mode 100644 index ab14f71de..000000000 Binary files a/test/render/flex/flexbox-anonymous-items-001.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-baseline-align-self-baseline-horiz-001.htm b/test/render/flex/flexbox-baseline-align-self-baseline-horiz-001.htm deleted file mode 100644 index b3b29234a..000000000 --- a/test/render/flex/flexbox-baseline-align-self-baseline-horiz-001.htm +++ /dev/null @@ -1,64 +0,0 @@ - - - - - CSS Test: Testing the baseline of a horizontal flex container with baseline-aligned flex items - - - - - - - - a -
-
b
-
c
-
d
-
-
-
e
-
f
-
g
-
-
-
h
-
i
-
j
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-baseline-align-self-baseline-horiz-001.htm.png b/test/render/flex/flexbox-baseline-align-self-baseline-horiz-001.htm.png deleted file mode 100644 index 6fc471302..000000000 Binary files a/test/render/flex/flexbox-baseline-align-self-baseline-horiz-001.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-baseline-align-self-baseline-vert-001.htm b/test/render/flex/flexbox-baseline-align-self-baseline-vert-001.htm deleted file mode 100644 index 973aa3ff4..000000000 --- a/test/render/flex/flexbox-baseline-align-self-baseline-vert-001.htm +++ /dev/null @@ -1,68 +0,0 @@ - - - - - CSS Test: Testing the baseline of a vertical flex container with baseline-aligned flex items - - - - - - - - - a -
-
b
-
c
-
d
-
-
-
e
-
f
-
g
-
-
-
h
-
i
-
j
-
- - - - - diff --git a/test/render/flex/flexbox-baseline-align-self-baseline-vert-001.htm.png b/test/render/flex/flexbox-baseline-align-self-baseline-vert-001.htm.png deleted file mode 100644 index 9c4d99ea2..000000000 Binary files a/test/render/flex/flexbox-baseline-align-self-baseline-vert-001.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-baseline-empty-001a.htm b/test/render/flex/flexbox-baseline-empty-001a.htm deleted file mode 100644 index 1260239c3..000000000 --- a/test/render/flex/flexbox-baseline-empty-001a.htm +++ /dev/null @@ -1,49 +0,0 @@ - - - - - CSS Test: Testing the baseline of an empty horizontal flex container - - - - - - - - - A -
-
-
-
-
-
- - - - - diff --git a/test/render/flex/flexbox-baseline-empty-001a.htm.png b/test/render/flex/flexbox-baseline-empty-001a.htm.png deleted file mode 100644 index 8fd3b7803..000000000 Binary files a/test/render/flex/flexbox-baseline-empty-001a.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-baseline-empty-001b.htm b/test/render/flex/flexbox-baseline-empty-001b.htm deleted file mode 100644 index 325751b47..000000000 --- a/test/render/flex/flexbox-baseline-empty-001b.htm +++ /dev/null @@ -1,50 +0,0 @@ - - - - - CSS Test: Testing the baseline of an empty vertical flex container - - - - - - - - - A -
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-baseline-empty-001b.htm.png b/test/render/flex/flexbox-baseline-empty-001b.htm.png deleted file mode 100644 index 8fd3b7803..000000000 Binary files a/test/render/flex/flexbox-baseline-empty-001b.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-baseline-multi-item-horiz-001a.htm b/test/render/flex/flexbox-baseline-multi-item-horiz-001a.htm deleted file mode 100644 index 2a21a4ec5..000000000 --- a/test/render/flex/flexbox-baseline-multi-item-horiz-001a.htm +++ /dev/null @@ -1,47 +0,0 @@ - - - - - CSS Test: Testing the baseline of a horizontal flex container whose flex items are not baseline-aligned - - - - - - - - a -
-
b
c
-
-
-
d
e
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-baseline-multi-item-horiz-001a.htm.png b/test/render/flex/flexbox-baseline-multi-item-horiz-001a.htm.png deleted file mode 100644 index 10b053cec..000000000 Binary files a/test/render/flex/flexbox-baseline-multi-item-horiz-001a.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-baseline-multi-item-horiz-001b.htm b/test/render/flex/flexbox-baseline-multi-item-horiz-001b.htm deleted file mode 100644 index cb241ee95..000000000 --- a/test/render/flex/flexbox-baseline-multi-item-horiz-001b.htm +++ /dev/null @@ -1,49 +0,0 @@ - - - - - CSS Test: Testing the baseline of a horizontal flex container whose flex items are not baseline-aligned - - - - - - - - a -
-
c
b
-
-
-
e
d
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-baseline-multi-item-horiz-001b.htm.png b/test/render/flex/flexbox-baseline-multi-item-horiz-001b.htm.png deleted file mode 100644 index 10b053cec..000000000 Binary files a/test/render/flex/flexbox-baseline-multi-item-horiz-001b.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-baseline-multi-item-vert-001a.htm b/test/render/flex/flexbox-baseline-multi-item-vert-001a.htm deleted file mode 100644 index 612556311..000000000 --- a/test/render/flex/flexbox-baseline-multi-item-vert-001a.htm +++ /dev/null @@ -1,57 +0,0 @@ - - - - - CSS Test: Testing the baseline of a vertical flex container whose flex items are not baseline-aligned - - - - - - - - - a -
-
b
c
-
-
-
d
e
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-baseline-multi-item-vert-001a.htm.png b/test/render/flex/flexbox-baseline-multi-item-vert-001a.htm.png deleted file mode 100644 index 0e1c1fc4f..000000000 Binary files a/test/render/flex/flexbox-baseline-multi-item-vert-001a.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-baseline-multi-item-vert-001b.htm b/test/render/flex/flexbox-baseline-multi-item-vert-001b.htm deleted file mode 100644 index 54b716477..000000000 --- a/test/render/flex/flexbox-baseline-multi-item-vert-001b.htm +++ /dev/null @@ -1,59 +0,0 @@ - - - - - CSS Test: Testing the baseline of a vertical flex container whose flex items are not baseline-aligned - - - - - - - - - a -
-
c
b
-
-
-
e
d
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-baseline-multi-item-vert-001b.htm.png b/test/render/flex/flexbox-baseline-multi-item-vert-001b.htm.png deleted file mode 100644 index 0e1c1fc4f..000000000 Binary files a/test/render/flex/flexbox-baseline-multi-item-vert-001b.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-baseline-multi-line-horiz-001.htm b/test/render/flex/flexbox-baseline-multi-line-horiz-001.htm deleted file mode 100644 index 23d2ea6eb..000000000 --- a/test/render/flex/flexbox-baseline-multi-line-horiz-001.htm +++ /dev/null @@ -1,76 +0,0 @@ - - - - - CSS Test: Testing the baseline of a horizontal flex container with multiple flex lines - - - - - - - - a - -
-
b
c
d
e
-
- -
-
f
g
h
i
-
- - -
-
j
k
l
m
-
- n - - - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-baseline-multi-line-horiz-001.htm.png b/test/render/flex/flexbox-baseline-multi-line-horiz-001.htm.png deleted file mode 100644 index e16213246..000000000 Binary files a/test/render/flex/flexbox-baseline-multi-line-horiz-001.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-baseline-multi-line-horiz-002.htm b/test/render/flex/flexbox-baseline-multi-line-horiz-002.htm deleted file mode 100644 index 67992698c..000000000 --- a/test/render/flex/flexbox-baseline-multi-line-horiz-002.htm +++ /dev/null @@ -1,76 +0,0 @@ - - - - - CSS Test: Testing the baseline of a horizontal flex container with multiple flex lines - - - - - - - - a - -
-
b
c
d
e
-
- -
-
f
g
h
i
-
- - -
-
j
k
l
m
-
- n - - - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-baseline-multi-line-horiz-002.htm.png b/test/render/flex/flexbox-baseline-multi-line-horiz-002.htm.png deleted file mode 100644 index fc5689f18..000000000 Binary files a/test/render/flex/flexbox-baseline-multi-line-horiz-002.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-baseline-multi-line-horiz-003.htm b/test/render/flex/flexbox-baseline-multi-line-horiz-003.htm deleted file mode 100644 index e19df8046..000000000 --- a/test/render/flex/flexbox-baseline-multi-line-horiz-003.htm +++ /dev/null @@ -1,76 +0,0 @@ - - - - - CSS Test: Testing the baseline of a horizontal multi-line (wrap) flex container with baseline-aligned items on first line - - - - - - - - a - -
-
b
-
c
-
d
-
e
-
- - -
-
f
-
g
-
h
-
i
-
- - -
-
j
-
k
-
l
-
m
-
- n - - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-baseline-multi-line-horiz-003.htm.png b/test/render/flex/flexbox-baseline-multi-line-horiz-003.htm.png deleted file mode 100644 index 6ce6ffa24..000000000 Binary files a/test/render/flex/flexbox-baseline-multi-line-horiz-003.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-baseline-multi-line-vert-001.htm b/test/render/flex/flexbox-baseline-multi-line-vert-001.htm deleted file mode 100644 index 55e9acf4e..000000000 --- a/test/render/flex/flexbox-baseline-multi-line-vert-001.htm +++ /dev/null @@ -1,77 +0,0 @@ - - - - - CSS Test: Testing the baseline of a vertical flex container with multiple flex lines - - - - - - - - a - -
-
b
c
d
e
-
- -
-
f
g
h
i
-
- - -
-
j
k
l
m
-
- n - - - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-baseline-multi-line-vert-001.htm.png b/test/render/flex/flexbox-baseline-multi-line-vert-001.htm.png deleted file mode 100644 index 38a3400fa..000000000 Binary files a/test/render/flex/flexbox-baseline-multi-line-vert-001.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-baseline-multi-line-vert-002.htm b/test/render/flex/flexbox-baseline-multi-line-vert-002.htm deleted file mode 100644 index 077fb8b67..000000000 --- a/test/render/flex/flexbox-baseline-multi-line-vert-002.htm +++ /dev/null @@ -1,78 +0,0 @@ - - - - - CSS Test: Testing the baseline of a vertical flex container with multiple flex lines - - - - - - - - a - -
-
b
c
d
e
-
- -
-
f
g
h
i
-
- - -
-
j
k
l
m
-
- n - - - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-baseline-multi-line-vert-002.htm.png b/test/render/flex/flexbox-baseline-multi-line-vert-002.htm.png deleted file mode 100644 index 31fda30f4..000000000 Binary files a/test/render/flex/flexbox-baseline-multi-line-vert-002.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-basic-block-horiz-001.htm b/test/render/flex/flexbox-basic-block-horiz-001.htm deleted file mode 100644 index 86a2404aa..000000000 --- a/test/render/flex/flexbox-basic-block-horiz-001.htm +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - CSS Test: Testing flexbox layout algorithm property on block flex items in a horizontal flex container - - - - - - -
-
-
-
-
-
-
-
-
- -
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-basic-block-horiz-001.htm.png b/test/render/flex/flexbox-basic-block-horiz-001.htm.png deleted file mode 100644 index 55a0a1afe..000000000 Binary files a/test/render/flex/flexbox-basic-block-horiz-001.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-basic-block-vert-001.htm b/test/render/flex/flexbox-basic-block-vert-001.htm deleted file mode 100644 index 7fa303728..000000000 --- a/test/render/flex/flexbox-basic-block-vert-001.htm +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - CSS Test: Testing flexbox layout algorithm property on block flex items in a vertical flex container - - - - - - -
-
-
-
-
-
-
-
-
- -
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-basic-block-vert-001.htm.png b/test/render/flex/flexbox-basic-block-vert-001.htm.png deleted file mode 100644 index e5625a407..000000000 Binary files a/test/render/flex/flexbox-basic-block-vert-001.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-break-request-horiz-001a.htm b/test/render/flex/flexbox-break-request-horiz-001a.htm deleted file mode 100644 index 3f198d9e2..000000000 --- a/test/render/flex/flexbox-break-request-horiz-001a.htm +++ /dev/null @@ -1,111 +0,0 @@ - - - - - CSS Test: Testing page-break-before in horizontal multi-line flex containers - - - - - - - - - -
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
- - - -
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
- - - -
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-break-request-horiz-001a.htm.png b/test/render/flex/flexbox-break-request-horiz-001a.htm.png deleted file mode 100644 index ae52642a0..000000000 Binary files a/test/render/flex/flexbox-break-request-horiz-001a.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-break-request-horiz-001b.htm b/test/render/flex/flexbox-break-request-horiz-001b.htm deleted file mode 100644 index e921f4a03..000000000 --- a/test/render/flex/flexbox-break-request-horiz-001b.htm +++ /dev/null @@ -1,111 +0,0 @@ - - - - - CSS Test: Testing page-break-after in horizontal multi-line flex containers - - - - - - - - - -
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
- - - -
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
- - - -
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-break-request-horiz-001b.htm.png b/test/render/flex/flexbox-break-request-horiz-001b.htm.png deleted file mode 100644 index ae52642a0..000000000 Binary files a/test/render/flex/flexbox-break-request-horiz-001b.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-break-request-horiz-002a.htm b/test/render/flex/flexbox-break-request-horiz-002a.htm deleted file mode 100644 index b272d359a..000000000 --- a/test/render/flex/flexbox-break-request-horiz-002a.htm +++ /dev/null @@ -1,110 +0,0 @@ - - - - - CSS Test: Testing page-break-before in horizontal single-line flex containers (should have no effect) - - - - - - - - - -
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
- - - -
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
- - - -
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-break-request-horiz-002a.htm.png b/test/render/flex/flexbox-break-request-horiz-002a.htm.png deleted file mode 100644 index 32f06e7fd..000000000 Binary files a/test/render/flex/flexbox-break-request-horiz-002a.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-break-request-horiz-002b.htm b/test/render/flex/flexbox-break-request-horiz-002b.htm deleted file mode 100644 index 480b23f9a..000000000 --- a/test/render/flex/flexbox-break-request-horiz-002b.htm +++ /dev/null @@ -1,110 +0,0 @@ - - - - - CSS Test: Testing page-break-after in horizontal single-line flex containers (should have no effect) - - - - - - - - - -
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
- - - -
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
- - - -
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-break-request-horiz-002b.htm.png b/test/render/flex/flexbox-break-request-horiz-002b.htm.png deleted file mode 100644 index 32f06e7fd..000000000 Binary files a/test/render/flex/flexbox-break-request-horiz-002b.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-break-request-vert-001a.htm b/test/render/flex/flexbox-break-request-vert-001a.htm deleted file mode 100644 index 0970a7f6c..000000000 --- a/test/render/flex/flexbox-break-request-vert-001a.htm +++ /dev/null @@ -1,112 +0,0 @@ - - - - - CSS Test: Testing page-break-before in vertical multi-line flex containers - - - - - - - - - -
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
- - - -
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
- - - -
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-break-request-vert-001a.htm.png b/test/render/flex/flexbox-break-request-vert-001a.htm.png deleted file mode 100644 index 00d088a40..000000000 Binary files a/test/render/flex/flexbox-break-request-vert-001a.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-break-request-vert-001b.htm b/test/render/flex/flexbox-break-request-vert-001b.htm deleted file mode 100644 index ca87ef6e7..000000000 --- a/test/render/flex/flexbox-break-request-vert-001b.htm +++ /dev/null @@ -1,112 +0,0 @@ - - - - - CSS Test: Testing page-break-after in vertical multi-line flex containers - - - - - - - - - -
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
- - - -
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
- - - -
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-break-request-vert-001b.htm.png b/test/render/flex/flexbox-break-request-vert-001b.htm.png deleted file mode 100644 index 00d088a40..000000000 Binary files a/test/render/flex/flexbox-break-request-vert-001b.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-break-request-vert-002a.htm b/test/render/flex/flexbox-break-request-vert-002a.htm deleted file mode 100644 index 4ad2516d3..000000000 --- a/test/render/flex/flexbox-break-request-vert-002a.htm +++ /dev/null @@ -1,111 +0,0 @@ - - - - - CSS Test: Testing page-break-before in vertical single-line flex containers (should have no effect) - - - - - - - - - -
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
- - - -
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
- - - -
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-break-request-vert-002a.htm.png b/test/render/flex/flexbox-break-request-vert-002a.htm.png deleted file mode 100644 index c2139c89a..000000000 Binary files a/test/render/flex/flexbox-break-request-vert-002a.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-break-request-vert-002b.htm b/test/render/flex/flexbox-break-request-vert-002b.htm deleted file mode 100644 index 012b80a73..000000000 --- a/test/render/flex/flexbox-break-request-vert-002b.htm +++ /dev/null @@ -1,111 +0,0 @@ - - - - - CSS Test: Testing page-break-after in vertical single-line flex containers (should have no effect) - - - - - - - - - -
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
- - - -
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
- - - -
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-break-request-vert-002b.htm.png b/test/render/flex/flexbox-break-request-vert-002b.htm.png deleted file mode 100644 index c2139c89a..000000000 Binary files a/test/render/flex/flexbox-break-request-vert-002b.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-flex-basis-content-nocanvas-001a.htm b/test/render/flex/flexbox-flex-basis-content-nocanvas-001a.htm deleted file mode 100644 index e66abb4ed..000000000 --- a/test/render/flex/flexbox-flex-basis-content-nocanvas-001a.htm +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - CSS Test: Testing "flex-basis: content" in a row-oriented flex container - - - - - - - - - - -
-
a b
-
c
-
-
-
- - -
-
a b
-
c
-
-
-
- - -
-
a b
-
c
-
-
-
- - - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-flex-basis-content-nocanvas-001a.htm.png b/test/render/flex/flexbox-flex-basis-content-nocanvas-001a.htm.png deleted file mode 100644 index a5f351d66..000000000 Binary files a/test/render/flex/flexbox-flex-basis-content-nocanvas-001a.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-flex-basis-content-nocanvas-001b.htm b/test/render/flex/flexbox-flex-basis-content-nocanvas-001b.htm deleted file mode 100644 index 8b4992931..000000000 --- a/test/render/flex/flexbox-flex-basis-content-nocanvas-001b.htm +++ /dev/null @@ -1,86 +0,0 @@ - - - - - - CSS Test: Testing "flex-basis: content" (set via the "flex" shorthand) - in a row-oriented flex container. - - - - - - - - - - -
-
a b
-
c
-
-
- -
- - -
-
a b
-
c
-
-
- -
- - -
-
a b
-
c
-
-
- -
- - - - - - diff --git a/test/render/flex/flexbox-flex-basis-content-nocanvas-001b.htm.png b/test/render/flex/flexbox-flex-basis-content-nocanvas-001b.htm.png deleted file mode 100644 index e9be0e852..000000000 Binary files a/test/render/flex/flexbox-flex-basis-content-nocanvas-001b.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-flex-direction-column-reverse.htm b/test/render/flex/flexbox-flex-direction-column-reverse.htm deleted file mode 100644 index c7bff4e66..000000000 --- a/test/render/flex/flexbox-flex-direction-column-reverse.htm +++ /dev/null @@ -1,61 +0,0 @@ - - - - - CSS Flexbox Test: Flex-direction = column-reverse - - - - - - - - - -

The test passes if there is a 3x3 grid of green squares, numbered 1-9 left-to-right and top-to-bottom, and there is no red.

-
-
7
4
1
-
8
5
2
-
9
6
3
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-flex-direction-column-reverse.htm.png b/test/render/flex/flexbox-flex-direction-column-reverse.htm.png deleted file mode 100644 index a23260e43..000000000 Binary files a/test/render/flex/flexbox-flex-direction-column-reverse.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-flex-direction-column.htm b/test/render/flex/flexbox-flex-direction-column.htm deleted file mode 100644 index 189c04ea7..000000000 --- a/test/render/flex/flexbox-flex-direction-column.htm +++ /dev/null @@ -1,60 +0,0 @@ - - - - - CSS Flexbox Test: Flex-direction = column - - - - - - - - - -

The test passes if there is a 3x3 grid of green squares, numbered 1-9 left-to-right and top-to-bottom, and there is no red.

-
-
1
4
7
-
2
5
8
-
3
6
9
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-flex-direction-column.htm.png b/test/render/flex/flexbox-flex-direction-column.htm.png deleted file mode 100644 index a23260e43..000000000 Binary files a/test/render/flex/flexbox-flex-direction-column.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-flex-direction-default.htm b/test/render/flex/flexbox-flex-direction-default.htm deleted file mode 100644 index e4e227468..000000000 --- a/test/render/flex/flexbox-flex-direction-default.htm +++ /dev/null @@ -1,59 +0,0 @@ - - - - - CSS Flexbox Test: Flex-direction = row by default - - - - - - - - - -

The test passes if there is a 3x3 grid of green squares, numbered 1-9 left-to-right and top-to-bottom, and there is no red.

-
-
1
2
3
-
4
5
6
-
7
8
9
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-flex-direction-default.htm.png b/test/render/flex/flexbox-flex-direction-default.htm.png deleted file mode 100644 index a23260e43..000000000 Binary files a/test/render/flex/flexbox-flex-direction-default.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-flex-direction-row-reverse.htm b/test/render/flex/flexbox-flex-direction-row-reverse.htm deleted file mode 100644 index aff8d1648..000000000 --- a/test/render/flex/flexbox-flex-direction-row-reverse.htm +++ /dev/null @@ -1,61 +0,0 @@ - - - - - CSS Flexbox Test: Flex-direction = row-reverse - - - - - - - - - -

The test passes if there is a 3x3 grid of green squares, numbered 1-9 left-to-right and top-to-bottom, and there is no red.

-
-
3
2
1
-
6
5
4
-
9
8
7
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-flex-direction-row-reverse.htm.png b/test/render/flex/flexbox-flex-direction-row-reverse.htm.png deleted file mode 100644 index a23260e43..000000000 Binary files a/test/render/flex/flexbox-flex-direction-row-reverse.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-flex-direction-row.htm b/test/render/flex/flexbox-flex-direction-row.htm deleted file mode 100644 index fa53bd57e..000000000 --- a/test/render/flex/flexbox-flex-direction-row.htm +++ /dev/null @@ -1,60 +0,0 @@ - - - - - CSS Flexbox Test: Flex-direction = row - - - - - - - - - -

The test passes if there is a 3x3 grid of green squares, numbered 1-9 left-to-right and top-to-bottom, and there is no red.

-
-
1
2
3
-
4
5
6
-
7
8
9
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-flex-direction-row.htm.png b/test/render/flex/flexbox-flex-direction-row.htm.png deleted file mode 100644 index a23260e43..000000000 Binary files a/test/render/flex/flexbox-flex-direction-row.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-flex-flow-001.htm b/test/render/flex/flexbox-flex-flow-001.htm deleted file mode 100644 index 1d8996d4d..000000000 --- a/test/render/flex/flexbox-flex-flow-001.htm +++ /dev/null @@ -1,129 +0,0 @@ - - - - - CSS Test: Testing all the values of the "flex-flow" shorthand property, with 4 flex items in each container - - - - - - - - -
-
1
2
3
4
-
-
-
1
2
3
4
-
-
-
1
2
3
4
-
-
-
1
2
3
4
-
- -
- - -
-
1
2
3
4
-
-
-
1
2
3
4
-
-
-
1
2
3
4
-
-
-
1
2
3
4
-
- -
- - -
-
1
2
3
4
-
-
-
1
2
3
4
-
-
-
1
2
3
4
-
-
-
1
2
3
4
-
- -
- - -
-
1
2
3
4
-
-
-
1
2
3
4
-
-
-
1
2
3
4
-
-
-
1
2
3
4
-
- -
- - -
-
1
2
3
4
-
-
-
1
2
3
4
-
-
-
1
2
3
4
-
-
-
1
2
3
4
-
- -
- - -
-
1
2
3
4
-
-
-
1
2
3
4
-
- - - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-flex-flow-001.htm.png b/test/render/flex/flexbox-flex-flow-001.htm.png deleted file mode 100644 index d210b04d3..000000000 Binary files a/test/render/flex/flexbox-flex-flow-001.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-flex-flow-002.htm b/test/render/flex/flexbox-flex-flow-002.htm deleted file mode 100644 index 17465ae2b..000000000 --- a/test/render/flex/flexbox-flex-flow-002.htm +++ /dev/null @@ -1,129 +0,0 @@ - - - - - CSS Test: Testing all the values of the "flex-flow" shorthand property, with 3 flex items in each container - - - - - - - - -
-
1
2
3
-
-
-
1
2
3
-
-
-
1
2
3
-
-
-
1
2
3
-
- -
- - -
-
1
2
3
-
-
-
1
2
3
-
-
-
1
2
3
-
-
-
1
2
3
-
- -
- - -
-
1
2
3
-
-
-
1
2
3
-
-
-
1
2
3
-
-
-
1
2
3
-
- -
- - -
-
1
2
3
-
-
-
1
2
3
-
-
-
1
2
3
-
-
-
1
2
3
-
- -
- - -
-
1
2
3
-
-
-
1
2
3
-
-
-
1
2
3
-
-
-
1
2
3
-
- -
- - -
-
1
2
3
-
-
-
1
2
3
-
- - - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-flex-flow-002.htm.png b/test/render/flex/flexbox-flex-flow-002.htm.png deleted file mode 100644 index ec95fc23b..000000000 Binary files a/test/render/flex/flexbox-flex-flow-002.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-flex-wrap-default.htm b/test/render/flex/flexbox-flex-wrap-default.htm deleted file mode 100644 index 5741eb5eb..000000000 --- a/test/render/flex/flexbox-flex-wrap-default.htm +++ /dev/null @@ -1,43 +0,0 @@ - - - - - CSS Flexbox Test: Flex-wrap defaults to nowrap - - - - - - - - - -

The test passes if there is a green square and no red.

-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-flex-wrap-default.htm.png b/test/render/flex/flexbox-flex-wrap-default.htm.png deleted file mode 100644 index 5c432eedf..000000000 Binary files a/test/render/flex/flexbox-flex-wrap-default.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-flex-wrap-flexing.htm b/test/render/flex/flexbox-flex-wrap-flexing.htm deleted file mode 100644 index 72f823b91..000000000 --- a/test/render/flex/flexbox-flex-wrap-flexing.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - - - CSS Flexbox Test: flex-wrap flexes widths after line breaking - - - - - - - - - - - -

Test passes if there is a green rectangle and no red.

-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-flex-wrap-flexing.htm.png b/test/render/flex/flexbox-flex-wrap-flexing.htm.png deleted file mode 100644 index 1b9a92e7b..000000000 Binary files a/test/render/flex/flexbox-flex-wrap-flexing.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-flex-wrap-horiz-001.htm b/test/render/flex/flexbox-flex-wrap-horiz-001.htm deleted file mode 100644 index 37bd9dda9..000000000 --- a/test/render/flex/flexbox-flex-wrap-horiz-001.htm +++ /dev/null @@ -1,100 +0,0 @@ - - - - - CSS Test: Testing flex-wrap in horizontal flex containers - - - - - - - - - -
-
-
- - -
-
-
- - -
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
- - -
-
-
- - -
-
-
-
- - -
-
-
-
- - - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-flex-wrap-horiz-001.htm.png b/test/render/flex/flexbox-flex-wrap-horiz-001.htm.png deleted file mode 100644 index 703987050..000000000 Binary files a/test/render/flex/flexbox-flex-wrap-horiz-001.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-flex-wrap-horiz-002.htm b/test/render/flex/flexbox-flex-wrap-horiz-002.htm deleted file mode 100644 index 019b53ae6..000000000 --- a/test/render/flex/flexbox-flex-wrap-horiz-002.htm +++ /dev/null @@ -1,64 +0,0 @@ - - - - - CSS Test: Ensure that min-width is honored for horizontal multi-line flex containers - - - - - - - - - -
-
- - -
-
-
- - -
-
-
-
-
-
-
- - -
-
-
-
-
-
-
- - - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-flex-wrap-horiz-002.htm.png b/test/render/flex/flexbox-flex-wrap-horiz-002.htm.png deleted file mode 100644 index 062333d6e..000000000 Binary files a/test/render/flex/flexbox-flex-wrap-horiz-002.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-flex-wrap-nowrap.htm b/test/render/flex/flexbox-flex-wrap-nowrap.htm deleted file mode 100644 index 9a287b905..000000000 --- a/test/render/flex/flexbox-flex-wrap-nowrap.htm +++ /dev/null @@ -1,44 +0,0 @@ - - - - - CSS Flexbox Test: Flex-wrap = nowrap - - - - - - - - - -

The test passes if there is a green square and no red.

-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-flex-wrap-nowrap.htm.png b/test/render/flex/flexbox-flex-wrap-nowrap.htm.png deleted file mode 100644 index 5c432eedf..000000000 Binary files a/test/render/flex/flexbox-flex-wrap-nowrap.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-flex-wrap-vert-001.htm b/test/render/flex/flexbox-flex-wrap-vert-001.htm deleted file mode 100644 index 069dfcbcc..000000000 --- a/test/render/flex/flexbox-flex-wrap-vert-001.htm +++ /dev/null @@ -1,102 +0,0 @@ - - - - - CSS Test: Testing flex-wrap in vertical flex containers - - - - - - - - - -
-
-
- - -
-
-
- - -
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
- - -
-
-
- - -
-
-
-
- - -
-
-
-
- - - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-flex-wrap-vert-001.htm.png b/test/render/flex/flexbox-flex-wrap-vert-001.htm.png deleted file mode 100644 index c4c60aa5d..000000000 Binary files a/test/render/flex/flexbox-flex-wrap-vert-001.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-flex-wrap-vert-002.htm b/test/render/flex/flexbox-flex-wrap-vert-002.htm deleted file mode 100644 index 649943701..000000000 --- a/test/render/flex/flexbox-flex-wrap-vert-002.htm +++ /dev/null @@ -1,64 +0,0 @@ - - - - - CSS Test: Ensure that min-height is honored for vertical multi-line flex containers - - - - - - - - - -
-
- - -
-
-
- - -
-
-
-
-
-
-
- - -
-
-
-
-
-
-
- - - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-flex-wrap-vert-002.htm.png b/test/render/flex/flexbox-flex-wrap-vert-002.htm.png deleted file mode 100644 index ae6c9077f..000000000 Binary files a/test/render/flex/flexbox-flex-wrap-vert-002.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-flex-wrap-wrap.htm b/test/render/flex/flexbox-flex-wrap-wrap.htm deleted file mode 100644 index d14b2082e..000000000 --- a/test/render/flex/flexbox-flex-wrap-wrap.htm +++ /dev/null @@ -1,60 +0,0 @@ - - - - - CSS Flexbox Test: Flex-wrap = wrap - - - - - - - - - -

The test passes if there is a 3x3 grid of green squares, numbered 1-9 left-to-right and top-to-bottom, and there is no red.

-
-
1
2
3
-
4
5
6
-
7
8
9
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-flex-wrap-wrap.htm.png b/test/render/flex/flexbox-flex-wrap-wrap.htm.png deleted file mode 100644 index a23260e43..000000000 Binary files a/test/render/flex/flexbox-flex-wrap-wrap.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-items-as-stacking-contexts-002.htm b/test/render/flex/flexbox-items-as-stacking-contexts-002.htm deleted file mode 100644 index 593dfb543..000000000 --- a/test/render/flex/flexbox-items-as-stacking-contexts-002.htm +++ /dev/null @@ -1,73 +0,0 @@ - - - - - CSS Test: Testing that flex items paint as pseudo-stacking contexts (like inline-blocks): atomically, in the absence of 'z-index' on descendants - - - - - - - -
ThisIsALongUnbrokenString
HereIsSomeMoreLongText
- - -
ThisIsALongUnbrokenString
HereIsSomeMoreLongText
- - -
ThisIsALongUnbrokenString
HereIsSomeMoreLongText
- - -
ThisIsALongUnbrokenString
HereIsSomeMoreLongText
- - -
ThisIsALongUnbrokenString
HereIsSomeMoreLongText
- - -
ThisIsALongUnbrokenString
HereIsSomeMoreLongText
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-items-as-stacking-contexts-002.htm.png b/test/render/flex/flexbox-items-as-stacking-contexts-002.htm.png deleted file mode 100644 index 0972738ba..000000000 Binary files a/test/render/flex/flexbox-items-as-stacking-contexts-002.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-items-as-stacking-contexts-003.htm b/test/render/flex/flexbox-items-as-stacking-contexts-003.htm deleted file mode 100644 index 8eb99bc4b..000000000 --- a/test/render/flex/flexbox-items-as-stacking-contexts-003.htm +++ /dev/null @@ -1,73 +0,0 @@ - - - - - CSS Test: Testing that flex items paint as pseudo-stacking contexts (like inline-blocks), instead of full stacking contexts: 'z-index' should let descendants interleave - - - - - - - -
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-items-as-stacking-contexts-003.htm.png b/test/render/flex/flexbox-items-as-stacking-contexts-003.htm.png deleted file mode 100644 index 05fd667bc..000000000 Binary files a/test/render/flex/flexbox-items-as-stacking-contexts-003.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-justify-content-horiz-001a.htm b/test/render/flex/flexbox-justify-content-horiz-001a.htm deleted file mode 100644 index 4a646858f..000000000 --- a/test/render/flex/flexbox-justify-content-horiz-001a.htm +++ /dev/null @@ -1,141 +0,0 @@ - - - - - - CSS Test: Testing 'justify-content' in a horizontal flex container - - - - - - - - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-justify-content-horiz-001a.htm.png b/test/render/flex/flexbox-justify-content-horiz-001a.htm.png deleted file mode 100644 index fb552056b..000000000 Binary files a/test/render/flex/flexbox-justify-content-horiz-001a.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-justify-content-horiz-001b.htm b/test/render/flex/flexbox-justify-content-horiz-001b.htm deleted file mode 100644 index 283664a18..000000000 --- a/test/render/flex/flexbox-justify-content-horiz-001b.htm +++ /dev/null @@ -1,147 +0,0 @@ - - - - - - CSS Test: Testing 'justify-content' in a horizontal flex container with "min-width" - - - - - - - - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-justify-content-horiz-001b.htm.png b/test/render/flex/flexbox-justify-content-horiz-001b.htm.png deleted file mode 100644 index 1ecc9135f..000000000 Binary files a/test/render/flex/flexbox-justify-content-horiz-001b.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-justify-content-horiz-002.htm b/test/render/flex/flexbox-justify-content-horiz-002.htm deleted file mode 100644 index 74bc2d44d..000000000 --- a/test/render/flex/flexbox-justify-content-horiz-002.htm +++ /dev/null @@ -1,154 +0,0 @@ - - - - - - CSS Test: Testing 'justify-content' in a horizontal flex container, with margins/border/padding on flex items - - - - - - - - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-justify-content-horiz-002.htm.png b/test/render/flex/flexbox-justify-content-horiz-002.htm.png deleted file mode 100644 index 76db4f6a2..000000000 Binary files a/test/render/flex/flexbox-justify-content-horiz-002.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-justify-content-horiz-003.htm b/test/render/flex/flexbox-justify-content-horiz-003.htm deleted file mode 100644 index a95237b4f..000000000 --- a/test/render/flex/flexbox-justify-content-horiz-003.htm +++ /dev/null @@ -1,149 +0,0 @@ - - - - - - CSS Test: Testing 'justify-content' in a horizontal flex container, and its effects on flex items that overflow - - - - - - - - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-justify-content-horiz-003.htm.png b/test/render/flex/flexbox-justify-content-horiz-003.htm.png deleted file mode 100644 index ddc562505..000000000 Binary files a/test/render/flex/flexbox-justify-content-horiz-003.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-justify-content-horiz-004.htm b/test/render/flex/flexbox-justify-content-horiz-004.htm deleted file mode 100644 index c696f7e0d..000000000 --- a/test/render/flex/flexbox-justify-content-horiz-004.htm +++ /dev/null @@ -1,160 +0,0 @@ - - - - - - CSS Test: Testing 'justify-content' in a horizontal flex container, and its effects on flex items that overflow, with margins/border/padding on flex items - - - - - - - - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-justify-content-horiz-004.htm.png b/test/render/flex/flexbox-justify-content-horiz-004.htm.png deleted file mode 100644 index 0e949ed0a..000000000 Binary files a/test/render/flex/flexbox-justify-content-horiz-004.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-justify-content-horiz-005.htm b/test/render/flex/flexbox-justify-content-horiz-005.htm deleted file mode 100644 index 4c57dfd3d..000000000 --- a/test/render/flex/flexbox-justify-content-horiz-005.htm +++ /dev/null @@ -1,140 +0,0 @@ - - - - - - CSS Test: Testing 'justify-content' in an auto-sized horizontal flex container - - - - - - - - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-justify-content-horiz-005.htm.png b/test/render/flex/flexbox-justify-content-horiz-005.htm.png deleted file mode 100644 index d4ca3d127..000000000 Binary files a/test/render/flex/flexbox-justify-content-horiz-005.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-justify-content-horiz-006.htm b/test/render/flex/flexbox-justify-content-horiz-006.htm deleted file mode 100644 index 91151e3cc..000000000 --- a/test/render/flex/flexbox-justify-content-horiz-006.htm +++ /dev/null @@ -1,142 +0,0 @@ - - - - - - CSS Test: Testing 'justify-content' in an auto-sized reversed horizontal flex container - - - - - - - - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-justify-content-horiz-006.htm.png b/test/render/flex/flexbox-justify-content-horiz-006.htm.png deleted file mode 100644 index 0d19f686b..000000000 Binary files a/test/render/flex/flexbox-justify-content-horiz-006.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-justify-content-vert-001a.htm b/test/render/flex/flexbox-justify-content-vert-001a.htm deleted file mode 100644 index a8e1335a3..000000000 --- a/test/render/flex/flexbox-justify-content-vert-001a.htm +++ /dev/null @@ -1,143 +0,0 @@ - - - - - - CSS Test: Testing 'justify-content' in a vertical flex container - - - - - - - - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-justify-content-vert-001a.htm.png b/test/render/flex/flexbox-justify-content-vert-001a.htm.png deleted file mode 100644 index 86cdcec97..000000000 Binary files a/test/render/flex/flexbox-justify-content-vert-001a.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-justify-content-vert-001b.htm b/test/render/flex/flexbox-justify-content-vert-001b.htm deleted file mode 100644 index 4f68232b4..000000000 --- a/test/render/flex/flexbox-justify-content-vert-001b.htm +++ /dev/null @@ -1,144 +0,0 @@ - - - - - - CSS Test: Testing 'justify-content' in a vertical flex container with "min-height" - - - - - - - - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-justify-content-vert-001b.htm.png b/test/render/flex/flexbox-justify-content-vert-001b.htm.png deleted file mode 100644 index 86cdcec97..000000000 Binary files a/test/render/flex/flexbox-justify-content-vert-001b.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-justify-content-vert-002.htm b/test/render/flex/flexbox-justify-content-vert-002.htm deleted file mode 100644 index 3416228ee..000000000 --- a/test/render/flex/flexbox-justify-content-vert-002.htm +++ /dev/null @@ -1,156 +0,0 @@ - - - - - - CSS Test: Testing 'justify-content' in a vertical flex container, with margins/border/padding on flex items - - - - - - - - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-justify-content-vert-002.htm.png b/test/render/flex/flexbox-justify-content-vert-002.htm.png deleted file mode 100644 index c2605500a..000000000 Binary files a/test/render/flex/flexbox-justify-content-vert-002.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-justify-content-vert-003.htm b/test/render/flex/flexbox-justify-content-vert-003.htm deleted file mode 100644 index d742f568b..000000000 --- a/test/render/flex/flexbox-justify-content-vert-003.htm +++ /dev/null @@ -1,152 +0,0 @@ - - - - - - CSS Test: Testing 'justify-content' in a vertical flex container, and its effects on flex items that overflow - - - - - - - - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-justify-content-vert-003.htm.png b/test/render/flex/flexbox-justify-content-vert-003.htm.png deleted file mode 100644 index 3f2fe7914..000000000 Binary files a/test/render/flex/flexbox-justify-content-vert-003.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-justify-content-vert-004.htm b/test/render/flex/flexbox-justify-content-vert-004.htm deleted file mode 100644 index f64737814..000000000 --- a/test/render/flex/flexbox-justify-content-vert-004.htm +++ /dev/null @@ -1,163 +0,0 @@ - - - - - - CSS Test: Testing 'justify-content' in a vertical flex container, and its effects on flex items that overflow, with margins/border/padding on flex items - - - - - - - - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-justify-content-vert-004.htm.png b/test/render/flex/flexbox-justify-content-vert-004.htm.png deleted file mode 100644 index b162fab68..000000000 Binary files a/test/render/flex/flexbox-justify-content-vert-004.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-justify-content-vert-005.htm b/test/render/flex/flexbox-justify-content-vert-005.htm deleted file mode 100644 index c1a2a5b2c..000000000 --- a/test/render/flex/flexbox-justify-content-vert-005.htm +++ /dev/null @@ -1,146 +0,0 @@ - - - - - - CSS Test: Testing 'justify-content' in an auto-sized vertical flex container - - - - - - - - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-justify-content-vert-005.htm.png b/test/render/flex/flexbox-justify-content-vert-005.htm.png deleted file mode 100644 index 0b6fd66bf..000000000 Binary files a/test/render/flex/flexbox-justify-content-vert-005.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-justify-content-vert-006.htm b/test/render/flex/flexbox-justify-content-vert-006.htm deleted file mode 100644 index 9cb36b1e8..000000000 --- a/test/render/flex/flexbox-justify-content-vert-006.htm +++ /dev/null @@ -1,143 +0,0 @@ - - - - - - CSS Test: Testing 'justify-content' in a vertical flex container - - - - - - - - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-justify-content-vert-006.htm.png b/test/render/flex/flexbox-justify-content-vert-006.htm.png deleted file mode 100644 index 88aae81b3..000000000 Binary files a/test/render/flex/flexbox-justify-content-vert-006.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-margin-auto-horiz-001.htm b/test/render/flex/flexbox-margin-auto-horiz-001.htm deleted file mode 100644 index 5bf136e96..000000000 --- a/test/render/flex/flexbox-margin-auto-horiz-001.htm +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - CSS Test: Testing horizontal auto margins on flex items in a horizontal flex container - - - - - - - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-margin-auto-horiz-001.htm.png b/test/render/flex/flexbox-margin-auto-horiz-001.htm.png deleted file mode 100644 index 5b4845656..000000000 Binary files a/test/render/flex/flexbox-margin-auto-horiz-001.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-margin-auto-horiz-002.htm b/test/render/flex/flexbox-margin-auto-horiz-002.htm deleted file mode 100644 index 0ac66b020..000000000 --- a/test/render/flex/flexbox-margin-auto-horiz-002.htm +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - CSS Test: Testing vertical auto margins on flex items in a horizontal flex container - - - - - - - - -
-
-
-
-
- -
-
-
-
- - -
-
-
-
-
-
- - -
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-margin-auto-horiz-002.htm.png b/test/render/flex/flexbox-margin-auto-horiz-002.htm.png deleted file mode 100644 index ce44257d5..000000000 Binary files a/test/render/flex/flexbox-margin-auto-horiz-002.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-mbp-horiz-001-reverse.htm b/test/render/flex/flexbox-mbp-horiz-001-reverse.htm deleted file mode 100644 index 389c222d4..000000000 --- a/test/render/flex/flexbox-mbp-horiz-001-reverse.htm +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - CSS Test: Testing borders on flex items in a row-reverse horizontal flex container - - - - - - -
-
-
-
-
-
-
-
-
- -
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-mbp-horiz-001-reverse.htm.png b/test/render/flex/flexbox-mbp-horiz-001-reverse.htm.png deleted file mode 100644 index f6f23db31..000000000 Binary files a/test/render/flex/flexbox-mbp-horiz-001-reverse.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-mbp-horiz-001.htm b/test/render/flex/flexbox-mbp-horiz-001.htm deleted file mode 100644 index ea948a10f..000000000 --- a/test/render/flex/flexbox-mbp-horiz-001.htm +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - CSS Test: Testing borders on flex items in a horizontal flex container - - - - - - -
-
-
-
-
-
-
-
-
- -
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-mbp-horiz-001.htm.png b/test/render/flex/flexbox-mbp-horiz-001.htm.png deleted file mode 100644 index cec56939e..000000000 Binary files a/test/render/flex/flexbox-mbp-horiz-001.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-mbp-horiz-002a.htm b/test/render/flex/flexbox-mbp-horiz-002a.htm deleted file mode 100644 index 02ab2ec17..000000000 --- a/test/render/flex/flexbox-mbp-horiz-002a.htm +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - CSS Test: Testing margins and borders on flex items in a horizontal flex container - - - - - - -
-
-
-
-
-
-
-
-
- -
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-mbp-horiz-002a.htm.png b/test/render/flex/flexbox-mbp-horiz-002a.htm.png deleted file mode 100644 index 8f94167fa..000000000 Binary files a/test/render/flex/flexbox-mbp-horiz-002a.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-mbp-horiz-002b.htm b/test/render/flex/flexbox-mbp-horiz-002b.htm deleted file mode 100644 index c1f7cfbc3..000000000 --- a/test/render/flex/flexbox-mbp-horiz-002b.htm +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - CSS Test: Testing margins, borders, and padding on flex items in a horizontal flex container - - - - - - -
-
-
-
-
-
-
-
-
- -
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-mbp-horiz-002b.htm.png b/test/render/flex/flexbox-mbp-horiz-002b.htm.png deleted file mode 100644 index 8f94167fa..000000000 Binary files a/test/render/flex/flexbox-mbp-horiz-002b.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-mbp-horiz-003-reverse.htm b/test/render/flex/flexbox-mbp-horiz-003-reverse.htm deleted file mode 100644 index 0417ff1eb..000000000 --- a/test/render/flex/flexbox-mbp-horiz-003-reverse.htm +++ /dev/null @@ -1,77 +0,0 @@ - - - - - - CSS Test: Testing borders and padding on a row-reverse horizontal flex container and its flex items - - - - - - -
-
-
-
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-mbp-horiz-003-reverse.htm.png b/test/render/flex/flexbox-mbp-horiz-003-reverse.htm.png deleted file mode 100644 index 8f27a8800..000000000 Binary files a/test/render/flex/flexbox-mbp-horiz-003-reverse.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-mbp-horiz-003.htm b/test/render/flex/flexbox-mbp-horiz-003.htm deleted file mode 100644 index 13e851e9a..000000000 --- a/test/render/flex/flexbox-mbp-horiz-003.htm +++ /dev/null @@ -1,76 +0,0 @@ - - - - - - CSS Test: Testing borders and padding on a horizontal flex container and its flex items - - - - - - -
-
-
-
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-mbp-horiz-003.htm.png b/test/render/flex/flexbox-mbp-horiz-003.htm.png deleted file mode 100644 index bd3320918..000000000 Binary files a/test/render/flex/flexbox-mbp-horiz-003.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-order-from-lowest.htm b/test/render/flex/flexbox-order-from-lowest.htm deleted file mode 100644 index fb5cf1247..000000000 --- a/test/render/flex/flexbox-order-from-lowest.htm +++ /dev/null @@ -1,39 +0,0 @@ - - - - - CSS Test: flex container layout starts with lowest order item - - - - - - - - -

Test passes if the paragraph below reads 'First,Second,Third'.

-
-

Third

-

Second,

-

First,

-
- - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-order-from-lowest.htm.png b/test/render/flex/flexbox-order-from-lowest.htm.png deleted file mode 100644 index 91f98b3a9..000000000 Binary files a/test/render/flex/flexbox-order-from-lowest.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-order-only-flexitems.htm b/test/render/flex/flexbox-order-only-flexitems.htm deleted file mode 100644 index 4211ac1c1..000000000 --- a/test/render/flex/flexbox-order-only-flexitems.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - - - CSS Test: order only affects flex items - - - - - - - - - -

Test passes if the paragraph below reads 'First, Second, Third'.

-
- First, - Second, - Third -
- - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-order-only-flexitems.htm.png b/test/render/flex/flexbox-order-only-flexitems.htm.png deleted file mode 100644 index 1c26ed8b0..000000000 Binary files a/test/render/flex/flexbox-order-only-flexitems.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-overflow-horiz-001.htm b/test/render/flex/flexbox-overflow-horiz-001.htm deleted file mode 100644 index 8e7699ecf..000000000 --- a/test/render/flex/flexbox-overflow-horiz-001.htm +++ /dev/null @@ -1,58 +0,0 @@ - - - - - CSS Test: Testing 'overflow' property on a horizontal flex container, with contents not overflowing - - - - - - -
-
-
-
- -
-
-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-overflow-horiz-001.htm.png b/test/render/flex/flexbox-overflow-horiz-001.htm.png deleted file mode 100644 index 84a85b522..000000000 Binary files a/test/render/flex/flexbox-overflow-horiz-001.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-overflow-horiz-002.htm b/test/render/flex/flexbox-overflow-horiz-002.htm deleted file mode 100644 index 5eec845ec..000000000 --- a/test/render/flex/flexbox-overflow-horiz-002.htm +++ /dev/null @@ -1,54 +0,0 @@ - - - - - CSS Test: Testing 'overflow' property on a horizontal flex container, with 'align-items: center' - - - - - - -
-
-
-
- - - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-overflow-horiz-002.htm.png b/test/render/flex/flexbox-overflow-horiz-002.htm.png deleted file mode 100644 index a052a42ca..000000000 Binary files a/test/render/flex/flexbox-overflow-horiz-002.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-overflow-horiz-003.htm b/test/render/flex/flexbox-overflow-horiz-003.htm deleted file mode 100644 index dc587e4b2..000000000 --- a/test/render/flex/flexbox-overflow-horiz-003.htm +++ /dev/null @@ -1,51 +0,0 @@ - - - - - CSS Test: Testing 'overflow' property on a horizontal flex container, with 'justify-content: space-around' - - - - - - -
-
-
-
- - - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-overflow-horiz-003.htm.png b/test/render/flex/flexbox-overflow-horiz-003.htm.png deleted file mode 100644 index 18efa69cd..000000000 Binary files a/test/render/flex/flexbox-overflow-horiz-003.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-overflow-horiz-004.htm b/test/render/flex/flexbox-overflow-horiz-004.htm deleted file mode 100644 index 91945057a..000000000 --- a/test/render/flex/flexbox-overflow-horiz-004.htm +++ /dev/null @@ -1,51 +0,0 @@ - - - - - CSS Test: Testing 'overflow' property on a horizontal flex container, with 'flex-wrap: wrap' - - - - - - -
-
-
-
- - - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-overflow-horiz-004.htm.png b/test/render/flex/flexbox-overflow-horiz-004.htm.png deleted file mode 100644 index 12842f7dd..000000000 Binary files a/test/render/flex/flexbox-overflow-horiz-004.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-overflow-horiz-005.htm b/test/render/flex/flexbox-overflow-horiz-005.htm deleted file mode 100644 index 83d57fbe6..000000000 --- a/test/render/flex/flexbox-overflow-horiz-005.htm +++ /dev/null @@ -1,53 +0,0 @@ - - - - - CSS Test: Testing 'overflow' property on a horizontal flex container, with 'align-content: space-around' - - - - - - -
-
-
-
- - - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-overflow-horiz-005.htm.png b/test/render/flex/flexbox-overflow-horiz-005.htm.png deleted file mode 100644 index 4bdd01bf5..000000000 Binary files a/test/render/flex/flexbox-overflow-horiz-005.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-overflow-vert-001.htm b/test/render/flex/flexbox-overflow-vert-001.htm deleted file mode 100644 index 5aaf5fda6..000000000 --- a/test/render/flex/flexbox-overflow-vert-001.htm +++ /dev/null @@ -1,58 +0,0 @@ - - - - - CSS Test: Testing 'overflow' property on a vertical flex container - - - - - - -
-
-
-
- -
-
-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-overflow-vert-001.htm.png b/test/render/flex/flexbox-overflow-vert-001.htm.png deleted file mode 100644 index 5ecaedfdc..000000000 Binary files a/test/render/flex/flexbox-overflow-vert-001.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-overflow-vert-002.htm b/test/render/flex/flexbox-overflow-vert-002.htm deleted file mode 100644 index 5085f7c4a..000000000 --- a/test/render/flex/flexbox-overflow-vert-002.htm +++ /dev/null @@ -1,54 +0,0 @@ - - - - - CSS Test: Testing 'overflow' property on a vertical flex container, with 'align-items: center' - - - - - - -
-
-
-
- - - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-overflow-vert-002.htm.png b/test/render/flex/flexbox-overflow-vert-002.htm.png deleted file mode 100644 index f7308d5c1..000000000 Binary files a/test/render/flex/flexbox-overflow-vert-002.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-overflow-vert-003.htm b/test/render/flex/flexbox-overflow-vert-003.htm deleted file mode 100644 index 6a920e443..000000000 --- a/test/render/flex/flexbox-overflow-vert-003.htm +++ /dev/null @@ -1,51 +0,0 @@ - - - - - CSS Test: Testing 'overflow' property on a vertical flex container, with 'justify-content: space-around' - - - - - - -
-
-
-
- - - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-overflow-vert-003.htm.png b/test/render/flex/flexbox-overflow-vert-003.htm.png deleted file mode 100644 index 1102be21f..000000000 Binary files a/test/render/flex/flexbox-overflow-vert-003.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-overflow-vert-004.htm b/test/render/flex/flexbox-overflow-vert-004.htm deleted file mode 100644 index db61fa174..000000000 --- a/test/render/flex/flexbox-overflow-vert-004.htm +++ /dev/null @@ -1,51 +0,0 @@ - - - - - CSS Test: Testing 'overflow' property on a vertical flex container, with 'flex-wrap: wrap' - - - - - - -
-
-
-
- - - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-overflow-vert-004.htm.png b/test/render/flex/flexbox-overflow-vert-004.htm.png deleted file mode 100644 index f22c4ed8b..000000000 Binary files a/test/render/flex/flexbox-overflow-vert-004.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-overflow-vert-005.htm b/test/render/flex/flexbox-overflow-vert-005.htm deleted file mode 100644 index 06026a244..000000000 --- a/test/render/flex/flexbox-overflow-vert-005.htm +++ /dev/null @@ -1,53 +0,0 @@ - - - - - CSS Test: Testing 'overflow' property on a vertical flex container, with 'align-content: space-around' - - - - - - -
-
-
-
- - - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-overflow-vert-005.htm.png b/test/render/flex/flexbox-overflow-vert-005.htm.png deleted file mode 100644 index dff1df57e..000000000 Binary files a/test/render/flex/flexbox-overflow-vert-005.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-paint-ordering-003.htm b/test/render/flex/flexbox-paint-ordering-003.htm deleted file mode 100644 index c3b67dba4..000000000 --- a/test/render/flex/flexbox-paint-ordering-003.htm +++ /dev/null @@ -1,55 +0,0 @@ - - - - - CSS Test: Testing that paint order isn't influenced - by "order" for absolutely positioned flex children - - - - - - - -
- -
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-paint-ordering-003.htm.png b/test/render/flex/flexbox-paint-ordering-003.htm.png deleted file mode 100644 index ec539b62a..000000000 Binary files a/test/render/flex/flexbox-paint-ordering-003.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-root-node-001a.htm b/test/render/flex/flexbox-root-node-001a.htm deleted file mode 100644 index c97d252de..000000000 --- a/test/render/flex/flexbox-root-node-001a.htm +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CSS Test: Testing 'display:flex' on root node - - - - - - - centered - - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-root-node-001a.htm.png b/test/render/flex/flexbox-root-node-001a.htm.png deleted file mode 100644 index 339ccae7b..000000000 Binary files a/test/render/flex/flexbox-root-node-001a.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-root-node-001b.htm b/test/render/flex/flexbox-root-node-001b.htm deleted file mode 100644 index c3048b11e..000000000 --- a/test/render/flex/flexbox-root-node-001b.htm +++ /dev/null @@ -1,24 +0,0 @@ - - - - - CSS Test: Testing 'display:flex' on root node - - - - - -centered - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-root-node-001b.htm.png b/test/render/flex/flexbox-root-node-001b.htm.png deleted file mode 100644 index 339ccae7b..000000000 Binary files a/test/render/flex/flexbox-root-node-001b.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-single-line-clamp-1.htm b/test/render/flex/flexbox-single-line-clamp-1.htm deleted file mode 100644 index 39adff3e1..000000000 --- a/test/render/flex/flexbox-single-line-clamp-1.htm +++ /dev/null @@ -1,36 +0,0 @@ - - - - -CSS Test: Single-line flex containers should clamp their line's height to the container's computed min and max cross-size. - - - - -
-
-
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox-single-line-clamp-1.htm.png b/test/render/flex/flexbox-single-line-clamp-1.htm.png deleted file mode 100644 index 58541d246..000000000 Binary files a/test/render/flex/flexbox-single-line-clamp-1.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-single-line-clamp-2.htm b/test/render/flex/flexbox-single-line-clamp-2.htm deleted file mode 100644 index 70346d7ab..000000000 --- a/test/render/flex/flexbox-single-line-clamp-2.htm +++ /dev/null @@ -1,43 +0,0 @@ - - - - -CSS Test: Single-line flex containers should clamp their line's height to the container's computed min and max cross-size. - - - - -
-
-
-
-
-
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox-single-line-clamp-2.htm.png b/test/render/flex/flexbox-single-line-clamp-2.htm.png deleted file mode 100644 index bc335cb09..000000000 Binary files a/test/render/flex/flexbox-single-line-clamp-2.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-table-fixup-001.htm b/test/render/flex/flexbox-table-fixup-001.htm deleted file mode 100644 index b6dbb12d4..000000000 --- a/test/render/flex/flexbox-table-fixup-001.htm +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - CSS Test: Testing that table cells in a flex container get blockified and each form their own flex item - - - - - - - - -
cell1cell2
- - -
cell1t
- - -
cell1
- - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-table-fixup-001.htm.png b/test/render/flex/flexbox-table-fixup-001.htm.png deleted file mode 100644 index 842f3a923..000000000 Binary files a/test/render/flex/flexbox-table-fixup-001.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-whitespace-handling-002.htm b/test/render/flex/flexbox-whitespace-handling-002.htm deleted file mode 100644 index b63cbb34f..000000000 --- a/test/render/flex/flexbox-whitespace-handling-002.htm +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - CSS Test: Test that whitespace is preserved at the edges of anonymous flex items if 'white-space: pre' is set - - - - - - - - -
abc
-
abc
-
abc
- - -
abc
-
abc
-
abc
- - -
abc
-
abc
-
abc
- - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-whitespace-handling-002.htm.png b/test/render/flex/flexbox-whitespace-handling-002.htm.png deleted file mode 100644 index 2f985adb3..000000000 Binary files a/test/render/flex/flexbox-whitespace-handling-002.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-with-pseudo-elements-001.htm b/test/render/flex/flexbox-with-pseudo-elements-001.htm deleted file mode 100644 index 205dc9c22..000000000 --- a/test/render/flex/flexbox-with-pseudo-elements-001.htm +++ /dev/null @@ -1,58 +0,0 @@ - - - - - CSS Test: Testing that generated content nodes are treated as a flex items - - - - - - - -
- x -
y
- z -
-
- x -
y
- z -
-
- x -
y
- z -
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-with-pseudo-elements-001.htm.png b/test/render/flex/flexbox-with-pseudo-elements-001.htm.png deleted file mode 100644 index 0c0269622..000000000 Binary files a/test/render/flex/flexbox-with-pseudo-elements-001.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox-with-pseudo-elements-002.htm b/test/render/flex/flexbox-with-pseudo-elements-002.htm deleted file mode 100644 index a4e144d72..000000000 --- a/test/render/flex/flexbox-with-pseudo-elements-002.htm +++ /dev/null @@ -1,81 +0,0 @@ - - - - - CSS Test: Testing that generated content nodes are treated as a flex items, and honor 'order' - - - - - - - - -
-
I
-
- - -
-
I
-
- - -
-
I
-
- - -
-
I
-
- - -
-
I
-
- - -
-
I
-
- - -
-
I
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/flexbox-with-pseudo-elements-002.htm.png b/test/render/flex/flexbox-with-pseudo-elements-002.htm.png deleted file mode 100644 index 4f5a43050..000000000 Binary files a/test/render/flex/flexbox-with-pseudo-elements-002.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_absolute-atomic.htm b/test/render/flex/flexbox_absolute-atomic.htm deleted file mode 100644 index a4890645e..000000000 --- a/test/render/flex/flexbox_absolute-atomic.htm +++ /dev/null @@ -1,33 +0,0 @@ - - - -flexbox | abspos atomic flexitems - - - - - -
-
filler
-
filler
-
filler
-
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_absolute-atomic.htm.png b/test/render/flex/flexbox_absolute-atomic.htm.png deleted file mode 100644 index 0b54584b8..000000000 Binary files a/test/render/flex/flexbox_absolute-atomic.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_align-content-center.htm b/test/render/flex/flexbox_align-content-center.htm deleted file mode 100644 index 273d72935..000000000 --- a/test/render/flex/flexbox_align-content-center.htm +++ /dev/null @@ -1,41 +0,0 @@ - - - -flexbox | align-content: center - - - - - -
- one - two - three -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_align-content-center.htm.png b/test/render/flex/flexbox_align-content-center.htm.png deleted file mode 100644 index e949f70c7..000000000 Binary files a/test/render/flex/flexbox_align-content-center.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_align-content-flexend.htm b/test/render/flex/flexbox_align-content-flexend.htm deleted file mode 100644 index 34a770c23..000000000 --- a/test/render/flex/flexbox_align-content-flexend.htm +++ /dev/null @@ -1,41 +0,0 @@ - - - -flexbox | align-content: flex-end - - - - - -
- one - two - three -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_align-content-flexend.htm.png b/test/render/flex/flexbox_align-content-flexend.htm.png deleted file mode 100644 index 0013c5d51..000000000 Binary files a/test/render/flex/flexbox_align-content-flexend.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_align-content-flexstart.htm b/test/render/flex/flexbox_align-content-flexstart.htm deleted file mode 100644 index 6cf2fae9e..000000000 --- a/test/render/flex/flexbox_align-content-flexstart.htm +++ /dev/null @@ -1,41 +0,0 @@ - - - -flexbox | align-content: flex-start - - - - - -
- one - two - three -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_align-content-flexstart.htm.png b/test/render/flex/flexbox_align-content-flexstart.htm.png deleted file mode 100644 index 04c663e33..000000000 Binary files a/test/render/flex/flexbox_align-content-flexstart.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_align-content-spacearound.htm b/test/render/flex/flexbox_align-content-spacearound.htm deleted file mode 100644 index 29c765a49..000000000 --- a/test/render/flex/flexbox_align-content-spacearound.htm +++ /dev/null @@ -1,41 +0,0 @@ - - - -flexbox | align-content: space-around - - - - - -
- one - two - three -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_align-content-spacearound.htm.png b/test/render/flex/flexbox_align-content-spacearound.htm.png deleted file mode 100644 index 9c4fae5dc..000000000 Binary files a/test/render/flex/flexbox_align-content-spacearound.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_align-content-spacebetween.htm b/test/render/flex/flexbox_align-content-spacebetween.htm deleted file mode 100644 index fc21bf7a1..000000000 --- a/test/render/flex/flexbox_align-content-spacebetween.htm +++ /dev/null @@ -1,41 +0,0 @@ - - - -flexbox | align-content: space-between - - - - - -
- one - two - three -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_align-content-spacebetween.htm.png b/test/render/flex/flexbox_align-content-spacebetween.htm.png deleted file mode 100644 index b305e476b..000000000 Binary files a/test/render/flex/flexbox_align-content-spacebetween.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_align-content-stretch-2.htm b/test/render/flex/flexbox_align-content-stretch-2.htm deleted file mode 100644 index 47ddf2f76..000000000 --- a/test/render/flex/flexbox_align-content-stretch-2.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | align-content: stretch - - - - - -
- one - two - three -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_align-content-stretch-2.htm.png b/test/render/flex/flexbox_align-content-stretch-2.htm.png deleted file mode 100644 index cfb61e969..000000000 Binary files a/test/render/flex/flexbox_align-content-stretch-2.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_align-content-stretch.htm b/test/render/flex/flexbox_align-content-stretch.htm deleted file mode 100644 index 86b4f076a..000000000 --- a/test/render/flex/flexbox_align-content-stretch.htm +++ /dev/null @@ -1,41 +0,0 @@ - - - -flexbox | align-content: stretch - - - - - -
- one - two - three -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_align-content-stretch.htm.png b/test/render/flex/flexbox_align-content-stretch.htm.png deleted file mode 100644 index 3cb58461d..000000000 Binary files a/test/render/flex/flexbox_align-content-stretch.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_align-items-baseline.htm b/test/render/flex/flexbox_align-items-baseline.htm deleted file mode 100644 index eb2d0101b..000000000 --- a/test/render/flex/flexbox_align-items-baseline.htm +++ /dev/null @@ -1,47 +0,0 @@ - - - -flexbox | align-items: baseline - - - - - -
- one - two - three -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_align-items-baseline.htm.png b/test/render/flex/flexbox_align-items-baseline.htm.png deleted file mode 100644 index 2b5568240..000000000 Binary files a/test/render/flex/flexbox_align-items-baseline.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_align-items-center-2.htm b/test/render/flex/flexbox_align-items-center-2.htm deleted file mode 100644 index 0881d30ac..000000000 --- a/test/render/flex/flexbox_align-items-center-2.htm +++ /dev/null @@ -1,46 +0,0 @@ - - - -flexbox | align-items: center - - - - - -
- one - two - three -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_align-items-center-2.htm.png b/test/render/flex/flexbox_align-items-center-2.htm.png deleted file mode 100644 index a4998fd8f..000000000 Binary files a/test/render/flex/flexbox_align-items-center-2.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_align-items-center.htm b/test/render/flex/flexbox_align-items-center.htm deleted file mode 100644 index d73dfee43..000000000 --- a/test/render/flex/flexbox_align-items-center.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | align-items: center - - - - - -
- one - two - three -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_align-items-center.htm.png b/test/render/flex/flexbox_align-items-center.htm.png deleted file mode 100644 index 13c983766..000000000 Binary files a/test/render/flex/flexbox_align-items-center.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_align-items-flexend-2.htm b/test/render/flex/flexbox_align-items-flexend-2.htm deleted file mode 100644 index c33c03151..000000000 --- a/test/render/flex/flexbox_align-items-flexend-2.htm +++ /dev/null @@ -1,45 +0,0 @@ - - - -flexbox | align-items: flex-end - - - - - -
- one - two - three -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_align-items-flexend-2.htm.png b/test/render/flex/flexbox_align-items-flexend-2.htm.png deleted file mode 100644 index d755125b5..000000000 Binary files a/test/render/flex/flexbox_align-items-flexend-2.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_align-items-flexend.htm b/test/render/flex/flexbox_align-items-flexend.htm deleted file mode 100644 index 83a9d3cb4..000000000 --- a/test/render/flex/flexbox_align-items-flexend.htm +++ /dev/null @@ -1,39 +0,0 @@ - - - -flexbox | align-items: flex-end - - - - - -
- one - two - three -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_align-items-flexend.htm.png b/test/render/flex/flexbox_align-items-flexend.htm.png deleted file mode 100644 index 81acb6161..000000000 Binary files a/test/render/flex/flexbox_align-items-flexend.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_align-items-flexstart-2.htm b/test/render/flex/flexbox_align-items-flexstart-2.htm deleted file mode 100644 index 61a93deed..000000000 --- a/test/render/flex/flexbox_align-items-flexstart-2.htm +++ /dev/null @@ -1,45 +0,0 @@ - - - -flexbox | align-items: flex-start - - - - - -
- one - two - three -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_align-items-flexstart-2.htm.png b/test/render/flex/flexbox_align-items-flexstart-2.htm.png deleted file mode 100644 index f2a887bd6..000000000 Binary files a/test/render/flex/flexbox_align-items-flexstart-2.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_align-items-flexstart.htm b/test/render/flex/flexbox_align-items-flexstart.htm deleted file mode 100644 index 19e850491..000000000 --- a/test/render/flex/flexbox_align-items-flexstart.htm +++ /dev/null @@ -1,39 +0,0 @@ - - - -flexbox | align-items: flex-start - - - - - -
- one - two - three -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_align-items-flexstart.htm.png b/test/render/flex/flexbox_align-items-flexstart.htm.png deleted file mode 100644 index 0ca4d0440..000000000 Binary files a/test/render/flex/flexbox_align-items-flexstart.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_align-items-stretch-2.htm b/test/render/flex/flexbox_align-items-stretch-2.htm deleted file mode 100644 index 9bd23b324..000000000 --- a/test/render/flex/flexbox_align-items-stretch-2.htm +++ /dev/null @@ -1,36 +0,0 @@ - - - -flexbox | align-items: stretch - - - - - -
- PASS - - x -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_align-items-stretch-2.htm.png b/test/render/flex/flexbox_align-items-stretch-2.htm.png deleted file mode 100644 index 3ac891b91..000000000 Binary files a/test/render/flex/flexbox_align-items-stretch-2.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_align-items-stretch.htm b/test/render/flex/flexbox_align-items-stretch.htm deleted file mode 100644 index 9dec9f372..000000000 --- a/test/render/flex/flexbox_align-items-stretch.htm +++ /dev/null @@ -1,44 +0,0 @@ - - - -flexbox | align-items: stretch - - - - - -
- one - two - three -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_align-items-stretch.htm.png b/test/render/flex/flexbox_align-items-stretch.htm.png deleted file mode 100644 index 178d4d19e..000000000 Binary files a/test/render/flex/flexbox_align-items-stretch.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_align-self-auto.htm b/test/render/flex/flexbox_align-self-auto.htm deleted file mode 100644 index 14fa46bdd..000000000 --- a/test/render/flex/flexbox_align-self-auto.htm +++ /dev/null @@ -1,42 +0,0 @@ - - - -flexbox | align-self: auto - - - - - -
- one - two - three -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_align-self-auto.htm.png b/test/render/flex/flexbox_align-self-auto.htm.png deleted file mode 100644 index 81acb6161..000000000 Binary files a/test/render/flex/flexbox_align-self-auto.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_align-self-baseline.htm b/test/render/flex/flexbox_align-self-baseline.htm deleted file mode 100644 index 07de6419d..000000000 --- a/test/render/flex/flexbox_align-self-baseline.htm +++ /dev/null @@ -1,43 +0,0 @@ - - - -flexbox | align-self: baseline - - - - - -
- one - two - three -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_align-self-baseline.htm.png b/test/render/flex/flexbox_align-self-baseline.htm.png deleted file mode 100644 index b91ccf0fb..000000000 Binary files a/test/render/flex/flexbox_align-self-baseline.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_align-self-center.htm b/test/render/flex/flexbox_align-self-center.htm deleted file mode 100644 index bec1a7810..000000000 --- a/test/render/flex/flexbox_align-self-center.htm +++ /dev/null @@ -1,43 +0,0 @@ - - - -flexbox | align-self: center - - - - - -
- one - two - three -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_align-self-center.htm.png b/test/render/flex/flexbox_align-self-center.htm.png deleted file mode 100644 index 649ed6ea1..000000000 Binary files a/test/render/flex/flexbox_align-self-center.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_align-self-flexend.htm b/test/render/flex/flexbox_align-self-flexend.htm deleted file mode 100644 index 395137d63..000000000 --- a/test/render/flex/flexbox_align-self-flexend.htm +++ /dev/null @@ -1,42 +0,0 @@ - - - -flexbox | align-self: flex-end - - - - - -
- one - two - three -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_align-self-flexend.htm.png b/test/render/flex/flexbox_align-self-flexend.htm.png deleted file mode 100644 index 129a5c72f..000000000 Binary files a/test/render/flex/flexbox_align-self-flexend.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_align-self-flexstart.htm b/test/render/flex/flexbox_align-self-flexstart.htm deleted file mode 100644 index 1e4519cb9..000000000 --- a/test/render/flex/flexbox_align-self-flexstart.htm +++ /dev/null @@ -1,42 +0,0 @@ - - - -flexbox | align-self: flex-start - - - - - -
- one - two - three -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_align-self-flexstart.htm.png b/test/render/flex/flexbox_align-self-flexstart.htm.png deleted file mode 100644 index 914e7dbe4..000000000 Binary files a/test/render/flex/flexbox_align-self-flexstart.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_align-self-stretch.htm b/test/render/flex/flexbox_align-self-stretch.htm deleted file mode 100644 index a8a94caef..000000000 --- a/test/render/flex/flexbox_align-self-stretch.htm +++ /dev/null @@ -1,44 +0,0 @@ - - - -flexbox | align-self: stretch - - - - - -
- one - two - three -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_align-self-stretch.htm.png b/test/render/flex/flexbox_align-self-stretch.htm.png deleted file mode 100644 index 40f8b2b55..000000000 Binary files a/test/render/flex/flexbox_align-self-stretch.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_box-clear.htm b/test/render/flex/flexbox_box-clear.htm deleted file mode 100644 index 0bc063812..000000000 --- a/test/render/flex/flexbox_box-clear.htm +++ /dev/null @@ -1,34 +0,0 @@ - - - -flexbox | cleared box - - - - - -
filler
- -
-
Yellow box should be below the blue box
-
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_box-clear.htm.png b/test/render/flex/flexbox_box-clear.htm.png deleted file mode 100644 index 4d382dc85..000000000 Binary files a/test/render/flex/flexbox_box-clear.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_columns.htm b/test/render/flex/flexbox_columns.htm deleted file mode 100644 index c598e9097..000000000 --- a/test/render/flex/flexbox_columns.htm +++ /dev/null @@ -1,30 +0,0 @@ - - - -flexbox | multicol - - - - - -
    -
  • one two three four
  • -
  • filler
  • -
  • filler
  • -
  • filler
  • -
  • filler
  • -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_columns.htm.png b/test/render/flex/flexbox_columns.htm.png deleted file mode 100644 index 93570db15..000000000 Binary files a/test/render/flex/flexbox_columns.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_direction-column-reverse.htm b/test/render/flex/flexbox_direction-column-reverse.htm deleted file mode 100644 index 504c3b1c1..000000000 --- a/test/render/flex/flexbox_direction-column-reverse.htm +++ /dev/null @@ -1,36 +0,0 @@ - - - -flexbox | flex-direction: column-reverse - - - - - -
- filler - filler - filler - filler -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_direction-column-reverse.htm.png b/test/render/flex/flexbox_direction-column-reverse.htm.png deleted file mode 100644 index 20822b7d6..000000000 Binary files a/test/render/flex/flexbox_direction-column-reverse.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_direction-column.htm b/test/render/flex/flexbox_direction-column.htm deleted file mode 100644 index 0e7f5bbf1..000000000 --- a/test/render/flex/flexbox_direction-column.htm +++ /dev/null @@ -1,33 +0,0 @@ - - - -flexbox | flex-direction: column - - - - - -
- filler - filler - filler - filler -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_direction-column.htm.png b/test/render/flex/flexbox_direction-column.htm.png deleted file mode 100644 index c54628855..000000000 Binary files a/test/render/flex/flexbox_direction-column.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_direction-row-reverse.htm b/test/render/flex/flexbox_direction-row-reverse.htm deleted file mode 100644 index 6c52282c8..000000000 --- a/test/render/flex/flexbox_direction-row-reverse.htm +++ /dev/null @@ -1,36 +0,0 @@ - - - -flexbox | flex-direction: row-reverse - - - - - -
    -
  • IJKL
  • -
  • ABCD
  • -
  • EFGH
  • -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_direction-row-reverse.htm.png b/test/render/flex/flexbox_direction-row-reverse.htm.png deleted file mode 100644 index be94316ad..000000000 Binary files a/test/render/flex/flexbox_direction-row-reverse.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_display.htm b/test/render/flex/flexbox_display.htm deleted file mode 100644 index 596654e23..000000000 --- a/test/render/flex/flexbox_display.htm +++ /dev/null @@ -1,29 +0,0 @@ - - - -flexbox | display error-handling - - - - - -
    -
  • filler
  • -
  • Antidisestablishmentarianism
  • -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_display.htm.png b/test/render/flex/flexbox_display.htm.png deleted file mode 100644 index 140c04c28..000000000 Binary files a/test/render/flex/flexbox_display.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_first-line.htm b/test/render/flex/flexbox_first-line.htm deleted file mode 100644 index 72789ace1..000000000 --- a/test/render/flex/flexbox_first-line.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | first-line - - - - - -
    -
  • filler
  • -
  • Antidisestablishmentarianism
  • -
  • filler
  • -
  • Antidisestablishmentarianism
  • -
  • Antidisestablishmentarianism
  • -
  • filler
  • -
  • Antidisestablishmentarianism
  • -
  • filler
  • -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_first-line.htm.png b/test/render/flex/flexbox_first-line.htm.png deleted file mode 100644 index e3315bc92..000000000 Binary files a/test/render/flex/flexbox_first-line.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-0-0-0-unitless.htm b/test/render/flex/flexbox_flex-0-0-0-unitless.htm deleted file mode 100644 index 813559015..000000000 --- a/test/render/flex/flexbox_flex-0-0-0-unitless.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 0 0 0 unitless - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-0-0-0-unitless.htm.png b/test/render/flex/flexbox_flex-0-0-0-unitless.htm.png deleted file mode 100644 index 2b11d4515..000000000 Binary files a/test/render/flex/flexbox_flex-0-0-0-unitless.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-0-0-0.htm b/test/render/flex/flexbox_flex-0-0-0.htm deleted file mode 100644 index 58ef45520..000000000 --- a/test/render/flex/flexbox_flex-0-0-0.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 0 0 0 - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-0-0-0.htm.png b/test/render/flex/flexbox_flex-0-0-0.htm.png deleted file mode 100644 index 2b11d4515..000000000 Binary files a/test/render/flex/flexbox_flex-0-0-0.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-0-0-1-unitless-basis.htm b/test/render/flex/flexbox_flex-0-0-1-unitless-basis.htm deleted file mode 100644 index 319a6f442..000000000 --- a/test/render/flex/flexbox_flex-0-0-1-unitless-basis.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 0 0 N unitless - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-0-0-1-unitless-basis.htm.png b/test/render/flex/flexbox_flex-0-0-1-unitless-basis.htm.png deleted file mode 100644 index ab79088f3..000000000 Binary files a/test/render/flex/flexbox_flex-0-0-1-unitless-basis.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-0-0-N-shrink.htm b/test/render/flex/flexbox_flex-0-0-N-shrink.htm deleted file mode 100644 index 9366af697..000000000 --- a/test/render/flex/flexbox_flex-0-0-N-shrink.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 0 0 N | shrinking - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-0-0-N-shrink.htm.png b/test/render/flex/flexbox_flex-0-0-N-shrink.htm.png deleted file mode 100644 index bda4a23ef..000000000 Binary files a/test/render/flex/flexbox_flex-0-0-N-shrink.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-0-0-N-unitless-basis.htm b/test/render/flex/flexbox_flex-0-0-N-unitless-basis.htm deleted file mode 100644 index f83dcb1d5..000000000 --- a/test/render/flex/flexbox_flex-0-0-N-unitless-basis.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 0 0 N unitless - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-0-0-N-unitless-basis.htm.png b/test/render/flex/flexbox_flex-0-0-N-unitless-basis.htm.png deleted file mode 100644 index ab79088f3..000000000 Binary files a/test/render/flex/flexbox_flex-0-0-N-unitless-basis.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-0-0-N.htm b/test/render/flex/flexbox_flex-0-0-N.htm deleted file mode 100644 index 64050ab8d..000000000 --- a/test/render/flex/flexbox_flex-0-0-N.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 0 0 N - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-0-0-N.htm.png b/test/render/flex/flexbox_flex-0-0-N.htm.png deleted file mode 100644 index b45b17234..000000000 Binary files a/test/render/flex/flexbox_flex-0-0-N.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-0-0-Npercent-shrink.htm b/test/render/flex/flexbox_flex-0-0-Npercent-shrink.htm deleted file mode 100644 index 6c0a6721e..000000000 --- a/test/render/flex/flexbox_flex-0-0-Npercent-shrink.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 0 0 N% | shrinking - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-0-0-Npercent-shrink.htm.png b/test/render/flex/flexbox_flex-0-0-Npercent-shrink.htm.png deleted file mode 100644 index c37d2519b..000000000 Binary files a/test/render/flex/flexbox_flex-0-0-Npercent-shrink.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-0-0-Npercent.htm b/test/render/flex/flexbox_flex-0-0-Npercent.htm deleted file mode 100644 index e820bbe99..000000000 --- a/test/render/flex/flexbox_flex-0-0-Npercent.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 0 0 N% - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-0-0-Npercent.htm.png b/test/render/flex/flexbox_flex-0-0-Npercent.htm.png deleted file mode 100644 index d1eb5dbb1..000000000 Binary files a/test/render/flex/flexbox_flex-0-0-Npercent.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-0-0-auto-shrink.htm b/test/render/flex/flexbox_flex-0-0-auto-shrink.htm deleted file mode 100644 index 69b319cf7..000000000 --- a/test/render/flex/flexbox_flex-0-0-auto-shrink.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 0 0 auto | shrinking - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-0-0-auto-shrink.htm.png b/test/render/flex/flexbox_flex-0-0-auto-shrink.htm.png deleted file mode 100644 index 304f96b45..000000000 Binary files a/test/render/flex/flexbox_flex-0-0-auto-shrink.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-0-0-auto.htm b/test/render/flex/flexbox_flex-0-0-auto.htm deleted file mode 100644 index bc167e38b..000000000 --- a/test/render/flex/flexbox_flex-0-0-auto.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 0 0 auto - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-0-0-auto.htm.png b/test/render/flex/flexbox_flex-0-0-auto.htm.png deleted file mode 100644 index ab79088f3..000000000 Binary files a/test/render/flex/flexbox_flex-0-0-auto.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-0-0.htm b/test/render/flex/flexbox_flex-0-0.htm deleted file mode 100644 index 55db99b16..000000000 --- a/test/render/flex/flexbox_flex-0-0.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 0 0 - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-0-0.htm.png b/test/render/flex/flexbox_flex-0-0.htm.png deleted file mode 100644 index 2b11d4515..000000000 Binary files a/test/render/flex/flexbox_flex-0-0.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-0-1-0-unitless.htm b/test/render/flex/flexbox_flex-0-1-0-unitless.htm deleted file mode 100644 index 961145f00..000000000 --- a/test/render/flex/flexbox_flex-0-1-0-unitless.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 0 1 0 unitless - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-0-1-0-unitless.htm.png b/test/render/flex/flexbox_flex-0-1-0-unitless.htm.png deleted file mode 100644 index 2b11d4515..000000000 Binary files a/test/render/flex/flexbox_flex-0-1-0-unitless.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-0-1-0.htm b/test/render/flex/flexbox_flex-0-1-0.htm deleted file mode 100644 index c18705cd4..000000000 --- a/test/render/flex/flexbox_flex-0-1-0.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 0 1 0 - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-0-1-0.htm.png b/test/render/flex/flexbox_flex-0-1-0.htm.png deleted file mode 100644 index 2b11d4515..000000000 Binary files a/test/render/flex/flexbox_flex-0-1-0.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-0-1-1-unitless-basis.htm b/test/render/flex/flexbox_flex-0-1-1-unitless-basis.htm deleted file mode 100644 index d9dfb22d7..000000000 --- a/test/render/flex/flexbox_flex-0-1-1-unitless-basis.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 0 1 1 unitless - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-0-1-1-unitless-basis.htm.png b/test/render/flex/flexbox_flex-0-1-1-unitless-basis.htm.png deleted file mode 100644 index ab79088f3..000000000 Binary files a/test/render/flex/flexbox_flex-0-1-1-unitless-basis.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-0-1-N-shrink.htm b/test/render/flex/flexbox_flex-0-1-N-shrink.htm deleted file mode 100644 index f47609027..000000000 --- a/test/render/flex/flexbox_flex-0-1-N-shrink.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 0 1 N | shrinking - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-0-1-N-shrink.htm.png b/test/render/flex/flexbox_flex-0-1-N-shrink.htm.png deleted file mode 100644 index 0f46f7c74..000000000 Binary files a/test/render/flex/flexbox_flex-0-1-N-shrink.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-0-1-N-unitless-basis.htm b/test/render/flex/flexbox_flex-0-1-N-unitless-basis.htm deleted file mode 100644 index 25f3bb093..000000000 --- a/test/render/flex/flexbox_flex-0-1-N-unitless-basis.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 0 1 N unitless - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-0-1-N-unitless-basis.htm.png b/test/render/flex/flexbox_flex-0-1-N-unitless-basis.htm.png deleted file mode 100644 index ab79088f3..000000000 Binary files a/test/render/flex/flexbox_flex-0-1-N-unitless-basis.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-0-1-N.htm b/test/render/flex/flexbox_flex-0-1-N.htm deleted file mode 100644 index 9c9280ad9..000000000 --- a/test/render/flex/flexbox_flex-0-1-N.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 0 1 N - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-0-1-N.htm.png b/test/render/flex/flexbox_flex-0-1-N.htm.png deleted file mode 100644 index b45b17234..000000000 Binary files a/test/render/flex/flexbox_flex-0-1-N.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-0-1-Npercent-shrink.htm b/test/render/flex/flexbox_flex-0-1-Npercent-shrink.htm deleted file mode 100644 index ce5c0d2cf..000000000 --- a/test/render/flex/flexbox_flex-0-1-Npercent-shrink.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 0 1 N% | shrinking - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-0-1-Npercent-shrink.htm.png b/test/render/flex/flexbox_flex-0-1-Npercent-shrink.htm.png deleted file mode 100644 index 0f46f7c74..000000000 Binary files a/test/render/flex/flexbox_flex-0-1-Npercent-shrink.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-0-1-Npercent.htm b/test/render/flex/flexbox_flex-0-1-Npercent.htm deleted file mode 100644 index fb1fb6abd..000000000 --- a/test/render/flex/flexbox_flex-0-1-Npercent.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 0 1 N% - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-0-1-Npercent.htm.png b/test/render/flex/flexbox_flex-0-1-Npercent.htm.png deleted file mode 100644 index d1eb5dbb1..000000000 Binary files a/test/render/flex/flexbox_flex-0-1-Npercent.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-0-1-auto-shrink.htm b/test/render/flex/flexbox_flex-0-1-auto-shrink.htm deleted file mode 100644 index 3d036bac3..000000000 --- a/test/render/flex/flexbox_flex-0-1-auto-shrink.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 0 1 auto | shrinking - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-0-1-auto-shrink.htm.png b/test/render/flex/flexbox_flex-0-1-auto-shrink.htm.png deleted file mode 100644 index 297113b2c..000000000 Binary files a/test/render/flex/flexbox_flex-0-1-auto-shrink.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-0-1-auto.htm b/test/render/flex/flexbox_flex-0-1-auto.htm deleted file mode 100644 index e4483cd21..000000000 --- a/test/render/flex/flexbox_flex-0-1-auto.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 0 1 auto - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-0-1-auto.htm.png b/test/render/flex/flexbox_flex-0-1-auto.htm.png deleted file mode 100644 index ab79088f3..000000000 Binary files a/test/render/flex/flexbox_flex-0-1-auto.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-0-1.htm b/test/render/flex/flexbox_flex-0-1.htm deleted file mode 100644 index 6593dde60..000000000 --- a/test/render/flex/flexbox_flex-0-1.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 0 1 - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-0-1.htm.png b/test/render/flex/flexbox_flex-0-1.htm.png deleted file mode 100644 index 2b11d4515..000000000 Binary files a/test/render/flex/flexbox_flex-0-1.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-0-N-0-unitless.htm b/test/render/flex/flexbox_flex-0-N-0-unitless.htm deleted file mode 100644 index e0037275e..000000000 --- a/test/render/flex/flexbox_flex-0-N-0-unitless.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 0 N 0 unitless - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-0-N-0-unitless.htm.png b/test/render/flex/flexbox_flex-0-N-0-unitless.htm.png deleted file mode 100644 index 2b11d4515..000000000 Binary files a/test/render/flex/flexbox_flex-0-N-0-unitless.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-0-N-0.htm b/test/render/flex/flexbox_flex-0-N-0.htm deleted file mode 100644 index dc95168ab..000000000 --- a/test/render/flex/flexbox_flex-0-N-0.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 0 N 0 - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-0-N-0.htm.png b/test/render/flex/flexbox_flex-0-N-0.htm.png deleted file mode 100644 index 2b11d4515..000000000 Binary files a/test/render/flex/flexbox_flex-0-N-0.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-0-N-N-shrink.htm b/test/render/flex/flexbox_flex-0-N-N-shrink.htm deleted file mode 100644 index 1a6a829ad..000000000 --- a/test/render/flex/flexbox_flex-0-N-N-shrink.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 0 N N | shrinking - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-0-N-N-shrink.htm.png b/test/render/flex/flexbox_flex-0-N-N-shrink.htm.png deleted file mode 100644 index 0f46f7c74..000000000 Binary files a/test/render/flex/flexbox_flex-0-N-N-shrink.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-0-N-N.htm b/test/render/flex/flexbox_flex-0-N-N.htm deleted file mode 100644 index 2e6ccc57d..000000000 --- a/test/render/flex/flexbox_flex-0-N-N.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 0 N N - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-0-N-N.htm.png b/test/render/flex/flexbox_flex-0-N-N.htm.png deleted file mode 100644 index b45b17234..000000000 Binary files a/test/render/flex/flexbox_flex-0-N-N.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-0-N-Npercent-shrink.htm b/test/render/flex/flexbox_flex-0-N-Npercent-shrink.htm deleted file mode 100644 index 8341ce323..000000000 --- a/test/render/flex/flexbox_flex-0-N-Npercent-shrink.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 0 N N% | shrinking - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-0-N-Npercent-shrink.htm.png b/test/render/flex/flexbox_flex-0-N-Npercent-shrink.htm.png deleted file mode 100644 index 0f46f7c74..000000000 Binary files a/test/render/flex/flexbox_flex-0-N-Npercent-shrink.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-0-N-Npercent.htm b/test/render/flex/flexbox_flex-0-N-Npercent.htm deleted file mode 100644 index 203ecec9a..000000000 --- a/test/render/flex/flexbox_flex-0-N-Npercent.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 0 N N% - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-0-N-Npercent.htm.png b/test/render/flex/flexbox_flex-0-N-Npercent.htm.png deleted file mode 100644 index d1eb5dbb1..000000000 Binary files a/test/render/flex/flexbox_flex-0-N-Npercent.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-0-N-auto-shrink.htm b/test/render/flex/flexbox_flex-0-N-auto-shrink.htm deleted file mode 100644 index 3aec84982..000000000 --- a/test/render/flex/flexbox_flex-0-N-auto-shrink.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 0 N auto | shrinking - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-0-N-auto-shrink.htm.png b/test/render/flex/flexbox_flex-0-N-auto-shrink.htm.png deleted file mode 100644 index 297113b2c..000000000 Binary files a/test/render/flex/flexbox_flex-0-N-auto-shrink.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-0-N-auto.htm b/test/render/flex/flexbox_flex-0-N-auto.htm deleted file mode 100644 index 2f1a9338c..000000000 --- a/test/render/flex/flexbox_flex-0-N-auto.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 0 N auto - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-0-N-auto.htm.png b/test/render/flex/flexbox_flex-0-N-auto.htm.png deleted file mode 100644 index ab79088f3..000000000 Binary files a/test/render/flex/flexbox_flex-0-N-auto.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-0-N.htm b/test/render/flex/flexbox_flex-0-N.htm deleted file mode 100644 index dcc063bce..000000000 --- a/test/render/flex/flexbox_flex-0-N.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 0 N - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-0-N.htm.png b/test/render/flex/flexbox_flex-0-N.htm.png deleted file mode 100644 index 2b11d4515..000000000 Binary files a/test/render/flex/flexbox_flex-0-N.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-0-auto.htm b/test/render/flex/flexbox_flex-0-auto.htm deleted file mode 100644 index d73108513..000000000 --- a/test/render/flex/flexbox_flex-0-auto.htm +++ /dev/null @@ -1,45 +0,0 @@ - - - -flexbox | flex: 0 auto - - - - - -
- one - two - three - four -
- -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-0-auto.htm.png b/test/render/flex/flexbox_flex-0-auto.htm.png deleted file mode 100644 index d4107b044..000000000 Binary files a/test/render/flex/flexbox_flex-0-auto.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-1-0-0-unitless.htm b/test/render/flex/flexbox_flex-1-0-0-unitless.htm deleted file mode 100644 index de81e61bb..000000000 --- a/test/render/flex/flexbox_flex-1-0-0-unitless.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 1 0 0 unitless - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-1-0-0-unitless.htm.png b/test/render/flex/flexbox_flex-1-0-0-unitless.htm.png deleted file mode 100644 index 8cf2cda98..000000000 Binary files a/test/render/flex/flexbox_flex-1-0-0-unitless.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-1-0-0.htm b/test/render/flex/flexbox_flex-1-0-0.htm deleted file mode 100644 index 92bab58ee..000000000 --- a/test/render/flex/flexbox_flex-1-0-0.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 1 0 0 - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-1-0-0.htm.png b/test/render/flex/flexbox_flex-1-0-0.htm.png deleted file mode 100644 index 8cf2cda98..000000000 Binary files a/test/render/flex/flexbox_flex-1-0-0.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-1-0-N-shrink.htm b/test/render/flex/flexbox_flex-1-0-N-shrink.htm deleted file mode 100644 index b4b402985..000000000 --- a/test/render/flex/flexbox_flex-1-0-N-shrink.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 1 0 N | shrinking - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-1-0-N-shrink.htm.png b/test/render/flex/flexbox_flex-1-0-N-shrink.htm.png deleted file mode 100644 index bda4a23ef..000000000 Binary files a/test/render/flex/flexbox_flex-1-0-N-shrink.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-1-0-N.htm b/test/render/flex/flexbox_flex-1-0-N.htm deleted file mode 100644 index 033638aed..000000000 --- a/test/render/flex/flexbox_flex-1-0-N.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 1 0 N - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-1-0-N.htm.png b/test/render/flex/flexbox_flex-1-0-N.htm.png deleted file mode 100644 index 8c69849c4..000000000 Binary files a/test/render/flex/flexbox_flex-1-0-N.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-1-0-Npercent-shrink.htm b/test/render/flex/flexbox_flex-1-0-Npercent-shrink.htm deleted file mode 100644 index 529c49d73..000000000 --- a/test/render/flex/flexbox_flex-1-0-Npercent-shrink.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 1 0 N% | shrinking - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-1-0-Npercent-shrink.htm.png b/test/render/flex/flexbox_flex-1-0-Npercent-shrink.htm.png deleted file mode 100644 index c37d2519b..000000000 Binary files a/test/render/flex/flexbox_flex-1-0-Npercent-shrink.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-1-0-Npercent.htm b/test/render/flex/flexbox_flex-1-0-Npercent.htm deleted file mode 100644 index 21068eb33..000000000 --- a/test/render/flex/flexbox_flex-1-0-Npercent.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 1 0 N% - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-1-0-Npercent.htm.png b/test/render/flex/flexbox_flex-1-0-Npercent.htm.png deleted file mode 100644 index 8c69849c4..000000000 Binary files a/test/render/flex/flexbox_flex-1-0-Npercent.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-1-0-auto-shrink.htm b/test/render/flex/flexbox_flex-1-0-auto-shrink.htm deleted file mode 100644 index 9ca1411b2..000000000 --- a/test/render/flex/flexbox_flex-1-0-auto-shrink.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 1 0 auto | shrinking - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-1-0-auto-shrink.htm.png b/test/render/flex/flexbox_flex-1-0-auto-shrink.htm.png deleted file mode 100644 index 304f96b45..000000000 Binary files a/test/render/flex/flexbox_flex-1-0-auto-shrink.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-1-0-auto.htm b/test/render/flex/flexbox_flex-1-0-auto.htm deleted file mode 100644 index ab966c68f..000000000 --- a/test/render/flex/flexbox_flex-1-0-auto.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 1 0 auto - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-1-0-auto.htm.png b/test/render/flex/flexbox_flex-1-0-auto.htm.png deleted file mode 100644 index 8c69849c4..000000000 Binary files a/test/render/flex/flexbox_flex-1-0-auto.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-1-0.htm b/test/render/flex/flexbox_flex-1-0.htm deleted file mode 100644 index 996a9b7f6..000000000 --- a/test/render/flex/flexbox_flex-1-0.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 1 0 - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-1-0.htm.png b/test/render/flex/flexbox_flex-1-0.htm.png deleted file mode 100644 index 8cf2cda98..000000000 Binary files a/test/render/flex/flexbox_flex-1-0.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-1-1-0-unitless.htm b/test/render/flex/flexbox_flex-1-1-0-unitless.htm deleted file mode 100644 index c949b0bfa..000000000 --- a/test/render/flex/flexbox_flex-1-1-0-unitless.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 1 1 0 unitless - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-1-1-0-unitless.htm.png b/test/render/flex/flexbox_flex-1-1-0-unitless.htm.png deleted file mode 100644 index 8cf2cda98..000000000 Binary files a/test/render/flex/flexbox_flex-1-1-0-unitless.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-1-1-0.htm b/test/render/flex/flexbox_flex-1-1-0.htm deleted file mode 100644 index c7c1aa59a..000000000 --- a/test/render/flex/flexbox_flex-1-1-0.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 1 1 0 - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-1-1-0.htm.png b/test/render/flex/flexbox_flex-1-1-0.htm.png deleted file mode 100644 index 8cf2cda98..000000000 Binary files a/test/render/flex/flexbox_flex-1-1-0.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-1-1-N-shrink.htm b/test/render/flex/flexbox_flex-1-1-N-shrink.htm deleted file mode 100644 index 190fa0115..000000000 --- a/test/render/flex/flexbox_flex-1-1-N-shrink.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 1 1 N | shrinking - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-1-1-N-shrink.htm.png b/test/render/flex/flexbox_flex-1-1-N-shrink.htm.png deleted file mode 100644 index 0f46f7c74..000000000 Binary files a/test/render/flex/flexbox_flex-1-1-N-shrink.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-1-1-N.htm b/test/render/flex/flexbox_flex-1-1-N.htm deleted file mode 100644 index 5f85c54af..000000000 --- a/test/render/flex/flexbox_flex-1-1-N.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 1 1 N - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-1-1-N.htm.png b/test/render/flex/flexbox_flex-1-1-N.htm.png deleted file mode 100644 index 8c69849c4..000000000 Binary files a/test/render/flex/flexbox_flex-1-1-N.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-1-1-Npercent-shrink.htm b/test/render/flex/flexbox_flex-1-1-Npercent-shrink.htm deleted file mode 100644 index ba1ebf0fe..000000000 --- a/test/render/flex/flexbox_flex-1-1-Npercent-shrink.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 1 1 N% | shrinking - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-1-1-Npercent-shrink.htm.png b/test/render/flex/flexbox_flex-1-1-Npercent-shrink.htm.png deleted file mode 100644 index 0f46f7c74..000000000 Binary files a/test/render/flex/flexbox_flex-1-1-Npercent-shrink.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-1-1-Npercent.htm b/test/render/flex/flexbox_flex-1-1-Npercent.htm deleted file mode 100644 index ab07ea327..000000000 --- a/test/render/flex/flexbox_flex-1-1-Npercent.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 1 1 N% - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-1-1-Npercent.htm.png b/test/render/flex/flexbox_flex-1-1-Npercent.htm.png deleted file mode 100644 index 8c69849c4..000000000 Binary files a/test/render/flex/flexbox_flex-1-1-Npercent.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-1-1-auto-shrink.htm b/test/render/flex/flexbox_flex-1-1-auto-shrink.htm deleted file mode 100644 index 22bddfe68..000000000 --- a/test/render/flex/flexbox_flex-1-1-auto-shrink.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 1 1 auto | shrinking - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-1-1-auto-shrink.htm.png b/test/render/flex/flexbox_flex-1-1-auto-shrink.htm.png deleted file mode 100644 index 297113b2c..000000000 Binary files a/test/render/flex/flexbox_flex-1-1-auto-shrink.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-1-1-auto.htm b/test/render/flex/flexbox_flex-1-1-auto.htm deleted file mode 100644 index 6e5500fb1..000000000 --- a/test/render/flex/flexbox_flex-1-1-auto.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 1 1 auto - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-1-1-auto.htm.png b/test/render/flex/flexbox_flex-1-1-auto.htm.png deleted file mode 100644 index 8c69849c4..000000000 Binary files a/test/render/flex/flexbox_flex-1-1-auto.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-1-1.htm b/test/render/flex/flexbox_flex-1-1.htm deleted file mode 100644 index 89fd32ced..000000000 --- a/test/render/flex/flexbox_flex-1-1.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 1 1 - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-1-1.htm.png b/test/render/flex/flexbox_flex-1-1.htm.png deleted file mode 100644 index 8cf2cda98..000000000 Binary files a/test/render/flex/flexbox_flex-1-1.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-1-N-0-unitless.htm b/test/render/flex/flexbox_flex-1-N-0-unitless.htm deleted file mode 100644 index 206b9c492..000000000 --- a/test/render/flex/flexbox_flex-1-N-0-unitless.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 1 N 0 unitless - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-1-N-0-unitless.htm.png b/test/render/flex/flexbox_flex-1-N-0-unitless.htm.png deleted file mode 100644 index 8cf2cda98..000000000 Binary files a/test/render/flex/flexbox_flex-1-N-0-unitless.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-1-N-0.htm b/test/render/flex/flexbox_flex-1-N-0.htm deleted file mode 100644 index cfa10fdae..000000000 --- a/test/render/flex/flexbox_flex-1-N-0.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 1 N 0 - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-1-N-0.htm.png b/test/render/flex/flexbox_flex-1-N-0.htm.png deleted file mode 100644 index 8cf2cda98..000000000 Binary files a/test/render/flex/flexbox_flex-1-N-0.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-1-N-N-shrink.htm b/test/render/flex/flexbox_flex-1-N-N-shrink.htm deleted file mode 100644 index 5c0b3e50c..000000000 --- a/test/render/flex/flexbox_flex-1-N-N-shrink.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 1 N N | shrinking - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-1-N-N-shrink.htm.png b/test/render/flex/flexbox_flex-1-N-N-shrink.htm.png deleted file mode 100644 index 0f46f7c74..000000000 Binary files a/test/render/flex/flexbox_flex-1-N-N-shrink.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-1-N-N.htm b/test/render/flex/flexbox_flex-1-N-N.htm deleted file mode 100644 index 1bdef9f5c..000000000 --- a/test/render/flex/flexbox_flex-1-N-N.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 1 N N - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-1-N-N.htm.png b/test/render/flex/flexbox_flex-1-N-N.htm.png deleted file mode 100644 index 8c69849c4..000000000 Binary files a/test/render/flex/flexbox_flex-1-N-N.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-1-N-Npercent-shrink.htm b/test/render/flex/flexbox_flex-1-N-Npercent-shrink.htm deleted file mode 100644 index a6c8ef46e..000000000 --- a/test/render/flex/flexbox_flex-1-N-Npercent-shrink.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 1 N N% | shrinking - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-1-N-Npercent-shrink.htm.png b/test/render/flex/flexbox_flex-1-N-Npercent-shrink.htm.png deleted file mode 100644 index 0f46f7c74..000000000 Binary files a/test/render/flex/flexbox_flex-1-N-Npercent-shrink.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-1-N-Npercent.htm b/test/render/flex/flexbox_flex-1-N-Npercent.htm deleted file mode 100644 index e7315b6ce..000000000 --- a/test/render/flex/flexbox_flex-1-N-Npercent.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 1 N N% - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-1-N-Npercent.htm.png b/test/render/flex/flexbox_flex-1-N-Npercent.htm.png deleted file mode 100644 index 8c69849c4..000000000 Binary files a/test/render/flex/flexbox_flex-1-N-Npercent.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-1-N-auto-shrink.htm b/test/render/flex/flexbox_flex-1-N-auto-shrink.htm deleted file mode 100644 index bd2c4b006..000000000 --- a/test/render/flex/flexbox_flex-1-N-auto-shrink.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 1 N auto | shrinking - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-1-N-auto-shrink.htm.png b/test/render/flex/flexbox_flex-1-N-auto-shrink.htm.png deleted file mode 100644 index 297113b2c..000000000 Binary files a/test/render/flex/flexbox_flex-1-N-auto-shrink.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-1-N-auto.htm b/test/render/flex/flexbox_flex-1-N-auto.htm deleted file mode 100644 index 2f0f62802..000000000 --- a/test/render/flex/flexbox_flex-1-N-auto.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 1 N auto - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-1-N-auto.htm.png b/test/render/flex/flexbox_flex-1-N-auto.htm.png deleted file mode 100644 index 8c69849c4..000000000 Binary files a/test/render/flex/flexbox_flex-1-N-auto.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-1-N.htm b/test/render/flex/flexbox_flex-1-N.htm deleted file mode 100644 index b71a7844d..000000000 --- a/test/render/flex/flexbox_flex-1-N.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: 1 N - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-1-N.htm.png b/test/render/flex/flexbox_flex-1-N.htm.png deleted file mode 100644 index 8cf2cda98..000000000 Binary files a/test/render/flex/flexbox_flex-1-N.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-N-0-0-unitless.htm b/test/render/flex/flexbox_flex-N-0-0-unitless.htm deleted file mode 100644 index 44e396dde..000000000 --- a/test/render/flex/flexbox_flex-N-0-0-unitless.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: N 0 0 unitless - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-N-0-0-unitless.htm.png b/test/render/flex/flexbox_flex-N-0-0-unitless.htm.png deleted file mode 100644 index 8cf2cda98..000000000 Binary files a/test/render/flex/flexbox_flex-N-0-0-unitless.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-N-0-0.htm b/test/render/flex/flexbox_flex-N-0-0.htm deleted file mode 100644 index 5bafb73bf..000000000 --- a/test/render/flex/flexbox_flex-N-0-0.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: N 0 0 - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-N-0-0.htm.png b/test/render/flex/flexbox_flex-N-0-0.htm.png deleted file mode 100644 index 8cf2cda98..000000000 Binary files a/test/render/flex/flexbox_flex-N-0-0.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-N-0-N-shrink.htm b/test/render/flex/flexbox_flex-N-0-N-shrink.htm deleted file mode 100644 index d5b521eaf..000000000 --- a/test/render/flex/flexbox_flex-N-0-N-shrink.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: N 0 N | shrinking - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-N-0-N-shrink.htm.png b/test/render/flex/flexbox_flex-N-0-N-shrink.htm.png deleted file mode 100644 index bda4a23ef..000000000 Binary files a/test/render/flex/flexbox_flex-N-0-N-shrink.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-N-0-N.htm b/test/render/flex/flexbox_flex-N-0-N.htm deleted file mode 100644 index 79b20dccb..000000000 --- a/test/render/flex/flexbox_flex-N-0-N.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: N 0 N - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-N-0-N.htm.png b/test/render/flex/flexbox_flex-N-0-N.htm.png deleted file mode 100644 index 8c69849c4..000000000 Binary files a/test/render/flex/flexbox_flex-N-0-N.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-N-0-Npercent-shrink.htm b/test/render/flex/flexbox_flex-N-0-Npercent-shrink.htm deleted file mode 100644 index 83d4efe01..000000000 --- a/test/render/flex/flexbox_flex-N-0-Npercent-shrink.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: N 0 N% | shrinking - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-N-0-Npercent-shrink.htm.png b/test/render/flex/flexbox_flex-N-0-Npercent-shrink.htm.png deleted file mode 100644 index c37d2519b..000000000 Binary files a/test/render/flex/flexbox_flex-N-0-Npercent-shrink.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-N-0-Npercent.htm b/test/render/flex/flexbox_flex-N-0-Npercent.htm deleted file mode 100644 index ba1ee80d9..000000000 --- a/test/render/flex/flexbox_flex-N-0-Npercent.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: N 0 N% - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-N-0-Npercent.htm.png b/test/render/flex/flexbox_flex-N-0-Npercent.htm.png deleted file mode 100644 index 8c69849c4..000000000 Binary files a/test/render/flex/flexbox_flex-N-0-Npercent.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-N-0-auto-shrink.htm b/test/render/flex/flexbox_flex-N-0-auto-shrink.htm deleted file mode 100644 index d6e2e3167..000000000 --- a/test/render/flex/flexbox_flex-N-0-auto-shrink.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: N 0 auto | shrinking - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-N-0-auto-shrink.htm.png b/test/render/flex/flexbox_flex-N-0-auto-shrink.htm.png deleted file mode 100644 index 2ba886ede..000000000 Binary files a/test/render/flex/flexbox_flex-N-0-auto-shrink.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-N-0-auto.htm b/test/render/flex/flexbox_flex-N-0-auto.htm deleted file mode 100644 index bf9fb04ab..000000000 --- a/test/render/flex/flexbox_flex-N-0-auto.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: N 0 auto - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-N-0-auto.htm.png b/test/render/flex/flexbox_flex-N-0-auto.htm.png deleted file mode 100644 index 8c69849c4..000000000 Binary files a/test/render/flex/flexbox_flex-N-0-auto.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-N-0.htm b/test/render/flex/flexbox_flex-N-0.htm deleted file mode 100644 index 71e7d40c6..000000000 --- a/test/render/flex/flexbox_flex-N-0.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: N 0 - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-N-0.htm.png b/test/render/flex/flexbox_flex-N-0.htm.png deleted file mode 100644 index 8cf2cda98..000000000 Binary files a/test/render/flex/flexbox_flex-N-0.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-N-1-0-unitless.htm b/test/render/flex/flexbox_flex-N-1-0-unitless.htm deleted file mode 100644 index 8db840065..000000000 --- a/test/render/flex/flexbox_flex-N-1-0-unitless.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: N 1 0 unitless - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-N-1-0-unitless.htm.png b/test/render/flex/flexbox_flex-N-1-0-unitless.htm.png deleted file mode 100644 index 8cf2cda98..000000000 Binary files a/test/render/flex/flexbox_flex-N-1-0-unitless.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-N-1-0.htm b/test/render/flex/flexbox_flex-N-1-0.htm deleted file mode 100644 index c21c35d9a..000000000 --- a/test/render/flex/flexbox_flex-N-1-0.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: N 1 0 - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-N-1-0.htm.png b/test/render/flex/flexbox_flex-N-1-0.htm.png deleted file mode 100644 index 8cf2cda98..000000000 Binary files a/test/render/flex/flexbox_flex-N-1-0.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-N-1-N-shrink.htm b/test/render/flex/flexbox_flex-N-1-N-shrink.htm deleted file mode 100644 index d6b4b0070..000000000 --- a/test/render/flex/flexbox_flex-N-1-N-shrink.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: N 1 N | shrinking - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-N-1-N-shrink.htm.png b/test/render/flex/flexbox_flex-N-1-N-shrink.htm.png deleted file mode 100644 index 0f46f7c74..000000000 Binary files a/test/render/flex/flexbox_flex-N-1-N-shrink.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-N-1-N.htm b/test/render/flex/flexbox_flex-N-1-N.htm deleted file mode 100644 index 9b6fc8931..000000000 --- a/test/render/flex/flexbox_flex-N-1-N.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: N 1 N - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-N-1-N.htm.png b/test/render/flex/flexbox_flex-N-1-N.htm.png deleted file mode 100644 index 8c69849c4..000000000 Binary files a/test/render/flex/flexbox_flex-N-1-N.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-N-1-Npercent-shrink.htm b/test/render/flex/flexbox_flex-N-1-Npercent-shrink.htm deleted file mode 100644 index 333bd4818..000000000 --- a/test/render/flex/flexbox_flex-N-1-Npercent-shrink.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: N 1 N% | shrinking - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-N-1-Npercent-shrink.htm.png b/test/render/flex/flexbox_flex-N-1-Npercent-shrink.htm.png deleted file mode 100644 index 0f46f7c74..000000000 Binary files a/test/render/flex/flexbox_flex-N-1-Npercent-shrink.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-N-1-Npercent.htm b/test/render/flex/flexbox_flex-N-1-Npercent.htm deleted file mode 100644 index b2f5e9544..000000000 --- a/test/render/flex/flexbox_flex-N-1-Npercent.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: N 1 N% - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-N-1-Npercent.htm.png b/test/render/flex/flexbox_flex-N-1-Npercent.htm.png deleted file mode 100644 index 8c69849c4..000000000 Binary files a/test/render/flex/flexbox_flex-N-1-Npercent.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-N-1-auto-shrink.htm b/test/render/flex/flexbox_flex-N-1-auto-shrink.htm deleted file mode 100644 index 7ced23183..000000000 --- a/test/render/flex/flexbox_flex-N-1-auto-shrink.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: N 1 auto | shrinking - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-N-1-auto-shrink.htm.png b/test/render/flex/flexbox_flex-N-1-auto-shrink.htm.png deleted file mode 100644 index 297113b2c..000000000 Binary files a/test/render/flex/flexbox_flex-N-1-auto-shrink.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-N-1-auto.htm b/test/render/flex/flexbox_flex-N-1-auto.htm deleted file mode 100644 index 7ed3906de..000000000 --- a/test/render/flex/flexbox_flex-N-1-auto.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: N 1 auto - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-N-1-auto.htm.png b/test/render/flex/flexbox_flex-N-1-auto.htm.png deleted file mode 100644 index 8c69849c4..000000000 Binary files a/test/render/flex/flexbox_flex-N-1-auto.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-N-1.htm b/test/render/flex/flexbox_flex-N-1.htm deleted file mode 100644 index d49d01cbc..000000000 --- a/test/render/flex/flexbox_flex-N-1.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: N 1 - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-N-1.htm.png b/test/render/flex/flexbox_flex-N-1.htm.png deleted file mode 100644 index 8cf2cda98..000000000 Binary files a/test/render/flex/flexbox_flex-N-1.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-N-N-0-unitless.htm b/test/render/flex/flexbox_flex-N-N-0-unitless.htm deleted file mode 100644 index de5b00aa8..000000000 --- a/test/render/flex/flexbox_flex-N-N-0-unitless.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: N N 0 unitless - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-N-N-0-unitless.htm.png b/test/render/flex/flexbox_flex-N-N-0-unitless.htm.png deleted file mode 100644 index 8cf2cda98..000000000 Binary files a/test/render/flex/flexbox_flex-N-N-0-unitless.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-N-N-0.htm b/test/render/flex/flexbox_flex-N-N-0.htm deleted file mode 100644 index b4f5c7e43..000000000 --- a/test/render/flex/flexbox_flex-N-N-0.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: N N 0 - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-N-N-0.htm.png b/test/render/flex/flexbox_flex-N-N-0.htm.png deleted file mode 100644 index 8cf2cda98..000000000 Binary files a/test/render/flex/flexbox_flex-N-N-0.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-N-N-N-shrink.htm b/test/render/flex/flexbox_flex-N-N-N-shrink.htm deleted file mode 100644 index 37fdc2e08..000000000 --- a/test/render/flex/flexbox_flex-N-N-N-shrink.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: N N N | shrinking - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-N-N-N-shrink.htm.png b/test/render/flex/flexbox_flex-N-N-N-shrink.htm.png deleted file mode 100644 index 0f46f7c74..000000000 Binary files a/test/render/flex/flexbox_flex-N-N-N-shrink.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-N-N-N.htm b/test/render/flex/flexbox_flex-N-N-N.htm deleted file mode 100644 index a78dc76fb..000000000 --- a/test/render/flex/flexbox_flex-N-N-N.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: N N N - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-N-N-N.htm.png b/test/render/flex/flexbox_flex-N-N-N.htm.png deleted file mode 100644 index 8c69849c4..000000000 Binary files a/test/render/flex/flexbox_flex-N-N-N.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-N-N-Npercent-shrink.htm b/test/render/flex/flexbox_flex-N-N-Npercent-shrink.htm deleted file mode 100644 index ce213915f..000000000 --- a/test/render/flex/flexbox_flex-N-N-Npercent-shrink.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: N N N% | shrinking - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-N-N-Npercent-shrink.htm.png b/test/render/flex/flexbox_flex-N-N-Npercent-shrink.htm.png deleted file mode 100644 index 0f46f7c74..000000000 Binary files a/test/render/flex/flexbox_flex-N-N-Npercent-shrink.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-N-N-Npercent.htm b/test/render/flex/flexbox_flex-N-N-Npercent.htm deleted file mode 100644 index 61eaa98c9..000000000 --- a/test/render/flex/flexbox_flex-N-N-Npercent.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: N N N% - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-N-N-Npercent.htm.png b/test/render/flex/flexbox_flex-N-N-Npercent.htm.png deleted file mode 100644 index 8c69849c4..000000000 Binary files a/test/render/flex/flexbox_flex-N-N-Npercent.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-N-N-auto-shrink.htm b/test/render/flex/flexbox_flex-N-N-auto-shrink.htm deleted file mode 100644 index eb8660deb..000000000 --- a/test/render/flex/flexbox_flex-N-N-auto-shrink.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: N N auto | shrinking - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-N-N-auto-shrink.htm.png b/test/render/flex/flexbox_flex-N-N-auto-shrink.htm.png deleted file mode 100644 index 297113b2c..000000000 Binary files a/test/render/flex/flexbox_flex-N-N-auto-shrink.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-N-N-auto.htm b/test/render/flex/flexbox_flex-N-N-auto.htm deleted file mode 100644 index 86664de23..000000000 --- a/test/render/flex/flexbox_flex-N-N-auto.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: N N auto - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-N-N-auto.htm.png b/test/render/flex/flexbox_flex-N-N-auto.htm.png deleted file mode 100644 index 8c69849c4..000000000 Binary files a/test/render/flex/flexbox_flex-N-N-auto.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-N-N.htm b/test/render/flex/flexbox_flex-N-N.htm deleted file mode 100644 index ff3133041..000000000 --- a/test/render/flex/flexbox_flex-N-N.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex: N N - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-N-N.htm.png b/test/render/flex/flexbox_flex-N-N.htm.png deleted file mode 100644 index 8cf2cda98..000000000 Binary files a/test/render/flex/flexbox_flex-N-N.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-auto.htm b/test/render/flex/flexbox_flex-auto.htm deleted file mode 100644 index 88216df16..000000000 --- a/test/render/flex/flexbox_flex-auto.htm +++ /dev/null @@ -1,46 +0,0 @@ - - - -flexbox | flex: auto - - - - - -
- one - two - three - four -
- -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-auto.htm.png b/test/render/flex/flexbox_flex-auto.htm.png deleted file mode 100644 index a15159b5b..000000000 Binary files a/test/render/flex/flexbox_flex-auto.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-basis-shrink.htm b/test/render/flex/flexbox_flex-basis-shrink.htm deleted file mode 100644 index bf101f0c3..000000000 --- a/test/render/flex/flexbox_flex-basis-shrink.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | flex-basis: percentage, flex-shrink: +integer - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-basis-shrink.htm.png b/test/render/flex/flexbox_flex-basis-shrink.htm.png deleted file mode 100644 index 957ada77c..000000000 Binary files a/test/render/flex/flexbox_flex-basis-shrink.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-basis.htm b/test/render/flex/flexbox_flex-basis.htm deleted file mode 100644 index fc6dc1204..000000000 --- a/test/render/flex/flexbox_flex-basis.htm +++ /dev/null @@ -1,42 +0,0 @@ - - - -flexbox | flex-basis: percentage - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-basis.htm.png b/test/render/flex/flexbox_flex-basis.htm.png deleted file mode 100644 index 40e9e1b5f..000000000 Binary files a/test/render/flex/flexbox_flex-basis.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-initial-2.htm b/test/render/flex/flexbox_flex-initial-2.htm deleted file mode 100644 index 02acae5a6..000000000 --- a/test/render/flex/flexbox_flex-initial-2.htm +++ /dev/null @@ -1,45 +0,0 @@ - - - -flexbox | flex: initial - - - - - -
- one - two - three - four -
- -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-initial-2.htm.png b/test/render/flex/flexbox_flex-initial-2.htm.png deleted file mode 100644 index c487b725d..000000000 Binary files a/test/render/flex/flexbox_flex-initial-2.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-initial.htm b/test/render/flex/flexbox_flex-initial.htm deleted file mode 100644 index e8ae362ab..000000000 --- a/test/render/flex/flexbox_flex-initial.htm +++ /dev/null @@ -1,45 +0,0 @@ - - - -flexbox | flex: initial - - - - - -
- one - two - three - four -
- -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-initial.htm.png b/test/render/flex/flexbox_flex-initial.htm.png deleted file mode 100644 index d4107b044..000000000 Binary files a/test/render/flex/flexbox_flex-initial.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-natural-mixed-basis-auto.htm b/test/render/flex/flexbox_flex-natural-mixed-basis-auto.htm deleted file mode 100644 index c81c8b50c..000000000 --- a/test/render/flex/flexbox_flex-natural-mixed-basis-auto.htm +++ /dev/null @@ -1,42 +0,0 @@ - - - -flexbox | flex: larger integer, mixed basis, auto - - - - - - - -
- a - aaa - aaaaa - aaaaaaaaaaaaaaa -
- - - diff --git a/test/render/flex/flexbox_flex-natural-mixed-basis-auto.htm.png b/test/render/flex/flexbox_flex-natural-mixed-basis-auto.htm.png deleted file mode 100644 index d28853718..000000000 Binary files a/test/render/flex/flexbox_flex-natural-mixed-basis-auto.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-natural-variable-auto-basis.htm b/test/render/flex/flexbox_flex-natural-variable-auto-basis.htm deleted file mode 100644 index 440ee8e4f..000000000 --- a/test/render/flex/flexbox_flex-natural-variable-auto-basis.htm +++ /dev/null @@ -1,39 +0,0 @@ - - - -flexbox | flex: larger integer, auto basis - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-natural-variable-auto-basis.htm.png b/test/render/flex/flexbox_flex-natural-variable-auto-basis.htm.png deleted file mode 100644 index b307e7537..000000000 Binary files a/test/render/flex/flexbox_flex-natural-variable-auto-basis.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-natural.htm b/test/render/flex/flexbox_flex-natural.htm deleted file mode 100644 index 4d566babd..000000000 --- a/test/render/flex/flexbox_flex-natural.htm +++ /dev/null @@ -1,50 +0,0 @@ - - - -flexbox | flex: larger integer - - - - - -
- one - two - three - four -
- -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-natural.htm.png b/test/render/flex/flexbox_flex-natural.htm.png deleted file mode 100644 index 5c99cad5f..000000000 Binary files a/test/render/flex/flexbox_flex-natural.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flex-none.htm b/test/render/flex/flexbox_flex-none.htm deleted file mode 100644 index edc748d95..000000000 --- a/test/render/flex/flexbox_flex-none.htm +++ /dev/null @@ -1,46 +0,0 @@ - - - -flexbox | flex: none - - - - - -
- one - two - three - four -
- -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flex-none.htm.png b/test/render/flex/flexbox_flex-none.htm.png deleted file mode 100644 index e09903126..000000000 Binary files a/test/render/flex/flexbox_flex-none.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flow-column-reverse-wrap-reverse.htm b/test/render/flex/flexbox_flow-column-reverse-wrap-reverse.htm deleted file mode 100644 index 0774e8023..000000000 --- a/test/render/flex/flexbox_flow-column-reverse-wrap-reverse.htm +++ /dev/null @@ -1,36 +0,0 @@ - - - -flexbox | flex-flow: column-reverse wrap-reverse - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flow-column-reverse-wrap-reverse.htm.png b/test/render/flex/flexbox_flow-column-reverse-wrap-reverse.htm.png deleted file mode 100644 index c9b70aeef..000000000 Binary files a/test/render/flex/flexbox_flow-column-reverse-wrap-reverse.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flow-column-reverse-wrap.htm b/test/render/flex/flexbox_flow-column-reverse-wrap.htm deleted file mode 100644 index e3b631fb9..000000000 --- a/test/render/flex/flexbox_flow-column-reverse-wrap.htm +++ /dev/null @@ -1,36 +0,0 @@ - - - -flexbox | flex-flow: column-reverse wrap - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flow-column-reverse-wrap.htm.png b/test/render/flex/flexbox_flow-column-reverse-wrap.htm.png deleted file mode 100644 index 572dd56c0..000000000 Binary files a/test/render/flex/flexbox_flow-column-reverse-wrap.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flow-column-wrap-reverse.htm b/test/render/flex/flexbox_flow-column-wrap-reverse.htm deleted file mode 100644 index 29dd65fc4..000000000 --- a/test/render/flex/flexbox_flow-column-wrap-reverse.htm +++ /dev/null @@ -1,35 +0,0 @@ - - - -flexbox | flex-flow: column wrap-reverse - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flow-column-wrap-reverse.htm.png b/test/render/flex/flexbox_flow-column-wrap-reverse.htm.png deleted file mode 100644 index e3b1b681f..000000000 Binary files a/test/render/flex/flexbox_flow-column-wrap-reverse.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flow-column-wrap.htm b/test/render/flex/flexbox_flow-column-wrap.htm deleted file mode 100644 index 33dfe03be..000000000 --- a/test/render/flex/flexbox_flow-column-wrap.htm +++ /dev/null @@ -1,35 +0,0 @@ - - - -flexbox | flex-flow: column wrap - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flow-column-wrap.htm.png b/test/render/flex/flexbox_flow-column-wrap.htm.png deleted file mode 100644 index 75b3343fc..000000000 Binary files a/test/render/flex/flexbox_flow-column-wrap.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flow-row-wrap-reverse.htm b/test/render/flex/flexbox_flow-row-wrap-reverse.htm deleted file mode 100644 index 6fc984c0d..000000000 --- a/test/render/flex/flexbox_flow-row-wrap-reverse.htm +++ /dev/null @@ -1,34 +0,0 @@ - - - -flexbox | flex-flow: row wrap-reverse - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flow-row-wrap-reverse.htm.png b/test/render/flex/flexbox_flow-row-wrap-reverse.htm.png deleted file mode 100644 index 1a181cd7d..000000000 Binary files a/test/render/flex/flexbox_flow-row-wrap-reverse.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_flow-row-wrap.htm b/test/render/flex/flexbox_flow-row-wrap.htm deleted file mode 100644 index 880ed3d76..000000000 --- a/test/render/flex/flexbox_flow-row-wrap.htm +++ /dev/null @@ -1,34 +0,0 @@ - - - -flexbox | flex-flow: row wrap - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_flow-row-wrap.htm.png b/test/render/flex/flexbox_flow-row-wrap.htm.png deleted file mode 100644 index 303d31699..000000000 Binary files a/test/render/flex/flexbox_flow-row-wrap.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_generated-flex.htm b/test/render/flex/flexbox_generated-flex.htm deleted file mode 100644 index 264be7f8d..000000000 --- a/test/render/flex/flexbox_generated-flex.htm +++ /dev/null @@ -1,27 +0,0 @@ - - - -flexbox | flexcontainer via generated content - - - - - -
-
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_generated-flex.htm.png b/test/render/flex/flexbox_generated-flex.htm.png deleted file mode 100644 index 3466de6b5..000000000 Binary files a/test/render/flex/flexbox_generated-flex.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_generated-nested-flex.htm b/test/render/flex/flexbox_generated-nested-flex.htm deleted file mode 100644 index 48fc8c575..000000000 --- a/test/render/flex/flexbox_generated-nested-flex.htm +++ /dev/null @@ -1,27 +0,0 @@ - - - -flexbox | flexcontainer via generated content - - - - - -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_generated-nested-flex.htm.png b/test/render/flex/flexbox_generated-nested-flex.htm.png deleted file mode 100644 index 3466de6b5..000000000 Binary files a/test/render/flex/flexbox_generated-nested-flex.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_generated.htm b/test/render/flex/flexbox_generated.htm deleted file mode 100644 index 53285a074..000000000 --- a/test/render/flex/flexbox_generated.htm +++ /dev/null @@ -1,32 +0,0 @@ - - - -flexbox | flexcontainer vs generated content - - - - - -
-

FAIL

-
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_generated.htm.png b/test/render/flex/flexbox_generated.htm.png deleted file mode 100644 index ce63e0a94..000000000 Binary files a/test/render/flex/flexbox_generated.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_item-bottom-float.htm b/test/render/flex/flexbox_item-bottom-float.htm deleted file mode 100644 index 821102472..000000000 --- a/test/render/flex/flexbox_item-bottom-float.htm +++ /dev/null @@ -1,34 +0,0 @@ - - - -flexbox | GCPM bottom float - - - - - -
-

-

-

-

-
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_item-bottom-float.htm.png b/test/render/flex/flexbox_item-bottom-float.htm.png deleted file mode 100644 index bc7d80a94..000000000 Binary files a/test/render/flex/flexbox_item-bottom-float.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_item-clear.htm b/test/render/flex/flexbox_item-clear.htm deleted file mode 100644 index a5e655241..000000000 --- a/test/render/flex/flexbox_item-clear.htm +++ /dev/null @@ -1,34 +0,0 @@ - - - -flexbox | cleared item - - - - - -
filler
- -
-
Yellow box should be to the right of the blue box, and - never below
-
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_item-clear.htm.png b/test/render/flex/flexbox_item-clear.htm.png deleted file mode 100644 index 7f8e75d72..000000000 Binary files a/test/render/flex/flexbox_item-clear.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_item-float.htm b/test/render/flex/flexbox_item-float.htm deleted file mode 100644 index ce7622dc6..000000000 --- a/test/render/flex/flexbox_item-float.htm +++ /dev/null @@ -1,32 +0,0 @@ - - - -flexbox | floated item - - - - - -
-
-
-
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_item-float.htm.png b/test/render/flex/flexbox_item-float.htm.png deleted file mode 100644 index 71a3008ff..000000000 Binary files a/test/render/flex/flexbox_item-float.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_item-top-float.htm b/test/render/flex/flexbox_item-top-float.htm deleted file mode 100644 index 16bea62c2..000000000 --- a/test/render/flex/flexbox_item-top-float.htm +++ /dev/null @@ -1,33 +0,0 @@ - - - -flexbox | floated item - - - - - -
-

-

-

-

-
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_item-top-float.htm.png b/test/render/flex/flexbox_item-top-float.htm.png deleted file mode 100644 index bc7d80a94..000000000 Binary files a/test/render/flex/flexbox_item-top-float.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_item-vertical-align.htm b/test/render/flex/flexbox_item-vertical-align.htm deleted file mode 100644 index ae67ee9d3..000000000 --- a/test/render/flex/flexbox_item-vertical-align.htm +++ /dev/null @@ -1,38 +0,0 @@ - - - -flexbox | vertical-align - - - - - -
-

-

-

-

-

-
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_item-vertical-align.htm.png b/test/render/flex/flexbox_item-vertical-align.htm.png deleted file mode 100644 index 62a6f3017..000000000 Binary files a/test/render/flex/flexbox_item-vertical-align.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_justifycontent-center.htm b/test/render/flex/flexbox_justifycontent-center.htm deleted file mode 100644 index d7ed4cae2..000000000 --- a/test/render/flex/flexbox_justifycontent-center.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | justify-content: center - - - - - -
- one - two - three -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_justifycontent-center.htm.png b/test/render/flex/flexbox_justifycontent-center.htm.png deleted file mode 100644 index c2731d3c9..000000000 Binary files a/test/render/flex/flexbox_justifycontent-center.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_justifycontent-flex-end.htm b/test/render/flex/flexbox_justifycontent-flex-end.htm deleted file mode 100644 index 0291d8c1e..000000000 --- a/test/render/flex/flexbox_justifycontent-flex-end.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | justify-content: flex-end - - - - - -
- one - two - three -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_justifycontent-flex-end.htm.png b/test/render/flex/flexbox_justifycontent-flex-end.htm.png deleted file mode 100644 index cd44e7c31..000000000 Binary files a/test/render/flex/flexbox_justifycontent-flex-end.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_justifycontent-flex-start.htm b/test/render/flex/flexbox_justifycontent-flex-start.htm deleted file mode 100644 index 0137f5430..000000000 --- a/test/render/flex/flexbox_justifycontent-flex-start.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | justify-content: flex-start - - - - - -
- one - two - three -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_justifycontent-flex-start.htm.png b/test/render/flex/flexbox_justifycontent-flex-start.htm.png deleted file mode 100644 index f45f26eb6..000000000 Binary files a/test/render/flex/flexbox_justifycontent-flex-start.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_justifycontent-spacearound-negative.htm b/test/render/flex/flexbox_justifycontent-spacearound-negative.htm deleted file mode 100644 index e7f8444b7..000000000 --- a/test/render/flex/flexbox_justifycontent-spacearound-negative.htm +++ /dev/null @@ -1,39 +0,0 @@ - - - -flexbox | justify-content: space-around / negative - - - - - -
- one - two - three -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_justifycontent-spacearound-negative.htm.png b/test/render/flex/flexbox_justifycontent-spacearound-negative.htm.png deleted file mode 100644 index 44e7ae5ce..000000000 Binary files a/test/render/flex/flexbox_justifycontent-spacearound-negative.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_justifycontent-spacearound-only.htm b/test/render/flex/flexbox_justifycontent-spacearound-only.htm deleted file mode 100644 index 89eb31c70..000000000 --- a/test/render/flex/flexbox_justifycontent-spacearound-only.htm +++ /dev/null @@ -1,35 +0,0 @@ - - - -flexbox | justify-content: space-around | single item - - - - - -
- one -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_justifycontent-spacearound-only.htm.png b/test/render/flex/flexbox_justifycontent-spacearound-only.htm.png deleted file mode 100644 index f48978676..000000000 Binary files a/test/render/flex/flexbox_justifycontent-spacearound-only.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_justifycontent-spacearound.htm b/test/render/flex/flexbox_justifycontent-spacearound.htm deleted file mode 100644 index 0fb853862..000000000 --- a/test/render/flex/flexbox_justifycontent-spacearound.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | justify-content: space-around - - - - - -
- one - two - three -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_justifycontent-spacearound.htm.png b/test/render/flex/flexbox_justifycontent-spacearound.htm.png deleted file mode 100644 index f45f26eb6..000000000 Binary files a/test/render/flex/flexbox_justifycontent-spacearound.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_justifycontent-spacebetween-negative.htm b/test/render/flex/flexbox_justifycontent-spacebetween-negative.htm deleted file mode 100644 index 6e431f659..000000000 --- a/test/render/flex/flexbox_justifycontent-spacebetween-negative.htm +++ /dev/null @@ -1,39 +0,0 @@ - - - -flexbox | justify-content: space-between / negative - - - - - -
- one - two - three -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_justifycontent-spacebetween-negative.htm.png b/test/render/flex/flexbox_justifycontent-spacebetween-negative.htm.png deleted file mode 100644 index d98579cf4..000000000 Binary files a/test/render/flex/flexbox_justifycontent-spacebetween-negative.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_justifycontent-spacebetween-only.htm b/test/render/flex/flexbox_justifycontent-spacebetween-only.htm deleted file mode 100644 index 812a6a005..000000000 --- a/test/render/flex/flexbox_justifycontent-spacebetween-only.htm +++ /dev/null @@ -1,35 +0,0 @@ - - - -flexbox | justify-content: space-between | single item - - - - - -
- one -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_justifycontent-spacebetween-only.htm.png b/test/render/flex/flexbox_justifycontent-spacebetween-only.htm.png deleted file mode 100644 index 9b8fa533a..000000000 Binary files a/test/render/flex/flexbox_justifycontent-spacebetween-only.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_justifycontent-spacebetween.htm b/test/render/flex/flexbox_justifycontent-spacebetween.htm deleted file mode 100644 index c8e00a382..000000000 --- a/test/render/flex/flexbox_justifycontent-spacebetween.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | justify-content: space-between - - - - - -
- one - two - three -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_justifycontent-spacebetween.htm.png b/test/render/flex/flexbox_justifycontent-spacebetween.htm.png deleted file mode 100644 index f03ac8629..000000000 Binary files a/test/render/flex/flexbox_justifycontent-spacebetween.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_margin-auto-overflow.htm b/test/render/flex/flexbox_margin-auto-overflow.htm deleted file mode 100644 index f8bbe7b49..000000000 --- a/test/render/flex/flexbox_margin-auto-overflow.htm +++ /dev/null @@ -1,35 +0,0 @@ - - - -flexbox | margin: auto in overflow - - - - - -
- one - two -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_margin-auto-overflow.htm.png b/test/render/flex/flexbox_margin-auto-overflow.htm.png deleted file mode 100644 index 33425b1c4..000000000 Binary files a/test/render/flex/flexbox_margin-auto-overflow.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_margin-auto.htm b/test/render/flex/flexbox_margin-auto.htm deleted file mode 100644 index 3f49cacf5..000000000 --- a/test/render/flex/flexbox_margin-auto.htm +++ /dev/null @@ -1,33 +0,0 @@ - - - -flexbox | margin: auto - - - - - -
- one - two -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_margin-auto.htm.png b/test/render/flex/flexbox_margin-auto.htm.png deleted file mode 100644 index 86c0cafd0..000000000 Binary files a/test/render/flex/flexbox_margin-auto.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_margin-left-ex.htm b/test/render/flex/flexbox_margin-left-ex.htm deleted file mode 100644 index ebe7e1c5f..000000000 --- a/test/render/flex/flexbox_margin-left-ex.htm +++ /dev/null @@ -1,33 +0,0 @@ - - - -flexbox | margin-left: auto - - - - - -
- onetwothreefour
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_margin-left-ex.htm.png b/test/render/flex/flexbox_margin-left-ex.htm.png deleted file mode 100644 index a192788dd..000000000 Binary files a/test/render/flex/flexbox_margin-left-ex.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_margin.htm b/test/render/flex/flexbox_margin.htm deleted file mode 100644 index f5dbba6da..000000000 --- a/test/render/flex/flexbox_margin.htm +++ /dev/null @@ -1,22 +0,0 @@ - - - -flexbox | margins - - - - - -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_margin.htm.png b/test/render/flex/flexbox_margin.htm.png deleted file mode 100644 index b9d24bbb6..000000000 Binary files a/test/render/flex/flexbox_margin.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_nested-flex.htm b/test/render/flex/flexbox_nested-flex.htm deleted file mode 100644 index 23109575d..000000000 --- a/test/render/flex/flexbox_nested-flex.htm +++ /dev/null @@ -1,28 +0,0 @@ - - - -flexbox | nested flexcontainer - - - - - -
-

xxx

-
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_nested-flex.htm.png b/test/render/flex/flexbox_nested-flex.htm.png deleted file mode 100644 index 3466de6b5..000000000 Binary files a/test/render/flex/flexbox_nested-flex.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_object.htm b/test/render/flex/flexbox_object.htm deleted file mode 100644 index 3f745c30e..000000000 --- a/test/render/flex/flexbox_object.htm +++ /dev/null @@ -1,26 +0,0 @@ - - - -flexbox | object fallback as a flex item - - - - - -
- this is supposed to be a flex item -

this is supposed to be a flex item

-
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_object.htm.png b/test/render/flex/flexbox_object.htm.png deleted file mode 100644 index cee494f6e..000000000 Binary files a/test/render/flex/flexbox_object.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_order-box.htm b/test/render/flex/flexbox_order-box.htm deleted file mode 100644 index a92c18e4e..000000000 --- a/test/render/flex/flexbox_order-box.htm +++ /dev/null @@ -1,43 +0,0 @@ - - - -flexbox | flex-flow: column-reverse wrap-reverse; order - - - - - -
-
- one - two -
- -
- three - four -
-
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_order-box.htm.png b/test/render/flex/flexbox_order-box.htm.png deleted file mode 100644 index eb2a5e2ff..000000000 Binary files a/test/render/flex/flexbox_order-box.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_order-noninteger-invalid.htm b/test/render/flex/flexbox_order-noninteger-invalid.htm deleted file mode 100644 index 2c4eb9b40..000000000 --- a/test/render/flex/flexbox_order-noninteger-invalid.htm +++ /dev/null @@ -1,44 +0,0 @@ - - - -flexbox | flex-flow: column-reverse wrap-reverse; order - - - - - - -
- - -
- -

- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_order-noninteger-invalid.htm.png b/test/render/flex/flexbox_order-noninteger-invalid.htm.png deleted file mode 100644 index a76685221..000000000 Binary files a/test/render/flex/flexbox_order-noninteger-invalid.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_order.htm b/test/render/flex/flexbox_order.htm deleted file mode 100644 index f0948e8ee..000000000 --- a/test/render/flex/flexbox_order.htm +++ /dev/null @@ -1,54 +0,0 @@ - - - -flexbox | flex-flow: column-reverse wrap-reverse; order - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_order.htm.png b/test/render/flex/flexbox_order.htm.png deleted file mode 100644 index b043d02fb..000000000 Binary files a/test/render/flex/flexbox_order.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_stf-abspos.htm b/test/render/flex/flexbox_stf-abspos.htm deleted file mode 100644 index 40230b8f1..000000000 --- a/test/render/flex/flexbox_stf-abspos.htm +++ /dev/null @@ -1,38 +0,0 @@ - - - -flexbox | flexcontainer versus stf :: abspos - - - - - -
-
-

filler

-

filler

-

filler

-

filler

-

filler

-
-
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_stf-abspos.htm.png b/test/render/flex/flexbox_stf-abspos.htm.png deleted file mode 100644 index bb85baad6..000000000 Binary files a/test/render/flex/flexbox_stf-abspos.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_stf-fixpos.htm b/test/render/flex/flexbox_stf-fixpos.htm deleted file mode 100644 index 0666fca88..000000000 --- a/test/render/flex/flexbox_stf-fixpos.htm +++ /dev/null @@ -1,38 +0,0 @@ - - - -flexbox | flexcontainer versus stf :: fixed - - - - - -
-
-

filler

-

filler

-

filler

-

filler

-

filler

-
-
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_stf-fixpos.htm.png b/test/render/flex/flexbox_stf-fixpos.htm.png deleted file mode 100644 index 0a2c61837..000000000 Binary files a/test/render/flex/flexbox_stf-fixpos.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_stf-float.htm b/test/render/flex/flexbox_stf-float.htm deleted file mode 100644 index a5e3429dd..000000000 --- a/test/render/flex/flexbox_stf-float.htm +++ /dev/null @@ -1,38 +0,0 @@ - - - -flexbox | flexcontainer versus stf :: float - - - - - -
-
-

filler

-

filler

-

filler

-

filler

-

filler

-
-
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_stf-float.htm.png b/test/render/flex/flexbox_stf-float.htm.png deleted file mode 100644 index bb85baad6..000000000 Binary files a/test/render/flex/flexbox_stf-float.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_stf-inline-block.htm b/test/render/flex/flexbox_stf-inline-block.htm deleted file mode 100644 index 147bb4ce7..000000000 --- a/test/render/flex/flexbox_stf-inline-block.htm +++ /dev/null @@ -1,38 +0,0 @@ - - - -flexbox | flexcontainer versus stf :: inline-block - - - - - -
-
-

filler

-

filler

-

filler

-

filler

-

filler

-
-
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_stf-inline-block.htm.png b/test/render/flex/flexbox_stf-inline-block.htm.png deleted file mode 100644 index bb85baad6..000000000 Binary files a/test/render/flex/flexbox_stf-inline-block.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_stf-table-caption.htm b/test/render/flex/flexbox_stf-table-caption.htm deleted file mode 100644 index b150c00c3..000000000 --- a/test/render/flex/flexbox_stf-table-caption.htm +++ /dev/null @@ -1,38 +0,0 @@ - - - -flexbox | flexcontainer versus stf :: table-caption - - - - - -
-
-

filler

-

filler

-

filler

-

filler

-

filler

-
-
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_stf-table-caption.htm.png b/test/render/flex/flexbox_stf-table-caption.htm.png deleted file mode 100644 index 193329237..000000000 Binary files a/test/render/flex/flexbox_stf-table-caption.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_stf-table-cell.htm b/test/render/flex/flexbox_stf-table-cell.htm deleted file mode 100644 index d456db2d0..000000000 --- a/test/render/flex/flexbox_stf-table-cell.htm +++ /dev/null @@ -1,38 +0,0 @@ - - - -flexbox | flexcontainer versus stf :: table cell - - - - - -
-
-

filler

-

filler

-

filler

-

filler

-

filler

-
-
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_stf-table-cell.htm.png b/test/render/flex/flexbox_stf-table-cell.htm.png deleted file mode 100644 index bb85baad6..000000000 Binary files a/test/render/flex/flexbox_stf-table-cell.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_stf-table-row-group.htm b/test/render/flex/flexbox_stf-table-row-group.htm deleted file mode 100644 index cb6369572..000000000 --- a/test/render/flex/flexbox_stf-table-row-group.htm +++ /dev/null @@ -1,38 +0,0 @@ - - - -flexbox | flexcontainer versus stf :: table row group - - - - - -
-
-

filler

-

filler

-

filler

-

filler

-

filler

-
-
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_stf-table-row-group.htm.png b/test/render/flex/flexbox_stf-table-row-group.htm.png deleted file mode 100644 index bb85baad6..000000000 Binary files a/test/render/flex/flexbox_stf-table-row-group.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_stf-table-row.htm b/test/render/flex/flexbox_stf-table-row.htm deleted file mode 100644 index 246036954..000000000 --- a/test/render/flex/flexbox_stf-table-row.htm +++ /dev/null @@ -1,38 +0,0 @@ - - - -flexbox | flexcontainer versus stf :: table row - - - - - -
-
-

filler

-

filler

-

filler

-

filler

-

filler

-
-
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_stf-table-row.htm.png b/test/render/flex/flexbox_stf-table-row.htm.png deleted file mode 100644 index bb85baad6..000000000 Binary files a/test/render/flex/flexbox_stf-table-row.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_stf-table.htm b/test/render/flex/flexbox_stf-table.htm deleted file mode 100644 index 8656055b2..000000000 --- a/test/render/flex/flexbox_stf-table.htm +++ /dev/null @@ -1,38 +0,0 @@ - - - -flexbox | flexcontainer versus stf :: table - - - - - -
-
-

filler

-

filler

-

filler

-

filler

-

filler

-
-
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_stf-table.htm.png b/test/render/flex/flexbox_stf-table.htm.png deleted file mode 100644 index bb85baad6..000000000 Binary files a/test/render/flex/flexbox_stf-table.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_visibility-collapse-line-wrapping.htm b/test/render/flex/flexbox_visibility-collapse-line-wrapping.htm deleted file mode 100644 index 1fd485010..000000000 --- a/test/render/flex/flexbox_visibility-collapse-line-wrapping.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - -flexbox | visibility: collapse and line wrapping - - - - - -
-

filler

-

filler

-

FAIL

-

FAIL

-

filler

-

filler

-
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_visibility-collapse-line-wrapping.htm.png b/test/render/flex/flexbox_visibility-collapse-line-wrapping.htm.png deleted file mode 100644 index b90ec28cd..000000000 Binary files a/test/render/flex/flexbox_visibility-collapse-line-wrapping.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_visibility-collapse.htm b/test/render/flex/flexbox_visibility-collapse.htm deleted file mode 100644 index 533f6a69a..000000000 --- a/test/render/flex/flexbox_visibility-collapse.htm +++ /dev/null @@ -1,34 +0,0 @@ - - - -flexbox | visibility: collapse - - - - - -
-

filler

-

FAIL

-

filler

-
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_visibility-collapse.htm.png b/test/render/flex/flexbox_visibility-collapse.htm.png deleted file mode 100644 index 294a0821d..000000000 Binary files a/test/render/flex/flexbox_visibility-collapse.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_width-overflow.htm.png b/test/render/flex/flexbox_width-overflow.htm.png deleted file mode 100644 index 8832cc7ae..000000000 Binary files a/test/render/flex/flexbox_width-overflow.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_wrap-long.htm b/test/render/flex/flexbox_wrap-long.htm deleted file mode 100644 index 3f03c50f3..000000000 --- a/test/render/flex/flexbox_wrap-long.htm +++ /dev/null @@ -1,37 +0,0 @@ - - - -flexbox | flex-wrap: wrap / long items - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_wrap-long.htm.png b/test/render/flex/flexbox_wrap-long.htm.png deleted file mode 100644 index 1f9bbde03..000000000 Binary files a/test/render/flex/flexbox_wrap-long.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_wrap-reverse.htm b/test/render/flex/flexbox_wrap-reverse.htm deleted file mode 100644 index b1aaee73b..000000000 --- a/test/render/flex/flexbox_wrap-reverse.htm +++ /dev/null @@ -1,34 +0,0 @@ - - - -flexbox | flex-wrap: wrap - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_wrap-reverse.htm.png b/test/render/flex/flexbox_wrap-reverse.htm.png deleted file mode 100644 index 1a181cd7d..000000000 Binary files a/test/render/flex/flexbox_wrap-reverse.htm.png and /dev/null differ diff --git a/test/render/flex/flexbox_wrap.htm b/test/render/flex/flexbox_wrap.htm deleted file mode 100644 index a5f7cb6bc..000000000 --- a/test/render/flex/flexbox_wrap.htm +++ /dev/null @@ -1,34 +0,0 @@ - - - -flexbox | flex-wrap: wrap - - - - - -
- one - two - three - four -
- - - \ No newline at end of file diff --git a/test/render/flex/flexbox_wrap.htm.png b/test/render/flex/flexbox_wrap.htm.png deleted file mode 100644 index 303d31699..000000000 Binary files a/test/render/flex/flexbox_wrap.htm.png and /dev/null differ diff --git a/test/render/flex/flexible-box-float.htm b/test/render/flex/flexible-box-float.htm deleted file mode 100644 index f53c3b211..000000000 --- a/test/render/flex/flexible-box-float.htm +++ /dev/null @@ -1,46 +0,0 @@ - - - flexible box flex item float effect - - - - - - - -

'float' have no effect on a flex item.

-

The test passes if there is a green square, a blue square and no red square.

-
-

 

 

 

-
- - - \ No newline at end of file diff --git a/test/render/flex/flexible-box-float.htm.chrome.png b/test/render/flex/flexible-box-float.htm.chrome.png deleted file mode 100644 index 0ecd77b09..000000000 Binary files a/test/render/flex/flexible-box-float.htm.chrome.png and /dev/null differ diff --git a/test/render/flex/flexible-box-float.htm.png b/test/render/flex/flexible-box-float.htm.png deleted file mode 100644 index 40d5fba65..000000000 Binary files a/test/render/flex/flexible-box-float.htm.png and /dev/null differ diff --git a/test/render/flex/flexible-order.htm b/test/render/flex/flexible-order.htm deleted file mode 100644 index 0bb4bd2c0..000000000 --- a/test/render/flex/flexible-order.htm +++ /dev/null @@ -1,70 +0,0 @@ - - - -CSS Test: Change the value of 'order' property - - - - - - - - - - - - - - - -
-
A
-
B
-
C
-
- - - - - - - - \ No newline at end of file diff --git a/test/render/flex/flexible-order.htm.png b/test/render/flex/flexible-order.htm.png deleted file mode 100644 index b0f4badae..000000000 Binary files a/test/render/flex/flexible-order.htm.png and /dev/null differ diff --git a/test/render/flex/item-with-table-with-infinite-max-intrinsic-width.htm b/test/render/flex/item-with-table-with-infinite-max-intrinsic-width.htm deleted file mode 100644 index ddc9dd7fb..000000000 --- a/test/render/flex/item-with-table-with-infinite-max-intrinsic-width.htm +++ /dev/null @@ -1,21 +0,0 @@ - - - -Flex item with table with infinite max intrinsic inline size - - - -

Test passes if there is a filled green square.

-
-
- - - - - -
  
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/item-with-table-with-infinite-max-intrinsic-width.htm.png b/test/render/flex/item-with-table-with-infinite-max-intrinsic-width.htm.png deleted file mode 100644 index 38b0d0675..000000000 Binary files a/test/render/flex/item-with-table-with-infinite-max-intrinsic-width.htm.png and /dev/null differ diff --git a/test/render/flex/justify-content_center.htm b/test/render/flex/justify-content_center.htm deleted file mode 100644 index 27aa76b76..000000000 --- a/test/render/flex/justify-content_center.htm +++ /dev/null @@ -1,45 +0,0 @@ - - - - - CSS Flexible Box Test: justify-content_center - - - - - - - -

Test passes if:
- 1. the rectangle 1, 2, 3 show up in a row in a red rectangle and no gap between them.
- 2. the rectangle 1, 2, 3 appear in middle of red rectangle.
- 3. equal amounts of empty space between the left edge of the red rectangle and ractangle 1 and between the right edge of the red rectangle and rectangle 3.
- 4. the height of the 1, 2, 3 is the same as the height of the red rectangle.

-
1
2
3
- - - - - \ No newline at end of file diff --git a/test/render/flex/justify-content_center.htm.png b/test/render/flex/justify-content_center.htm.png deleted file mode 100644 index 4a3199da4..000000000 Binary files a/test/render/flex/justify-content_center.htm.png and /dev/null differ diff --git a/test/render/flex/justify-content_flex-end.htm b/test/render/flex/justify-content_flex-end.htm deleted file mode 100644 index 948516a68..000000000 --- a/test/render/flex/justify-content_flex-end.htm +++ /dev/null @@ -1,44 +0,0 @@ - - - - - CSS Flexible Box Test: justify-content_flex-end - - - - - - - -

Test passes if:
- 1. the rectangle 1, 2, 3 show up in a row in a red rectangle and no gap between them.
- 2. the rectangle 1, 2, 3 appear in right of red rectangle.
- 3. 3. the height of the 1, 2, 3 is the same as the height of the red rectangle.

-
1
2
3
- - - - - \ No newline at end of file diff --git a/test/render/flex/justify-content_flex-end.htm.png b/test/render/flex/justify-content_flex-end.htm.png deleted file mode 100644 index 061a84fb9..000000000 Binary files a/test/render/flex/justify-content_flex-end.htm.png and /dev/null differ diff --git a/test/render/flex/justify-content_flex-start.htm b/test/render/flex/justify-content_flex-start.htm deleted file mode 100644 index fad6926bc..000000000 --- a/test/render/flex/justify-content_flex-start.htm +++ /dev/null @@ -1,44 +0,0 @@ - - - - - CSS Flexible Box Test: justify-content_flex-start - - - - - - - -

Test passes if:
- 1. the rectangle 1, 2, 3 show up in a row in a red rectangle and no gap between them.
- 2. the rectangle 1, 2, 3 appear in left of red rectangle.
- 3. the height of the 1, 2, 3 is the same as the height of the red rectangle.

-
1
2
3
- - - - - \ No newline at end of file diff --git a/test/render/flex/justify-content_flex-start.htm.png b/test/render/flex/justify-content_flex-start.htm.png deleted file mode 100644 index 5281fb164..000000000 Binary files a/test/render/flex/justify-content_flex-start.htm.png and /dev/null differ diff --git a/test/render/flex/justify-content_space-around.htm b/test/render/flex/justify-content_space-around.htm deleted file mode 100644 index 9d1f30c1f..000000000 --- a/test/render/flex/justify-content_space-around.htm +++ /dev/null @@ -1,44 +0,0 @@ - - - - - CSS Flexible Box Test: justify-content_space-around - - - - - - - -

Test passes if:
- 1. the rectangle 1, 2, 3 show up in a row in a red rectangle.
- 2. the rectangle 1, 2, 3 are distributed such that the empty space between any two adjacent rectangle is the same, and the empty space of the row before the first and after the last rectangle are half the size of the other empty spaces.
- 3. the height of the 1, 2, 3 is the same as the height of the red rectangle.

-
1
2
3
- - - - - \ No newline at end of file diff --git a/test/render/flex/justify-content_space-around.htm.png b/test/render/flex/justify-content_space-around.htm.png deleted file mode 100644 index 295c5d0a9..000000000 Binary files a/test/render/flex/justify-content_space-around.htm.png and /dev/null differ diff --git a/test/render/flex/justify-content_space-between-001.htm b/test/render/flex/justify-content_space-between-001.htm deleted file mode 100644 index 4b5eaa972..000000000 --- a/test/render/flex/justify-content_space-between-001.htm +++ /dev/null @@ -1,44 +0,0 @@ - - - - - CSS Flexible Box Test: justify-content_space-between - - - - - - - -

Test passes if:
- 1. the rectangle 1, 2, 3 show up in a row in a red rectangle.
- 2. No gap between the left edge of red rectangle and the left of rectangle 1, no gap between the right edge of red rectangle and the right of rectangle 3 too, and rectangle 2 is distributed so that the empty space between rectangle 1 and rectangle 3 is the same.
- 3. the height of the 1, 2, 3 is the same as the height of the red rectangle.

-
1
2
3
- - - - - \ No newline at end of file diff --git a/test/render/flex/justify-content_space-between-001.htm.png b/test/render/flex/justify-content_space-between-001.htm.png deleted file mode 100644 index 1fa272066..000000000 Binary files a/test/render/flex/justify-content_space-between-001.htm.png and /dev/null differ diff --git a/test/render/flex/layout-algorithm_algo-cross-line-001.htm b/test/render/flex/layout-algorithm_algo-cross-line-001.htm deleted file mode 100644 index ae98e7d00..000000000 --- a/test/render/flex/layout-algorithm_algo-cross-line-001.htm +++ /dev/null @@ -1,36 +0,0 @@ - - - - - CSS Grid Layout Test: cross size determination with overflow:scroll - - - - - - - -

Test passes if there is a filled green square with scrollbars and no red.

- -
-
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/layout-algorithm_algo-cross-line-001.htm.png b/test/render/flex/layout-algorithm_algo-cross-line-001.htm.png deleted file mode 100644 index e6e396079..000000000 Binary files a/test/render/flex/layout-algorithm_algo-cross-line-001.htm.png and /dev/null differ diff --git a/test/render/flex/layout-algorithm_algo-cross-line-002.htm b/test/render/flex/layout-algorithm_algo-cross-line-002.htm deleted file mode 100644 index 5716cdfba..000000000 --- a/test/render/flex/layout-algorithm_algo-cross-line-002.htm +++ /dev/null @@ -1,37 +0,0 @@ - - - - - CSS Grid Layout Test: cross size determination with overflow:scroll - - - - - - - -

Test passes if there is a filled green square with scrollbars and no red.

- -
-
-
- - - - - \ No newline at end of file diff --git a/test/render/flex/layout-algorithm_algo-cross-line-002.htm.png b/test/render/flex/layout-algorithm_algo-cross-line-002.htm.png deleted file mode 100644 index e6e396079..000000000 Binary files a/test/render/flex/layout-algorithm_algo-cross-line-002.htm.png and /dev/null differ diff --git a/test/render/flex/multi-line-wrap-reverse-column-reverse.htm b/test/render/flex/multi-line-wrap-reverse-column-reverse.htm deleted file mode 100644 index 4f88b89fa..000000000 --- a/test/render/flex/multi-line-wrap-reverse-column-reverse.htm +++ /dev/null @@ -1,74 +0,0 @@ - - - - - CSS Test: flex container multiline wrapping-reverse in column-reverse direction. - - - - - - - - - - -
-

3-1

-

2-2

-

2-1

-

1-3

-

1-2

-

1-1

-
- - - - - \ No newline at end of file diff --git a/test/render/flex/multi-line-wrap-reverse-column-reverse.htm.png b/test/render/flex/multi-line-wrap-reverse-column-reverse.htm.png deleted file mode 100644 index 1cbe50aeb..000000000 Binary files a/test/render/flex/multi-line-wrap-reverse-column-reverse.htm.png and /dev/null differ diff --git a/test/render/flex/multi-line-wrap-reverse-row-reverse.htm b/test/render/flex/multi-line-wrap-reverse-row-reverse.htm deleted file mode 100644 index 07c3143ab..000000000 --- a/test/render/flex/multi-line-wrap-reverse-row-reverse.htm +++ /dev/null @@ -1,68 +0,0 @@ - - - - - CSS Test: flex container multiline wrapping-reverse in row-reverse direction. - - - - - - - - - -
-

3-1

-

2-2

-

2-1

-

1-3

-

1-2

-

1-1

-
- - - - - \ No newline at end of file diff --git a/test/render/flex/multi-line-wrap-reverse-row-reverse.htm.png b/test/render/flex/multi-line-wrap-reverse-row-reverse.htm.png deleted file mode 100644 index 2d73f1cd8..000000000 Binary files a/test/render/flex/multi-line-wrap-reverse-row-reverse.htm.png and /dev/null differ diff --git a/test/render/flex/multi-line-wrap-with-column-reverse.htm b/test/render/flex/multi-line-wrap-with-column-reverse.htm deleted file mode 100644 index ab07525c7..000000000 --- a/test/render/flex/multi-line-wrap-with-column-reverse.htm +++ /dev/null @@ -1,67 +0,0 @@ - - - - - CSS Test: flex container multiline wrapping in column-reverse direction - - - - - - - - - -
-

1-3

-

1-2

-

1-1

-

2-2

-

2-1

-

3-1

-
- - - - - \ No newline at end of file diff --git a/test/render/flex/multi-line-wrap-with-column-reverse.htm.png b/test/render/flex/multi-line-wrap-with-column-reverse.htm.png deleted file mode 100644 index 45b3da248..000000000 Binary files a/test/render/flex/multi-line-wrap-with-column-reverse.htm.png and /dev/null differ diff --git a/test/render/flex/multi-line-wrap-with-row-reverse.htm b/test/render/flex/multi-line-wrap-with-row-reverse.htm deleted file mode 100644 index fc8fb0798..000000000 --- a/test/render/flex/multi-line-wrap-with-row-reverse.htm +++ /dev/null @@ -1,64 +0,0 @@ - - - - - CSS Test: flex container multiline wrapping in row-reverse direction - - - - - - - - - -
-

1-3

-

1-2

-

1-1

-

2-2

-

2-1

-

3-1

-
- - - - - \ No newline at end of file diff --git a/test/render/flex/multi-line-wrap-with-row-reverse.htm.png b/test/render/flex/multi-line-wrap-with-row-reverse.htm.png deleted file mode 100644 index b715b147a..000000000 Binary files a/test/render/flex/multi-line-wrap-with-row-reverse.htm.png and /dev/null differ diff --git a/test/render/flex/multiline-reverse-wrap-baseline.htm b/test/render/flex/multiline-reverse-wrap-baseline.htm deleted file mode 100644 index e35cf60f8..000000000 --- a/test/render/flex/multiline-reverse-wrap-baseline.htm +++ /dev/null @@ -1,61 +0,0 @@ - - - -CSS Flexbox: multiline reverse wrap baseline. - - - - - - -
-
first
first
first
-
second
-
third
-
fourth
fourth
-
- -
-
first
first
first
-
second
-
third
-
fourth
fourth
-
- -
-
first
-
second
-
third
third
-
- - - - - - \ No newline at end of file diff --git a/test/render/flex/multiline-reverse-wrap-baseline.htm.png b/test/render/flex/multiline-reverse-wrap-baseline.htm.png deleted file mode 100644 index 4e68413ee..000000000 Binary files a/test/render/flex/multiline-reverse-wrap-baseline.htm.png and /dev/null differ diff --git a/test/render/flex/negative-flex-margins-crash.htm.png b/test/render/flex/negative-flex-margins-crash.htm.png deleted file mode 100644 index d4ea7f4a7..000000000 Binary files a/test/render/flex/negative-flex-margins-crash.htm.png and /dev/null differ diff --git a/test/render/flex/negative-margins-001.htm b/test/render/flex/negative-margins-001.htm deleted file mode 100644 index 74cf8c33d..000000000 --- a/test/render/flex/negative-margins-001.htm +++ /dev/null @@ -1,51 +0,0 @@ - - - - -CSS Flexible Box Test: Negative margins - - - - - - -

You should see a green rectangle with a black border, 40px wide. You should see no red.

- -
-
-
-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/negative-margins-001.htm.png b/test/render/flex/negative-margins-001.htm.png deleted file mode 100644 index 7d90389d3..000000000 Binary files a/test/render/flex/negative-margins-001.htm.png and /dev/null differ diff --git a/test/render/flex/order-001.htm b/test/render/flex/order-001.htm deleted file mode 100644 index 2f305fb9d..000000000 --- a/test/render/flex/order-001.htm +++ /dev/null @@ -1,42 +0,0 @@ - - - - - CSS Test: The 'order' property on flex items set to a value of '-1' - - - - - - - -

Test passes if there is a single blue rectangle on the left, a single orange rectangle directly to its right, and there is no red visible on the page.

-
-
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/order-001.htm.png b/test/render/flex/order-001.htm.png deleted file mode 100644 index 156126b1f..000000000 Binary files a/test/render/flex/order-001.htm.png and /dev/null differ diff --git a/test/render/flex/order-with-column-reverse.htm b/test/render/flex/order-with-column-reverse.htm deleted file mode 100644 index 19880e25f..000000000 --- a/test/render/flex/order-with-column-reverse.htm +++ /dev/null @@ -1,44 +0,0 @@ - - - - - CSS Test: flex container layout lowest order with column-reverse direction - - - - - - - - -

Test passes if the paragraph below reads 'First,Second,Third' from top.

-
-

Second,

-

First,

-

Third

-
- - - - - \ No newline at end of file diff --git a/test/render/flex/order-with-column-reverse.htm.png b/test/render/flex/order-with-column-reverse.htm.png deleted file mode 100644 index a9fdf5665..000000000 Binary files a/test/render/flex/order-with-column-reverse.htm.png and /dev/null differ diff --git a/test/render/flex/order-with-row-reverse.htm b/test/render/flex/order-with-row-reverse.htm deleted file mode 100644 index f780761c7..000000000 --- a/test/render/flex/order-with-row-reverse.htm +++ /dev/null @@ -1,42 +0,0 @@ - - - - - CSS Test: flex container layout lowest order with row-reverse direction - - - - - - - - - -

Test passes if the paragraph below reads 'First,Second,Third' from leftmost.

-
-

First,

-

Second,

-

Third

-
- - - - - \ No newline at end of file diff --git a/test/render/flex/order-with-row-reverse.htm.png b/test/render/flex/order-with-row-reverse.htm.png deleted file mode 100644 index cfa0adb7b..000000000 Binary files a/test/render/flex/order-with-row-reverse.htm.png and /dev/null differ diff --git a/test/render/flex/padding-overflow-crash.htm b/test/render/flex/padding-overflow-crash.htm deleted file mode 100644 index d895c4dca..000000000 --- a/test/render/flex/padding-overflow-crash.htm +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - -

Test passes if there is a filled green square only.

- -
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/padding-overflow-crash.htm.png b/test/render/flex/padding-overflow-crash.htm.png deleted file mode 100644 index ced33bc89..000000000 Binary files a/test/render/flex/padding-overflow-crash.htm.png and /dev/null differ diff --git a/test/render/flex/percentage-heights-004.htm b/test/render/flex/percentage-heights-004.htm deleted file mode 100644 index c81c7ed32..000000000 --- a/test/render/flex/percentage-heights-004.htm +++ /dev/null @@ -1,66 +0,0 @@ - - - -CSS Flexbox: Percentages in stretched container - - - - - - -

You should not see tan (except perhaps as the background of a horizontal - scrollbar), and you should not see a vertical scrollbar.

- -
-
-
- hello -
-
-
- -
-
-
- hello -
-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/percentage-heights-004.htm.png b/test/render/flex/percentage-heights-004.htm.png deleted file mode 100644 index 4eca9e233..000000000 Binary files a/test/render/flex/percentage-heights-004.htm.png and /dev/null differ diff --git a/test/render/flex/percentage-heights-006.htm b/test/render/flex/percentage-heights-006.htm deleted file mode 100644 index 60822626a..000000000 --- a/test/render/flex/percentage-heights-006.htm +++ /dev/null @@ -1,48 +0,0 @@ - - - -Definite cross sizes - - - - - - - - -

Test passes if there is a filled green square and no red.

- -
- - - -
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/percentage-heights-006.htm.png b/test/render/flex/percentage-heights-006.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/percentage-heights-006.htm.png and /dev/null differ diff --git a/test/render/flex/percentage-heights-007.htm b/test/render/flex/percentage-heights-007.htm deleted file mode 100644 index a72e33d16..000000000 --- a/test/render/flex/percentage-heights-007.htm +++ /dev/null @@ -1,47 +0,0 @@ - - - -Definite sizes with fixed flex-basis - - - - - - - - -

Test passes if there is a filled green square and no red.

- -
- - - -
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/percentage-heights-007.htm.png b/test/render/flex/percentage-heights-007.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/percentage-heights-007.htm.png and /dev/null differ diff --git a/test/render/flex/percentage-heights-009.htm b/test/render/flex/percentage-heights-009.htm deleted file mode 100644 index 55cbd1e14..000000000 --- a/test/render/flex/percentage-heights-009.htm +++ /dev/null @@ -1,42 +0,0 @@ - - - -height: 100% should not be considered indefinite on a second flex item (triggers an obscure bug in Blink) - - - - - -

Test passes if there is a filled green square and no red.

-
-
-
-
-
-
-
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/percentage-heights-009.htm.png b/test/render/flex/percentage-heights-009.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/percentage-heights-009.htm.png and /dev/null differ diff --git a/test/render/flex/space-evenly-001.htm b/test/render/flex/space-evenly-001.htm deleted file mode 100644 index 51420ef4d..000000000 --- a/test/render/flex/space-evenly-001.htm +++ /dev/null @@ -1,40 +0,0 @@ - - - - - CSS Box Alignment: space-evenly & flexbox with single item - - - - - - - - - -

Test passes if there is a filled green square and no red.

-
-
- - - - \ No newline at end of file diff --git a/test/render/flex/space-evenly-001.htm.png b/test/render/flex/space-evenly-001.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/space-evenly-001.htm.png and /dev/null differ diff --git a/test/render/flex/support/a-green.css b/test/render/flex/support/a-green.css deleted file mode 100644 index b0dbb071d..000000000 --- a/test/render/flex/support/a-green.css +++ /dev/null @@ -1 +0,0 @@ -.a { color: green; } diff --git a/test/render/flex/support/b-green.css b/test/render/flex/support/b-green.css deleted file mode 100644 index a0473f5ca..000000000 --- a/test/render/flex/support/b-green.css +++ /dev/null @@ -1 +0,0 @@ -.b { color: green; } \ No newline at end of file diff --git a/test/render/flex/support/c-red.css b/test/render/flex/support/c-red.css deleted file mode 100644 index d4ba5c64e..000000000 --- a/test/render/flex/support/c-red.css +++ /dev/null @@ -1 +0,0 @@ -.c { color: red; } \ No newline at end of file diff --git a/test/render/flex/support/flexbox.css b/test/render/flex/support/flexbox.css deleted file mode 100644 index 83502cd14..000000000 --- a/test/render/flex/support/flexbox.css +++ /dev/null @@ -1,143 +0,0 @@ -.flexbox { - display: -webkit-flex; - display: flex; -} -.inline-flexbox { - display: -webkit-inline-flex; - display: inline-flex; -} - -.flex-none { - -webkit-flex: none; - flex: none; -} -.flex-auto { - -webkit-flex: auto; - flex: auto; -} -.flex-one { - -webkit-flex: 1; - flex: 1; -} -.flex-one-one-auto { - -webkit-flex: 1 1 auto; - flex: 1 1 auto; -} - -.row { - -webkit-flex-direction: row; - flex-direction: row; -} -.row-reverse { - -webkit-flex-direction: row-reverse; - flex-direction: row-reverse; -} -.column { - -webkit-flex-direction: column; - flex-direction: column; -} -.column-reverse { - -webkit-flex-direction: column-reverse; - flex-direction: column-reverse; -} - -.wrap { - -webkit-flex-wrap: wrap; - flex-wrap: wrap; -} -.wrap-reverse { - -webkit-flex-wrap: wrap-reverse; - flex-wrap: wrap-reverse; -} - -.align-content-flex-start { - -webkit-align-content: flex-start; - align-content: flex-start; -} -.align-content-flex-end { - -webkit-align-content: flex-end; - align-content: flex-end; -} -.align-content-center { - -webkit-align-content: center; - align-content: center; -} -.align-content-space-between { - -webkit-align-content: space-between; - align-content: space-between; -} -.align-content-space-around { - -webkit-align-content: space-around; - align-content: space-around; -} -.align-content-stretch { - -webkit-align-content: stretch; - align-content: stretch; -} - -.align-items-flex-start { - -webkit-align-items: flex-start; - align-items: flex-start; -} -.align-items-flex-end { - -webkit-align-items: flex-end; - align-items: flex-end; -} -.align-items-center { - -webkit-align-items: center; - align-items: center; -} -.align-items-baseline { - -webkit-align-items: baseline; - align-items: baseline; -} -.align-items-stretch { - -webkit-align-items: stretch; - align-items: stretch; -} - -.align-self-auto { - -webkit-align-self: auto; - align-self: auto; -} -.align-self-flex-start { - -webkit-align-self: flex-start; - align-self: flex-start; -} -.align-self-flex-end { - -webkit-align-self: flex-end; - align-self: flex-end; -} -.align-self-center { - -webkit-align-self: center; - align-self: center; -} -.align-self-baseline { - -webkit-align-self: baseline; - align-self: baseline; -} -.align-self-stretch { - -webkit-align-self: stretch; - align-self: stretch; -} - -.justify-content-flex-start { - -webkit-justify-content: flex-start; - justify-content: flex-start; -} -.justify-content-flex-end { - -webkit-justify-content: flex-end; - justify-content: flex-end; -} -.justify-content-center { - -webkit-justify-content: center; - justify-content: center; -} -.justify-content-space-between { - -webkit-justify-content: space-between; - justify-content: space-between; -} -.justify-content-space-around { - -webkit-justify-content: space-around; - justify-content: space-around; -} diff --git a/test/render/flex/support/import-green.css b/test/render/flex/support/import-green.css deleted file mode 100644 index 537104e66..000000000 --- a/test/render/flex/support/import-green.css +++ /dev/null @@ -1 +0,0 @@ -.import { color: green; } diff --git a/test/render/flex/support/import-red.css b/test/render/flex/support/import-red.css deleted file mode 100644 index 9945ef471..000000000 --- a/test/render/flex/support/import-red.css +++ /dev/null @@ -1 +0,0 @@ -.import { color: red; } diff --git a/test/render/flex/support/test-style.css b/test/render/flex/support/test-style.css deleted file mode 100644 index 17f44c117..000000000 --- a/test/render/flex/support/test-style.css +++ /dev/null @@ -1,18 +0,0 @@ - #test01, #test02, #test03{ - width: 50px; - height: 50px; - text-align:center; - font-size: 20px; - } - #test{ - background: #ff0000; - } - #test01{ - background: #7FFF00; - } - #test02{ - background: #00FFFF; - } - #test03{ - background: #4169E1; - } diff --git a/test/render/flex/table-as-item-auto-min-width.htm b/test/render/flex/table-as-item-auto-min-width.htm deleted file mode 100644 index ae5f240e6..000000000 --- a/test/render/flex/table-as-item-auto-min-width.htm +++ /dev/null @@ -1,16 +0,0 @@ - - - -CSS Flexbox Test: Flex item as table, specified width less than minimum intrinsic width - - - -

Test passes if there is a filled green square and no red.

-
-
-
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/table-as-item-auto-min-width.htm.png b/test/render/flex/table-as-item-auto-min-width.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/table-as-item-auto-min-width.htm.png and /dev/null differ diff --git a/test/render/flex/table-as-item-fixed-min-width-2.htm b/test/render/flex/table-as-item-fixed-min-width-2.htm deleted file mode 100644 index 34b4a2b95..000000000 --- a/test/render/flex/table-as-item-fixed-min-width-2.htm +++ /dev/null @@ -1,23 +0,0 @@ - - - -table is flex item - - - - - - -

Test passes if there is a filled green square and no red.

- -
-
-
-
-
-
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/table-as-item-fixed-min-width-2.htm.png b/test/render/flex/table-as-item-fixed-min-width-2.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/table-as-item-fixed-min-width-2.htm.png and /dev/null differ diff --git a/test/render/flex/table-as-item-fixed-min-width.htm b/test/render/flex/table-as-item-fixed-min-width.htm deleted file mode 100644 index 59457d30a..000000000 --- a/test/render/flex/table-as-item-fixed-min-width.htm +++ /dev/null @@ -1,21 +0,0 @@ - - - -CSS Flexbox Test: Flex item as table, specified width and min-width less than minimum intrinsic width - - - - - - - -

Test passes if there is a filled green square and no red.

- -
-
-
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/table-as-item-fixed-min-width.htm.png b/test/render/flex/table-as-item-fixed-min-width.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/table-as-item-fixed-min-width.htm.png and /dev/null differ diff --git a/test/render/flex/table-as-item-narrow-content-2.htm.png b/test/render/flex/table-as-item-narrow-content-2.htm.png deleted file mode 100644 index a08a84f42..000000000 Binary files a/test/render/flex/table-as-item-narrow-content-2.htm.png and /dev/null differ diff --git a/test/render/flex/table-as-item-narrow-content.htm b/test/render/flex/table-as-item-narrow-content.htm deleted file mode 100644 index 5aeafed47..000000000 --- a/test/render/flex/table-as-item-narrow-content.htm +++ /dev/null @@ -1,18 +0,0 @@ - - - -CSS Flexbox Test: Flex item as table with narrow content - - - - -

Test passes if there is a filled green square.

-
-
-
-
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/table-as-item-narrow-content.htm.png b/test/render/flex/table-as-item-narrow-content.htm.png deleted file mode 100644 index 1d8721367..000000000 Binary files a/test/render/flex/table-as-item-narrow-content.htm.png and /dev/null differ diff --git a/test/render/flex/table-as-item-percent-width-cell-001.htm.png b/test/render/flex/table-as-item-percent-width-cell-001.htm.png deleted file mode 100644 index 6b8de0e9e..000000000 Binary files a/test/render/flex/table-as-item-percent-width-cell-001.htm.png and /dev/null differ diff --git a/test/render/flex/table-as-item-stretch-cross-size-3.htm b/test/render/flex/table-as-item-stretch-cross-size-3.htm deleted file mode 100644 index 9bd295840..000000000 --- a/test/render/flex/table-as-item-stretch-cross-size-3.htm +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - -

Test passes if there is a filled green square and no red.

-
- -
- - - -
-
- - - \ No newline at end of file diff --git a/test/render/flex/table-as-item-stretch-cross-size-3.htm.png b/test/render/flex/table-as-item-stretch-cross-size-3.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/table-as-item-stretch-cross-size-3.htm.png and /dev/null differ diff --git a/test/render/flex/table-as-item-stretch-cross-size-4.htm b/test/render/flex/table-as-item-stretch-cross-size-4.htm deleted file mode 100644 index 31642cec1..000000000 --- a/test/render/flex/table-as-item-stretch-cross-size-4.htm +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - -

Test passes if there is a filled green square and no red.

-
- - -
-
-
-
-
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/table-as-item-stretch-cross-size-4.htm.png b/test/render/flex/table-as-item-stretch-cross-size-4.htm.png deleted file mode 100644 index d230b8dd3..000000000 Binary files a/test/render/flex/table-as-item-stretch-cross-size-4.htm.png and /dev/null differ diff --git a/test/render/flex/table-with-infinite-max-intrinsic-width.htm b/test/render/flex/table-with-infinite-max-intrinsic-width.htm deleted file mode 100644 index 2eebf92d6..000000000 --- a/test/render/flex/table-with-infinite-max-intrinsic-width.htm +++ /dev/null @@ -1,19 +0,0 @@ - - - -Table flex item with infinite max intrinsic inline size - - - -

Test passes if there is a filled green square.

-
- - - - - -
  
-
- - - \ No newline at end of file diff --git a/test/render/flex/table-with-infinite-max-intrinsic-width.htm.png b/test/render/flex/table-with-infinite-max-intrinsic-width.htm.png deleted file mode 100644 index ae0cbd172..000000000 Binary files a/test/render/flex/table-with-infinite-max-intrinsic-width.htm.png and /dev/null differ diff --git a/test/render/flex/zero-content-size-with-scrollbar-crash.htm b/test/render/flex/zero-content-size-with-scrollbar-crash.htm deleted file mode 100644 index dceaffca0..000000000 --- a/test/render/flex/zero-content-size-with-scrollbar-crash.htm +++ /dev/null @@ -1,11 +0,0 @@ - - - - - -
-
-
- - - \ No newline at end of file diff --git a/test/render/flex/zero-content-size-with-scrollbar-crash.htm.png b/test/render/flex/zero-content-size-with-scrollbar-crash.htm.png deleted file mode 100644 index dcbee0d89..000000000 Binary files a/test/render/flex/zero-content-size-with-scrollbar-crash.htm.png and /dev/null differ diff --git a/test/render/render-1-inline.htm b/test/render/render-1-inline.htm deleted file mode 100644 index fc8b7b193..000000000 --- a/test/render/render-1-inline.htm +++ /dev/null @@ -1 +0,0 @@ -
qBIG WORDSq.

PINKq.

GREENq.
\ No newline at end of file diff --git a/test/render/render-1-inline.htm.png b/test/render/render-1-inline.htm.png deleted file mode 100644 index 2a5987a70..000000000 Binary files a/test/render/render-1-inline.htm.png and /dev/null differ diff --git a/test/render/table-1-width.htm b/test/render/table-1-width.htm deleted file mode 100644 index 118c2f17a..000000000 --- a/test/render/table-1-width.htm +++ /dev/null @@ -1,6 +0,0 @@ - - -
- -
aaaaaaaaa - text text text text text diff --git a/test/render/table-1-width.htm.png b/test/render/table-1-width.htm.png deleted file mode 100644 index b243e7318..000000000 Binary files a/test/render/table-1-width.htm.png and /dev/null differ diff --git a/test/render/table-2-width.htm b/test/render/table-2-width.htm deleted file mode 100644 index 77d7aa973..000000000 --- a/test/render/table-2-width.htm +++ /dev/null @@ -1,9 +0,0 @@ - - - - - -
text diff --git a/test/render/table-2-width.htm.png b/test/render/table-2-width.htm.png deleted file mode 100644 index f88e0c87b..000000000 Binary files a/test/render/table-2-width.htm.png and /dev/null differ diff --git a/test/render/table-3-width.htm b/test/render/table-3-width.htm deleted file mode 100644 index ac00594d9..000000000 --- a/test/render/table-3-width.htm +++ /dev/null @@ -1,7 +0,0 @@ - - - -
text diff --git a/test/render/table-3-width.htm.png b/test/render/table-3-width.htm.png deleted file mode 100644 index b852b89f5..000000000 Binary files a/test/render/table-3-width.htm.png and /dev/null differ diff --git a/test/render/table-4-td-width.htm b/test/render/table-4-td-width.htm deleted file mode 100644 index 910657206..000000000 --- a/test/render/table-4-td-width.htm +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - -
-
aaa -
- bbb bbb bbb bbb bbb bbb bbb bbb bbb bbb \ No newline at end of file diff --git a/test/render/table-4-td-width.htm.png b/test/render/table-4-td-width.htm.png deleted file mode 100644 index c1617f85b..000000000 Binary files a/test/render/table-4-td-width.htm.png and /dev/null differ diff --git a/test/render/test1.htm b/test/render/test1.htm deleted file mode 100644 index eee82c866..000000000 --- a/test/render/test1.htm +++ /dev/null @@ -1,54 +0,0 @@ - -
-
-
-
-
-
-
-
-
-
diff --git a/test/render/test1.htm.png b/test/render/test1.htm.png deleted file mode 100644 index 2aa261c3b..000000000 Binary files a/test/render/test1.htm.png and /dev/null differ diff --git a/test/render/test10.htm b/test/render/test10.htm deleted file mode 100644 index af2a3c06e..000000000 --- a/test/render/test10.htm +++ /dev/null @@ -1,19 +0,0 @@ - - - - Lorem -
a
b
-
\ No newline at end of file diff --git a/test/render/test10.htm.png b/test/render/test10.htm.png deleted file mode 100644 index c831e42be..000000000 Binary files a/test/render/test10.htm.png and /dev/null differ diff --git a/test/render/test11.htm b/test/render/test11.htm deleted file mode 100644 index 7506cb83b..000000000 --- a/test/render/test11.htm +++ /dev/null @@ -1,14 +0,0 @@ - - - - Lorem -
- sqt -
- amet. -
\ No newline at end of file diff --git a/test/render/test11.htm.png b/test/render/test11.htm.png deleted file mode 100644 index 0ed2c1c55..000000000 Binary files a/test/render/test11.htm.png and /dev/null differ diff --git a/test/render/test12.htm b/test/render/test12.htm deleted file mode 100644 index d87e354d4..000000000 --- a/test/render/test12.htm +++ /dev/null @@ -1 +0,0 @@ -Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. \ No newline at end of file diff --git a/test/render/test12.htm.png b/test/render/test12.htm.png deleted file mode 100644 index 6bdd29756..000000000 Binary files a/test/render/test12.htm.png and /dev/null differ diff --git a/test/render/test13.htm b/test/render/test13.htm deleted file mode 100644 index 51534654f..000000000 --- a/test/render/test13.htm +++ /dev/null @@ -1,56 +0,0 @@ - - - Hello
My
- Dear
World -
-
- - Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet
- Lorem ipsum dolor - top
3
- sit amet
- dolorsit der - Lorem - wery - - ipsud - - asd - - 2lkjsd - bottom - a - top - kjlkds
- jkkl -
- sdd -
- dolor sit amet
- Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet
- Lorem ipsum amet - bottom
3
4
- Lorem ipsum dolor - top
3
- sit amet
- Lorem ipsum amet - top
3
4
- Lorem ipsum dolor - bottom
3
- sit amet
- Lorem ipsum dolor - bottom
3
- sit amet
- Lorem ipsum dolor - top
3
- sit amet
-
\ No newline at end of file diff --git a/test/render/test13.htm.png b/test/render/test13.htm.png deleted file mode 100644 index bd5a506c5..000000000 Binary files a/test/render/test13.htm.png and /dev/null differ diff --git a/test/render/test14.htm b/test/render/test14.htm deleted file mode 100644 index 96b312a86..000000000 --- a/test/render/test14.htm +++ /dev/null @@ -1,2 +0,0 @@ -
float
-
table
\ No newline at end of file diff --git a/test/render/test14.htm.png b/test/render/test14.htm.png deleted file mode 100644 index fa7834443..000000000 Binary files a/test/render/test14.htm.png and /dev/null differ diff --git a/test/render/test15.htm b/test/render/test15.htm deleted file mode 100644 index a9e2dfc64..000000000 --- a/test/render/test15.htm +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - -
- - - \ No newline at end of file diff --git a/test/render/test15.htm.png b/test/render/test15.htm.png deleted file mode 100644 index 0a624fbd9..000000000 Binary files a/test/render/test15.htm.png and /dev/null differ diff --git a/test/render/test16.htm b/test/render/test16.htm deleted file mode 100644 index 30695a9f4..000000000 --- a/test/render/test16.htm +++ /dev/null @@ -1,3 +0,0 @@ -
- ab -
\ No newline at end of file diff --git a/test/render/test16.htm.png b/test/render/test16.htm.png deleted file mode 100644 index 285039ec8..000000000 Binary files a/test/render/test16.htm.png and /dev/null differ diff --git a/test/render/test17.htm b/test/render/test17.htm deleted file mode 100644 index e5ebd4b23..000000000 --- a/test/render/test17.htm +++ /dev/null @@ -1,3 +0,0 @@ -
- Lorem ipsum dolor sit amet -
\ No newline at end of file diff --git a/test/render/test17.htm.png b/test/render/test17.htm.png deleted file mode 100644 index 76962cff3..000000000 Binary files a/test/render/test17.htm.png and /dev/null differ diff --git a/test/render/test18.htm b/test/render/test18.htm deleted file mode 100644 index 1e4c73206..000000000 --- a/test/render/test18.htm +++ /dev/null @@ -1,6 +0,0 @@ -
- left-top - right-top - left-bottom - right-bottom -
\ No newline at end of file diff --git a/test/render/test18.htm.png b/test/render/test18.htm.png deleted file mode 100644 index 0d07afb4d..000000000 Binary files a/test/render/test18.htm.png and /dev/null differ diff --git a/test/render/test19.htm b/test/render/test19.htm deleted file mode 100644 index 16ecaf428..000000000 --- a/test/render/test19.htm +++ /dev/null @@ -1,4 +0,0 @@ -
- aaa - -
\ No newline at end of file diff --git a/test/render/test19.htm.png b/test/render/test19.htm.png deleted file mode 100644 index ee3a473e7..000000000 Binary files a/test/render/test19.htm.png and /dev/null differ diff --git a/test/render/test2.htm b/test/render/test2.htm deleted file mode 100644 index 4dce07d17..000000000 --- a/test/render/test2.htm +++ /dev/null @@ -1,54 +0,0 @@ - - - litehtml • Fast and lightweight HTML/CSS rendering engine - - - -
- -
- -
-
- - diff --git a/test/render/test2.htm.png b/test/render/test2.htm.png deleted file mode 100644 index 167fc6116..000000000 Binary files a/test/render/test2.htm.png and /dev/null differ diff --git a/test/render/test20.htm b/test/render/test20.htm deleted file mode 100644 index 8b6cfbc74..000000000 --- a/test/render/test20.htm +++ /dev/null @@ -1,3 +0,0 @@ -
- -
diff --git a/test/render/test20.htm.png b/test/render/test20.htm.png deleted file mode 100644 index 1f7ce172d..000000000 Binary files a/test/render/test20.htm.png and /dev/null differ diff --git a/test/render/test21.htm b/test/render/test21.htm deleted file mode 100644 index 1d7c36388..000000000 --- a/test/render/test21.htm +++ /dev/null @@ -1,13 +0,0 @@ - - - - -
- - - - -
- Lorem ipsum -
-
\ No newline at end of file diff --git a/test/render/test21.htm.png b/test/render/test21.htm.png deleted file mode 100644 index 50ec2d05d..000000000 Binary files a/test/render/test21.htm.png and /dev/null differ diff --git a/test/render/test22.htm b/test/render/test22.htm deleted file mode 100644 index 8085b34e0..000000000 --- a/test/render/test22.htm +++ /dev/null @@ -1,9 +0,0 @@ -
- - - - -
- Lorem ipsum -
-
\ No newline at end of file diff --git a/test/render/test22.htm.png b/test/render/test22.htm.png deleted file mode 100644 index fff069296..000000000 Binary files a/test/render/test22.htm.png and /dev/null differ diff --git a/test/render/test23.htm b/test/render/test23.htm deleted file mode 100644 index dc71ab1b2..000000000 --- a/test/render/test23.htm +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - -
- - - - - -
Pellentesque - Duis rutrum nibh vestibulum finibus luctus. Sed ac gravida urna. Phasellus at est ut augue interdum condimentum. Nunc scelerisque, ligula a feugiat aliquet, est enim pulvinar nunc, at feugiat velit quam vel leo. -
-
\ No newline at end of file diff --git a/test/render/test23.htm.png b/test/render/test23.htm.png deleted file mode 100644 index 83a7cc768..000000000 Binary files a/test/render/test23.htm.png and /dev/null differ diff --git a/test/render/test24.htm b/test/render/test24.htm deleted file mode 100644 index 13fd341c1..000000000 --- a/test/render/test24.htm +++ /dev/null @@ -1,4 +0,0 @@ -
- lorem -
ipsum
-
\ No newline at end of file diff --git a/test/render/test24.htm.png b/test/render/test24.htm.png deleted file mode 100644 index 9e8ff8440..000000000 Binary files a/test/render/test24.htm.png and /dev/null differ diff --git a/test/render/test25.htm b/test/render/test25.htm deleted file mode 100644 index 8113b174a..000000000 --- a/test/render/test25.htm +++ /dev/null @@ -1,15 +0,0 @@ -
- Lorem ipsum dolor sit amets, consectetur. -
- -
- Lorem ipsum dolor sit amets, consectetur. -
- -
- Lorem ipsum dolor sit amets, consectetur. -
- -
- Lorem ipsum dolor sit amets, consectetur. -
diff --git a/test/render/test25.htm.png b/test/render/test25.htm.png deleted file mode 100644 index 2d2eef7d4..000000000 Binary files a/test/render/test25.htm.png and /dev/null differ diff --git a/test/render/test26.htm b/test/render/test26.htm deleted file mode 100644 index 5c81498e0..000000000 --- a/test/render/test26.htm +++ /dev/null @@ -1,16 +0,0 @@ - - -
- - - - -
- Duis rutrum nibh vestibulum finibus luctus. Sed ac gravida urna. Phasellus at est ut augue interdum condimentum. Nunc scelerisque, ligula a feugiat aliquet, est enim pulvinar nunc, at feugiat velit quam vel leo. -
-
\ No newline at end of file diff --git a/test/render/test26.htm.png b/test/render/test26.htm.png deleted file mode 100644 index 1dbb335fd..000000000 Binary files a/test/render/test26.htm.png and /dev/null differ diff --git a/test/render/test27.htm b/test/render/test27.htm deleted file mode 100644 index d527db8b9..000000000 --- a/test/render/test27.htm +++ /dev/null @@ -1,13 +0,0 @@ - - - -
- hello -
- - diff --git a/test/render/test27.htm.png b/test/render/test27.htm.png deleted file mode 100644 index a131a7b79..000000000 Binary files a/test/render/test27.htm.png and /dev/null differ diff --git a/test/render/test28.htm b/test/render/test28.htm deleted file mode 100644 index 548e4a8ea..000000000 --- a/test/render/test28.htm +++ /dev/null @@ -1,8 +0,0 @@ - - - - -
- Hello -
-
diff --git a/test/render/test28.htm.png b/test/render/test28.htm.png deleted file mode 100644 index 7d8a98161..000000000 Binary files a/test/render/test28.htm.png and /dev/null differ diff --git a/test/render/test29.htm b/test/render/test29.htm deleted file mode 100644 index 7c472782c..000000000 --- a/test/render/test29.htm +++ /dev/null @@ -1,48 +0,0 @@ - - - - -
- - - - -
- ipsum -
-
-
-
- - - - -
- ipsum -
-
-
-
- - - - -
- ipsum -
-
-
- - - - -
-
- - - - -
- ipsum -
-
diff --git a/test/render/test29.htm.png b/test/render/test29.htm.png deleted file mode 100644 index 1f7c1ed04..000000000 Binary files a/test/render/test29.htm.png and /dev/null differ diff --git a/test/render/test3.htm b/test/render/test3.htm deleted file mode 100644 index 9d8353329..000000000 --- a/test/render/test3.htm +++ /dev/null @@ -1,76 +0,0 @@ - -
-
-
-
-
-
diff --git a/test/render/test3.htm.png b/test/render/test3.htm.png deleted file mode 100644 index d5984604f..000000000 Binary files a/test/render/test3.htm.png and /dev/null differ diff --git a/test/render/test30.htm b/test/render/test30.htm deleted file mode 100644 index 72f3b028b..000000000 --- a/test/render/test30.htm +++ /dev/null @@ -1,3 +0,0 @@ -
- Lorem ipsum -
diff --git a/test/render/test30.htm.png b/test/render/test30.htm.png deleted file mode 100644 index 29a0e59bb..000000000 Binary files a/test/render/test30.htm.png and /dev/null differ diff --git a/test/render/test31.htm b/test/render/test31.htm deleted file mode 100644 index fe6c2ccdd..000000000 --- a/test/render/test31.htm +++ /dev/null @@ -1,7 +0,0 @@ -
-
-
-
-
-
-
diff --git a/test/render/test31.htm.png b/test/render/test31.htm.png deleted file mode 100644 index b72072854..000000000 Binary files a/test/render/test31.htm.png and /dev/null differ diff --git a/test/render/test32.htm b/test/render/test32.htm deleted file mode 100644 index d8f82182f..000000000 --- a/test/render/test32.htm +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - -
-
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. -
-
- Lorem ipsum dolor sit -
-
-
-
- - diff --git a/test/render/test32.htm.png b/test/render/test32.htm.png deleted file mode 100644 index 18b870382..000000000 Binary files a/test/render/test32.htm.png and /dev/null differ diff --git a/test/render/test33.htm b/test/render/test33.htm deleted file mode 100644 index d47eb2365..000000000 --- a/test/render/test33.htm +++ /dev/null @@ -1,5 +0,0 @@ - - - - -
Lorem ipsum dolor
diff --git a/test/render/test33.htm.png b/test/render/test33.htm.png deleted file mode 100644 index d1bee94be..000000000 Binary files a/test/render/test33.htm.png and /dev/null differ diff --git a/test/render/test34.htm b/test/render/test34.htm deleted file mode 100644 index 34586732b..000000000 --- a/test/render/test34.htm +++ /dev/null @@ -1,14 +0,0 @@ - - - - - -
Should be on right side
-
- - - - -
Should be on left side
diff --git a/test/render/test34.htm.png b/test/render/test34.htm.png deleted file mode 100644 index fd4dbab0a..000000000 Binary files a/test/render/test34.htm.png and /dev/null differ diff --git a/test/render/test35.htm b/test/render/test35.htm deleted file mode 100644 index aeffaec46..000000000 --- a/test/render/test35.htm +++ /dev/null @@ -1,28 +0,0 @@ - - - diff --git a/test/render/test35.htm.png b/test/render/test35.htm.png deleted file mode 100644 index 9ab5b44d8..000000000 Binary files a/test/render/test35.htm.png and /dev/null differ diff --git a/test/render/test36.htm b/test/render/test36.htm deleted file mode 100644 index 62afa347f..000000000 --- a/test/render/test36.htm +++ /dev/null @@ -1,68 +0,0 @@ - - -
-
- - - -
-
diff --git a/test/render/test36.htm.png b/test/render/test36.htm.png deleted file mode 100644 index ef7f915aa..000000000 Binary files a/test/render/test36.htm.png and /dev/null differ diff --git a/test/render/test37.htm b/test/render/test37.htm deleted file mode 100644 index a6f722111..000000000 --- a/test/render/test37.htm +++ /dev/null @@ -1,46 +0,0 @@ - - -
-
a
-
s
-
-
-
d
-
f
-
diff --git a/test/render/test37.htm.png b/test/render/test37.htm.png deleted file mode 100644 index 75b2b7c4d..000000000 Binary files a/test/render/test37.htm.png and /dev/null differ diff --git a/test/render/test38.htm b/test/render/test38.htm deleted file mode 100644 index a2eb9c325..000000000 --- a/test/render/test38.htm +++ /dev/null @@ -1,33 +0,0 @@ - - -
    -
  • item1
  • -
  • item2
  • -
  • item3
  • -
\ No newline at end of file diff --git a/test/render/test38.htm.png b/test/render/test38.htm.png deleted file mode 100644 index 0abfc11b5..000000000 Binary files a/test/render/test38.htm.png and /dev/null differ diff --git a/test/render/test39.htm b/test/render/test39.htm deleted file mode 100644 index 6e417bb70..000000000 --- a/test/render/test39.htm +++ /dev/null @@ -1,42 +0,0 @@ - - -
-
block number 1
-
block number 2
-
hello my dear friend
-
-
-
block number 1
-
block number 2
-
hello my dear friend
-
-
-
block number 1
-
block number 2
-
hello my dear friend
-
-
-
block number 1
-
block number 2
-
hello my dear friend
-
diff --git a/test/render/test39.htm.png b/test/render/test39.htm.png deleted file mode 100644 index 5d518ffb8..000000000 Binary files a/test/render/test39.htm.png and /dev/null differ diff --git a/test/render/test4.htm b/test/render/test4.htm deleted file mode 100644 index 5bad119d8..000000000 --- a/test/render/test4.htm +++ /dev/null @@ -1,42 +0,0 @@ - - -
-
-
-
-
-
- diff --git a/test/render/test4.htm.png b/test/render/test4.htm.png deleted file mode 100644 index 310c3609f..000000000 Binary files a/test/render/test4.htm.png and /dev/null differ diff --git a/test/render/test5.htm b/test/render/test5.htm deleted file mode 100644 index 2e3efee51..000000000 --- a/test/render/test5.htm +++ /dev/null @@ -1,31 +0,0 @@ - - -
-
-
-
- diff --git a/test/render/test5.htm.png b/test/render/test5.htm.png deleted file mode 100644 index 276ecda5d..000000000 Binary files a/test/render/test5.htm.png and /dev/null differ diff --git a/test/render/test6.htm b/test/render/test6.htm deleted file mode 100644 index cd5276464..000000000 --- a/test/render/test6.htm +++ /dev/null @@ -1,29 +0,0 @@ - - - - oops - - Hello -
    -
  • item1
  • -
  • item2
  • -
  • item3
  • -
- hhh -
    -
  • item1-1
  • -
  • item1-2
  • -
  • item1-3
  • -
- World
-
- diff --git a/test/render/test6.htm.png b/test/render/test6.htm.png deleted file mode 100644 index 819fe6be3..000000000 Binary files a/test/render/test6.htm.png and /dev/null differ diff --git a/test/render/test7.htm b/test/render/test7.htm deleted file mode 100644 index ec532eaf1..000000000 --- a/test/render/test7.htm +++ /dev/null @@ -1,19 +0,0 @@ - -
\ No newline at end of file diff --git a/test/render/test7.htm.png b/test/render/test7.htm.png deleted file mode 100644 index ebf17da11..000000000 Binary files a/test/render/test7.htm.png and /dev/null differ diff --git a/test/render/test8.htm b/test/render/test8.htm deleted file mode 100644 index 8d68ac855..000000000 --- a/test/render/test8.htm +++ /dev/null @@ -1,32 +0,0 @@ - -
    -
  • Item1
  • -
  • Item2
  • -
  • Item3
  • -
-
    -
  1. Item1
  2. -
  3. Item2
  4. -
  5. Item3
  6. -
-
- Item1 - Item2 - hello - Item3 -
\ No newline at end of file diff --git a/test/render/test8.htm.png b/test/render/test8.htm.png deleted file mode 100644 index c331ca5d6..000000000 Binary files a/test/render/test8.htm.png and /dev/null differ diff --git a/test/render/test9.htm b/test/render/test9.htm deleted file mode 100644 index 34d34e061..000000000 --- a/test/render/test9.htm +++ /dev/null @@ -1,2 +0,0 @@ -

BIG WORDS.
WORDS
MORE
AAA
-
MORE WORDS.

diff --git a/test/render/test9.htm.png b/test/render/test9.htm.png deleted file mode 100644 index 6f661fcb7..000000000 Binary files a/test/render/test9.htm.png and /dev/null differ diff --git a/test/render/text-before-after.htm b/test/render/text-before-after.htm deleted file mode 100644 index a714b53ae..000000000 --- a/test/render/text-before-after.htm +++ /dev/null @@ -1,18 +0,0 @@ - - -Hello \ No newline at end of file diff --git a/test/render/text-before-after.htm.png b/test/render/text-before-after.htm.png deleted file mode 100644 index 118a608d4..000000000 Binary files a/test/render/text-before-after.htm.png and /dev/null differ diff --git a/test/render/text-justify.htm b/test/render/text-justify.htm deleted file mode 100644 index 4528abba7..000000000 --- a/test/render/text-justify.htm +++ /dev/null @@ -1,9 +0,0 @@ -

-Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Pretium aenean pharetra magna ac placerat. Augue lacus viverra vitae congue. Enim tortor at auctor urna nunc id cursus. Lobortis elementum nibh tellus molestie nunc non blandit massa. Donec ultrices tincidunt arcu non sodales neque sodales ut. Et netus et malesuada fames. Nibh cras pulvinar mattis nunc sed blandit libero. Faucibus turpis in eu mi bibendum neque egestas. Odio facilisis mauris sit amet massa vitae. Massa tempor nec feugiat nisl pretium fusce id velit. -

-

-Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Pretium aenean pharetra magna ac placerat. Augue lacus viverra vitae congue. Enim tortor at auctor urna nunc id cursus. Lobortis elementum nibh tellus molestie nunc non blandit massa. Donec ultrices tincidunt arcu non sodales neque sodales ut. Et netus et malesuada fames. Nibh cras pulvinar mattis nunc sed blandit libero. Faucibus turpis in eu mi bibendum neque egestas. Odio facilisis mauris sit amet massa vitae. Massa tempor nec feugiat nisl pretium fusce id velit. -

-

-Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Pretium aenean pharetra magna ac placerat. Augue lacus viverra vitae congue. Enim tortor at auctor urna nunc id cursus. Lobortis elementum nibh tellus molestie nunc non blandit massa. Donec ultrices tincidunt arcu non sodales neque sodales ut. Et netus et malesuada fames. Nibh cras pulvinar mattis nunc sed blandit libero. Faucibus turpis in eu mi bibendum neque egestas. Odio facilisis mauris sit amet massa vitae. Massa tempor nec feugiat nisl pretium fusce id velit. -

\ No newline at end of file diff --git a/test/render/text-justify.htm.png b/test/render/text-justify.htm.png deleted file mode 100644 index d9482f4fa..000000000 Binary files a/test/render/text-justify.htm.png and /dev/null differ diff --git a/test/render_test.cpp b/test/render_test.cpp deleted file mode 100644 index 4c5c7aadc..000000000 --- a/test/render_test.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#define _CRT_SECURE_NO_WARNINGS -#include -#include -#ifdef _WIN32 - #include "dirent.h" -#else - #include -#endif -#include "../containers/test/test_container.h" -#include "../containers/test/Bitmap.h" -using namespace std; - -vector find_htm_files(); -void test(string filename); - -const char* test_dir = "../test/render"; // ctest is run from litehtml/build - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -using render_test = testing::TestWithParam; - -TEST_P(render_test, _) -{ - test(string(test_dir) + "/" + GetParam()); -} - -INSTANTIATE_TEST_SUITE_P(, render_test, testing::ValuesIn(find_htm_files())); -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -void error(const char* msg) { puts(msg); exit(1); } - -void read_dir(const string& subdir, vector& files) -{ - string full_path = string(test_dir) + "/" + subdir; - DIR* dir = opendir(full_path.c_str()); - if (!dir) error(full_path.c_str()); - while (dirent* ent = readdir(dir)) - { - string name = ent->d_name; - if (ent->d_type == DT_DIR) - { - if(name != "." && name != ".." && name[0] != '-') - { - read_dir(subdir + "/" + name, files); - } - } else if (ent->d_type == DT_REG) - { - if (name[0] != '-' && name.size() > 4 && - (name.substr(name.size() - 4) == ".htm" || name.substr(name.size() - 5) == ".html")) - files.push_back(subdir + "/" + name); - } - } - closedir(dir); -} - -vector find_htm_files() -{ - vector ret; - read_dir("", ret); - sort(ret.begin(), ret.end()); - return ret; -} - -string readfile(string filename) -{ - stringstream ss; - ifstream(filename) >> ss.rdbuf(); - return ss.str(); -} - -Bitmap draw(document::ptr doc, int width, int height) -{ - Bitmap bmp(width, height); - position clip(0, 0, width, height); - - doc->draw((uint_ptr)&bmp, 0, 0, &clip); - - bmp.resize(width, height); - - return bmp; -} - -void test(string filename) -{ - string html = readfile(filename); - - int width = 800, height = 1600; // image will be cropped to content_width/content_height - auto last_slash_pos = filename.find_last_of('/'); - string base_path; - if(last_slash_pos != string::npos) - { - base_path = filename.substr(0, last_slash_pos); - } else - { - base_path = test_dir; - } - test_container container(width, height, base_path); - - auto doc = document::createFromString(html.c_str(), &container); - doc->render(width); - Bitmap bmp = draw(doc, doc->content_width(), doc->content_height()); - - Bitmap good(filename + ".png"); - if (bmp != good) - { - bmp.save(filename + "-FAILED.png"); - ASSERT_TRUE(false); - } -} diff --git a/test/tstring_view_test.cpp b/test/tstring_view_test.cpp deleted file mode 100644 index 026e79207..000000000 --- a/test/tstring_view_test.cpp +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (C) 2020-2021 Primate Labs Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the names of the copyright holders nor the names of their -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include "litehtml/tstring_view.h" - -#include - -#include - -using namespace litehtml; - -TEST(TStringViewTest, DefaultConstructor) -{ - tstring_view view; - - EXPECT_EQ(nullptr, view.data()); - EXPECT_EQ(0, view.size()); - EXPECT_TRUE(view.empty()); -} - -TEST(TStringViewTest, Constructor) -{ - constexpr size_t offset = 5; - constexpr size_t length = 10; - - string string = "the quick brown fox jumps over the lazy dog"; - tstring_view view(string.data() + offset, length); - - EXPECT_EQ(string.data() + offset, view.data()); - EXPECT_EQ(length, view.size()); - EXPECT_FALSE(view.empty()); - - for (size_t i = 0; i < view.size(); i++) { - EXPECT_EQ(string[offset + i], view[i]); - } -} - -TEST(TStringViewTest, RangeForLoop) -{ - constexpr size_t offset = 5; - constexpr size_t length = 10; - - string string = "the quick brown fox jumps over the lazy dog"; - tstring_view view(string.data() + offset, length); - - for (auto c : view) { - // TODO: How can we automatically (rather than manually) verify the - // iterator is working properly here? - std::cout << c << std::endl; - } -} diff --git a/test/url_path_test.cpp b/test/url_path_test.cpp deleted file mode 100644 index 72349cc6a..000000000 --- a/test/url_path_test.cpp +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright (C) 2020-2021 Primate Labs Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the names of the copyright holders nor the names of their -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include "litehtml/url_path.h" - -#include -#include -#include - -#include - -using namespace litehtml; - -namespace { - -struct url_path_testcase { - string base; - string path; - string expected; -}; - -} // namespace - -TEST(URLPathTest, Absolute) -{ - std::vector> testcases = { - { "", false }, - { "a", false }, - { "a/", false }, - { "a/b", false }, - { "a/b/", false }, - { "a/b/c", false }, - { "a/b/c/", false }, - - { "/", true }, - { "/a", true }, - { "/a/", true }, - { "/a/b", true }, - { "/a/b/", true }, - { "/a/b/c", true }, - { "/a/b/c/", true }, - }; - - for (auto& testcase : testcases) { - EXPECT_EQ(testcase.second, is_url_path_absolute(testcase.first)); - } -} - -TEST(URLPathTest, DirectoryName) -{ - std::vector> testcases = { - { "", "." }, - { "a", "." }, - { "a/", "a/" }, - { "a/b", "a/" }, - { "a/b/", "a/b/" }, - { "a/b/c", "a/b/" }, - { "a/b/c/", "a/b/c/" }, - - { "/", "/" }, - { "/a", "/" }, - { "/a/", "/a/" }, - { "/a/b", "/a/" }, - { "/a/b/", "/a/b/" }, - { "/a/b/c", "/a/b/" }, - { "/a/b/c/", "/a/b/c/" }, - }; - - for (auto& testcase : testcases) { - EXPECT_EQ(testcase.second, url_path_directory_name(testcase.first)); - } -} - -TEST(URLPathTest, BaseName) -{ - std::vector> testcases = { - { "", "" }, - { "a", "a" }, - { "a/", "" }, - { "a/b", "b" }, - { "a/b/", "" }, - { "a/b/c", "c" }, - { "a/b/c/", "" }, - - { "/", "" }, - { "/a", "a" }, - { "/a/", "" }, - { "/a/b", "b" }, - { "/a/b/", "" }, - { "/a/b/c", "c" }, - { "/a/b/c/", "" }, - }; - - for (auto& testcase : testcases) { - EXPECT_EQ(testcase.second, url_path_base_name(testcase.first)); - } -} - -TEST(URLPathTest, Append) -{ - std::vector testcases = { - { "", "a", "a" }, - { "/", "a", "/a" }, - { "/a", "", "/a" }, - { "/a", "b", "/a/b" }, - }; - - for (auto& testcase : testcases) { - EXPECT_EQ(testcase.expected, url_path_append(testcase.base, testcase.path)); - } -} - -TEST(URLPathTest, Resolve) -{ - std::vector testcases = { - { "/", "a", "/a" }, - { "/a", "b", "/b" }, - { "/a", "/b", "/b" }, - }; - - for (auto& testcase : testcases) { - EXPECT_EQ(testcase.expected, url_path_resolve(testcase.base, testcase.path)); - } -} \ No newline at end of file diff --git a/test/url_test.cpp b/test/url_test.cpp deleted file mode 100644 index f98ec5f0a..000000000 --- a/test/url_test.cpp +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright (C) 2020-2021 Primate Labs Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the names of the copyright holders nor the names of their -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include "litehtml/url.h" - -#include - -#include - -using namespace litehtml; - -namespace { - -struct url_parse_testcase { - string str; - string scheme; - string authority; - string path; - string query; - string fragment; -}; - -struct url_resolve_testcase { - string base; - string reference; - string expected; -}; - -} // namespace - -TEST(URLTest, DefaultConstructor) -{ - url u; - - EXPECT_TRUE(u.scheme().empty()); - EXPECT_TRUE(u.authority().empty()); - EXPECT_TRUE(u.path().empty()); - EXPECT_TRUE(u.query().empty()); - EXPECT_TRUE(u.fragment().empty()); -} - -TEST(URLTest, Parse) -{ - std::vector testcases = { - - // Example from RFC 3986 that includes a scheme, an authority, a path, - // a query, and a fragment. - { "foo://example.com:8042/over/there?name=ferret#nose", - "foo", "example.com:8042", "/over/there", "name=ferret", "nose" }, - - // Example from RFC 3986 that only includes a scheme and a path. - { "urn:example:animal:ferret:nose", - "urn", "", "example:animal:ferret:nose", "", "" }, - - { "http://www.litehtml.com/", - "http", "www.litehtml.com", "/", "", "" }, - - { "https://www.slashdot.org/", - "https", "www.slashdot.org", "/", "", "" }, - - { "https://www.slashdot.org", - "https", "www.slashdot.org", "", "", "" }, - - { "https://news.slashdot.org/story/21/09/24/2157247/", - "https", "news.slashdot.org", "/story/21/09/24/2157247/", "", "" }, - - { "https://www.cbc.ca/news/politics/spavor-kovrig-return-1.6189516", - "https", "www.cbc.ca", "/news/politics/spavor-kovrig-return-1.6189516", "", "" }, - - { "https://twitter.com/geekbench/status/1412433598200823810", - "https", "twitter.com", "/geekbench/status/1412433598200823810", "", "" }, - - { "https://browser.geekbench.com/v5/cpu/search?q=ryzen", - "https", "browser.geekbench.com", "/v5/cpu/search", "q=ryzen", "" }, - - { "https://datatracker.ietf.org/doc/html/rfc3986#section-2.2", - "https", "datatracker.ietf.org", "/doc/html/rfc3986", "", "section-2.2" }, - - { "file:///home/litehtml/build/hipster.html", - "file", "", "/home/litehtml/build/hipster.html" }, - - { "/home/litehtml/Projects/litehtml/build/hipster.html", - "", "", "/home/litehtml/Projects/litehtml/build/hipster.html" }, - }; - - for (auto& testcase : testcases) { - url u(testcase.str); - - EXPECT_EQ(testcase.scheme, u.scheme()); - EXPECT_EQ(testcase.authority, u.authority()); - EXPECT_EQ(testcase.path, u.path()); - EXPECT_EQ(testcase.query, u.query()); - EXPECT_EQ(testcase.fragment, u.fragment()); - } -} - -TEST(URLTest, Build) -{ - std::vector testcases = { - - // Example from RFC 3986 that includes a scheme, an authority, a path, - // a query, and a fragment. - { "foo://example.com:8042/over/there?name=ferret#nose", - "foo", "example.com:8042", "/over/there", "name=ferret", "nose" }, - - // Example from RFC 3986 that only includes a scheme and a path. - { "urn:example:animal:ferret:nose", - "urn", "", "example:animal:ferret:nose", "", "" }, - - { "http://www.litehtml.com/", - "http", "www.litehtml.com", "/", "", "" }, - - { "https://www.slashdot.org/", - "https", "www.slashdot.org", "/", "", "" }, - - { "https://www.slashdot.org", - "https", "www.slashdot.org", "", "", "" }, - - { "https://news.slashdot.org/story/21/09/24/2157247/", - "https", "news.slashdot.org", "/story/21/09/24/2157247/", "", "" }, - - { "https://www.cbc.ca/news/politics/spavor-kovrig-return-1.6189516", - "https", "www.cbc.ca", "/news/politics/spavor-kovrig-return-1.6189516", "", "" }, - - { "https://twitter.com/geekbench/status/1412433598200823810", - "https", "twitter.com", "/geekbench/status/1412433598200823810", "", "" }, - - { "https://browser.geekbench.com/v5/cpu/search?q=ryzen", - "https", "browser.geekbench.com", "/v5/cpu/search", "q=ryzen", "" }, - - { "https://datatracker.ietf.org/doc/html/rfc3986#section-2.2", - "https", "datatracker.ietf.org", "/doc/html/rfc3986", "", "section-2.2" }, - - // Disabled since the url class does not regenerate the same URL for - // this test case (it does not emit the double slash at the start of - // the authority). How do we determine which schemes require the double - // slash and which ones do not? - - // { "file:///home/litehtml/build/hipster.html", - // "file", "", "/home/litehtml/build/hipster.html" }, - - { "/home/litehtml/Projects/litehtml/build/hipster.html", - "", "", "/home/litehtml/Projects/litehtml/build/hipster.html" }, - }; - - for (auto& testcase : testcases) { - url u(testcase.scheme, - testcase.authority, - testcase.path, - testcase.query, - testcase.fragment); - EXPECT_EQ(testcase.str, u.str()); - } - -} - -TEST(URLTest, Resolve) -{ - std::vector testcases = { - { "https://www.twitter.com/", "/foo", - "https://www.twitter.com/foo" }, - - { "https://www.twitter.com/", "https://www.facebook.com/", - "https://www.facebook.com/" }, - - { "https://www.example.com/index.html", "about.html", - "https://www.example.com/about.html" }, - }; - - for (auto& testcase : testcases) { - url u = resolve(url(testcase.base), url(testcase.reference)); - url expected(testcase.expected); - - EXPECT_EQ(expected.scheme(), u.scheme()); - EXPECT_EQ(expected.authority(), u.authority()); - EXPECT_EQ(expected.path(), u.path()); - EXPECT_EQ(expected.query(), u.query()); - EXPECT_EQ(expected.fragment(), u.fragment()); - } -}