From 10c3746a6df45dd154955a9e91e662260f36363f Mon Sep 17 00:00:00 2001 From: Richard Qin Date: Tue, 14 Apr 2026 14:38:42 +0800 Subject: refactor: adjust code structure --- src/render/utils.cc | 135 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 src/render/utils.cc (limited to 'src/render/utils.cc') diff --git a/src/render/utils.cc b/src/render/utils.cc new file mode 100644 index 0000000..9c6610a --- /dev/null +++ b/src/render/utils.cc @@ -0,0 +1,135 @@ +/* + * Ricochet - A minimalist CLI web browser for fast, distraction-free navigation. + * Copyright (C) 2026 Richard Qin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "ricochet/render/utils.hh" + +#include +#include +#include + +namespace ricochet::render { + +std::string decode_entities( std::string text ) +{ + constexpr std::array, 25> entities + = { { { " ", " " }, { "<", "<" }, { ">", ">" }, { "&", "&" }, { """, "\"" }, + { "'", "'" }, { "©", "©" }, { "®", "®" }, { "™", "™" }, { "€", "€" }, + { "£", "£" }, { "¥", "¥" }, { "¢", "¢" }, { "§", "§" }, { "¶", "¶" }, + { "…", "…" }, { "†", "†" }, { "‡", "‡" }, { "•", "•" }, { "′", "′" }, + { "″", "″" }, { "‹", "‹" }, { "›", "›" }, { "‾", "‾" }, { "⁄", "⁄" } } }; + + for ( const auto& [entity, replacement] : entities ) { + std::size_t pos = 0; + while ( ( pos = text.find( entity, pos ) ) != std::string::npos ) { + text.replace( pos, entity.length(), replacement ); + pos += replacement.length(); + } + } + + std::size_t pos = 0; + while ( ( pos = text.find( "&#", pos ) ) != std::string::npos ) { + const std::size_t end = text.find( ';', pos ); + if ( end != std::string::npos && end - pos < 10 ) { + const std::string code = text.substr( pos + 2, end - pos - 2 ); + try { + std::uint32_t cp = 0; + if ( !code.empty() && ( code[0] == 'x' || code[0] == 'X' ) ) { + cp = std::stoul( code.substr( 1 ), nullptr, 16 ); + } else { + cp = std::stoul( code ); + } + if ( cp < 128 ) { + text.replace( pos, end - pos + 1, 1, static_cast( cp ) ); + continue; + } + } catch ( const std::invalid_argument& ) { + (void)0; + } catch ( const std::out_of_range& ) { + (void)0; + } + } + pos++; + } + return text; +} + +std::string extract_attr( const std::string& tag_name, std::string_view attr_name ) +{ + std::string match = " " + std::string( attr_name ) + "="; + std::size_t pos = tag_name.find( match ); + + if ( pos == std::string::npos ) { + match = std::string( attr_name ) + "="; + if ( !tag_name.starts_with( match ) ) { + return ""; + } + pos = 0; + } else { + pos += 1; + } + + pos += attr_name.length() + 1; + if ( pos >= tag_name.length() ) { + return ""; + } + + const char quote = tag_name[pos]; + if ( quote == '"' || quote == '\'' ) { + const std::size_t end = tag_name.find( quote, pos + 1 ); + if ( end != std::string::npos ) { + return tag_name.substr( pos + 1, end - pos - 1 ); + } + return ""; + } + + const std::size_t end = tag_name.find_first_of( " >/", pos ); + return tag_name.substr( pos, ( end == std::string::npos ) ? std::string::npos : end - pos ); +} + +std::size_t get_visible_length( std::string_view str ) +{ + std::size_t len = 0; + bool in_ansi = false; + for ( std::size_t i = 0; i < str.length(); ++i ) { + const auto c = static_cast( str[i] ); + if ( c == 0x1B ) { + in_ansi = true; + } else if ( in_ansi && c == 'm' ) { + in_ansi = false; + } else if ( !in_ansi ) { + if ( ( c & 0x80 ) == 0 || ( c & 0xE0 ) == 0xC0 ) { + len += 1; + } else if ( ( c & 0xF0 ) == 0xE0 || ( c & 0xF8 ) == 0xF0 ) { + len += 2; + } + } + } + return len; +} + +bool is_ignored_tag( std::string_view tag ) +{ + return tag == "head" || tag == "style" || tag == "script" || tag == "meta" || tag == "title" || tag == "link"; +} + +bool is_heading_tag( std::string_view tag ) +{ + return tag == "h1" || tag == "h2" || tag == "h3" || tag == "h4"; +} + +} // namespace ricochet::render -- cgit v1.2.3