Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ jobs:
qt-modules: ''
name: Linux-Qt5
use-apt: true
apt-packages: 'qtbase5-dev qttools5-dev ninja-build xvfb libxcb-cursor0'
apt-packages: 'qtbase5-dev qttools5-dev libqt5svg5-dev ninja-build xvfb libxcb-cursor0'
test-cmd: xvfb-run -a ctest -V -E NOT_BUILT

# ========== Windows Builds ==========
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## v2.3.2

- support SVG Images
- [#29](https://github.com/procitec/qlitehtmlbrowser/issues/29): Show links to image files in own Dialog

## v2.3.1

- [#26](https://github.com/procitec/qlitehtmlbrowser/issues/26): Fix Multi-Elemen selection highlight boxes
Expand Down
6 changes: 3 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.28)

project( QLiteHtmlBrowser VERSION 2.3.1 )
project( QLiteHtmlBrowser VERSION 2.3.2 )

include(GNUInstallDirs)

Expand All @@ -27,12 +27,12 @@ if(PROJECT_IS_TOP_LEVEL)
set( AUTOUIC OFF)
set( AUTORCC OFF)

find_package(Qt6 COMPONENTS Core Gui Widgets)
find_package(Qt6 COMPONENTS Core Gui Widgets Svg)
if(Qt6_FOUND)
set(QT_VERSION_MAJOR 6)
else()
set(QT_VERSION_MAJOR 5)
find_package(Qt5 5.15 COMPONENTS Core Gui Widgets REQUIRED)
find_package(Qt5 5.15 COMPONENTS Core Gui Widgets Svg REQUIRED)
endif()

if( WITH_DOCS )
Expand Down
2 changes: 1 addition & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ PRIVATE
"${PROJECT_SOURCE_DIR}/include"
)

target_link_libraries(QLiteHtmlBrowser PRIVATE litehtml Qt::Widgets Qt::Gui Qt::Core)
target_link_libraries(QLiteHtmlBrowser PRIVATE litehtml Qt::Widgets Qt::Gui Qt::Svg Qt::Core)
set_target_properties(QLiteHtmlBrowser PROPERTIES VERSION ${QLiteHtmlBrowser_VERSION})
set_target_properties(QLiteHtmlBrowser PROPERTIES PUBLIC_HEADER "${PUBLIC_HEADERS}")
set_property(TARGET QLiteHtmlBrowser PROPERTY CXX_STANDARD 17)
Expand Down
177 changes: 158 additions & 19 deletions src/QLiteHtmlBrowserImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@
#include <QShortcut>
#include <QtGui/QDesktopServices>
#include <functional>
#include <QtWidgets/QLabel>
#include <QtWidgets/QScrollArea>
#include <QtSvg/QSvgRenderer>
#include <QtGui/QPainter>
#include <QtCore/QBuffer>
#include <QtGui/QScreen>
#include <QtWidgets/QScrollBar>

QLiteHtmlBrowserImpl::QLiteHtmlBrowserImpl( QWidget* parent )
: QWidget( parent )
Expand Down Expand Up @@ -166,20 +173,26 @@ void QLiteHtmlBrowserImpl::mousePressEvent( QMouseEvent* e )
// }
//}

void QLiteHtmlBrowserImpl::setUrl( const QUrl& url, int type, bool clearFWHist )
bool QLiteHtmlBrowserImpl::isImageUrl( const QString& u ) const
{
mUrl = UrlType( url, type );
auto [home_url, home_type] = mHome;
if ( home_url.isEmpty() )
{
mHome = UrlType( url, type );
}
const auto lower = u.toLower();
return lower.endsWith( ".png" ) || lower.endsWith( ".jpg" ) || lower.endsWith( ".jpeg" ) || lower.endsWith( ".gif" ) || lower.endsWith( ".svg" );
}

bool QLiteHtmlBrowserImpl::isHtmlUrl( const QString& u ) const
{
const auto lower = u.toLower();
return lower.endsWith( ".html" ) || lower.endsWith( ".htm" );
}

#include <QtWidgets/QMessageBox>
void QLiteHtmlBrowserImpl::setUrl( const QUrl& url, int type, bool clearFWHist )
{
if ( mContainer )
{
auto pure_url = QUrl( url );
pure_url.setFragment( {} );
QString html;
QByteArray content;

if ( pure_url.isLocalFile() )
{
Expand All @@ -188,20 +201,40 @@ void QLiteHtmlBrowserImpl::setUrl( const QUrl& url, int type, bool clearFWHist )
QFile f( pure_url.toLocalFile() );
if ( f.open( QIODevice::ReadOnly ) )
{
html = f.readAll();
content = f.readAll();
f.close();
}
}
else
{
// eg. if ( url.scheme() == "qthelp" )
html = mResourceHandler( type, url );
content = mResourceHandler( type, url );
}

if ( !html.isEmpty() )
if ( !content.isEmpty() )
{
parseUrl( url );
mContainer->setHtml( html, url );
if ( isHtmlUrl( url.toString() ) )
{
parseUrl( url );
mContainer->setHtml( QString::fromUtf8( content ), url );
}
else if ( isImageUrl( url.toString() ) )
{
onImageClicked( url );
return;
}
else
{
// could not be shown / displayed -> return
return;
}

mUrl = UrlType( url, type );
auto [home_url, home_type] = mHome;
if ( home_url.isEmpty() )
{
mHome = UrlType( url, type );
}

auto hist_url = QUrl();

Expand Down Expand Up @@ -290,18 +323,49 @@ double QLiteHtmlBrowserImpl::scale() const
return scale;
}

QByteArray QLiteHtmlBrowserImpl::loadResource( int /*type*/, const QUrl& url )
QImage QLiteHtmlBrowserImpl::loadSvgFromFile( const QString& filename )
{
QSvgRenderer renderer;
QImage img;
renderer.load( filename );
if ( renderer.isValid() )
{
QSize size( renderer.defaultSize() );
QImage svgImg( size, QImage::Format_ARGB32_Premultiplied );
svgImg.fill( Qt::transparent );
QPainter p( &svgImg );
renderer.render( &p );
img = svgImg;
}
return img;
}

QByteArray QLiteHtmlBrowserImpl::loadResource( int type, const QUrl& url )
{
QByteArray data;

auto resource_type = static_cast<Browser::ResourceType>( type );

QString fileName = findFile( url );
if ( !fileName.isEmpty() )
{
QFile f( fileName );
if ( f.open( QFile::ReadOnly ) )
if ( resource_type == Browser::ResourceType::Image && fileName.toLower().endsWith( ".svg" ) )
{
data = f.readAll();
f.close();
auto img = loadSvgFromFile( fileName );
auto pixmap = QPixmap::fromImage( img );
QBuffer buffer( &data );
buffer.open( QIODevice::WriteOnly );
pixmap.save( &buffer, "PNG" );
buffer.close();
}
else
{
QFile f( fileName );
if ( f.open( QFile::ReadOnly ) )
{
data = f.readAll();
f.close();
}
}
}

Expand Down Expand Up @@ -330,12 +394,13 @@ QUrl QLiteHtmlBrowserImpl::resolveUrl( const QString& url )
{
resolved = QUrl( mBaseUrl ).resolved( _url );
}

if ( !resolved.isRelative() )
{
return resolved;
}

else if ( QFileInfo( resolved.toLocalFile() ).isReadable() )
if ( QFileInfo( resolved.toLocalFile() ).isReadable() )
{
return QUrl::fromLocalFile( resolved.toLocalFile() );
}
Expand Down Expand Up @@ -504,3 +569,77 @@ QString QLiteHtmlBrowserImpl::selectedText() const
}
return text;
}

void QLiteHtmlBrowserImpl::onImageClicked( const QUrl& url )
{
auto pure_url = QUrl( url );
pure_url.setFragment( {} );
QFileInfo f( url.toLocalFile() );

if ( !f.exists() )
{
return;
}

// Bild aus Bytes laden
QImage img;

// sinnvolle Startgröße (z.B. max 80% der Parentgröße)
const QSize parentSize = this->size();
const int def_w = parentSize.width() * 8 / 10;
const int def_h = parentSize.height() * 8 / 10;

if ( !img.load( f.absoluteFilePath() ) )
{
if ( url.toString().toLower().endsWith( ".svg" ) )
{
img = loadSvgFromFile( f.absoluteFilePath() );
}
}

if ( img.isNull() )
{
return;
}

const int w = qMax( img.width(), def_w );
const int h = qMax( img.height(), def_h );

// Dialog mit Bild anlegen, Parent ist dieses Widget (MyViewer)
QDialog* dlg = new QDialog( this );
dlg->setAttribute( Qt::WA_DeleteOnClose );
dlg->setWindowTitle( f.fileName() );

// Inhalt: ScrollArea + QLabel mit Pixmap
auto* layout = new QVBoxLayout( dlg );
auto* scroll = new QScrollArea( dlg );
// scroll->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOn );
// scroll->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOn );
auto* label = new QLabel( dlg );

label->setPixmap( QPixmap::fromImage( img ) );
label->setAlignment( Qt::AlignCenter );
label->resize( w, h );
scroll->setWidget( label );
scroll->setWidgetResizable( true );

layout->addWidget( scroll );
dlg->setLayout( layout );

// Optional: Maximalgröße begrenzen (80% des Screens)
QSize screenSize = screen()->availableGeometry().size();
dlg->setMaximumSize( screenSize * 0.5 );

auto margins = dlg->contentsMargins() + scroll->contentsMargins() + scroll->viewport()->contentsMargins() + label->contentsMargins();

dlg->resize( qMax( 200, w + scroll->verticalScrollBar()->width() + margins.left() + margins.right() ),
qMax( 200, h + scroll->horizontalScrollBar()->height() + margins.top() + margins.bottom() ) );

// Dialog über dem Parent zentrieren (absolute Screen-Koordinaten)
QRect dialogRect = dlg->frameGeometry();
dialogRect.moveCenter( this->mapToGlobal( this->rect().center() ) );
dlg->move( dialogRect.topLeft() );

dlg->setModal( true ); // optional, je nach gewünschtem Verhalten
dlg->show(); // oder dlg->exec();
}
4 changes: 4 additions & 0 deletions src/QLiteHtmlBrowserImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ class QLiteHtmlBrowserImpl : public QWidget
void parseUrl( const QUrl& url );
QString readResourceCss( const QString& ) const;
void applyCSS();
bool isImageUrl( const QString& u ) const;
bool isHtmlUrl( const QString& u ) const;
void onImageClicked( const QUrl& url );
QImage loadSvgFromFile( const QString& filename );

Q_DISABLE_COPY_MOVE( QLiteHtmlBrowserImpl );

Expand Down
6 changes: 3 additions & 3 deletions test/browser/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
project( TestBrowser )

find_package(Qt6 COMPONENTS Core Gui Widgets Help)
find_package(Qt6 COMPONENTS Core Gui Widgets Svg Help)
if(Qt6_FOUND)
set(QT_VERSION_MAJOR 6)
else()
set(QT_VERSION_MAJOR 5)
find_package(Qt5 5.15 COMPONENTS Core Gui Widgets Help REQUIRED)
find_package(Qt5 5.15 COMPONENTS Core Gui Widgets Svg Help REQUIRED)
endif()

add_executable( testbrowser )
Expand All @@ -26,4 +26,4 @@ target_sources( testbrowser
)

target_include_directories(testbrowser PRIVATE "${QLiteHtmlBrowser_SOURCE_DIR}/include")
target_link_libraries( testbrowser PRIVATE QLiteHtmlBrowser Qt::Widgets Qt::Gui Qt::Core Qt::Help )
target_link_libraries( testbrowser PRIVATE QLiteHtmlBrowser Qt::Widgets Qt::Gui Qt::Core Qt::Svg Qt::Help )
1 change: 1 addition & 0 deletions test/browser/files/images_01/images/plantuml.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 31 additions & 0 deletions test/browser/files/images_01/index-linkedimages.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<!DOCTYPE html>
<html>
<head>
<title>Images Demo</title>
</head>
<body>

<div class="figure">
<a class="reference internal image-reference" href="images/gradient_600x600.jpg"><img src="images/gradient_600x600.jpg" alt="unscaled gradient jpg"/></a>
<p>Unscaled image (jpg) from subdirectory.</p>
</div>

<div class="figure">
<a class="reference internal image-reference" href="images/procitec_info.png"><img src="images/procitec_info.png" alt="unscaled procitec png" width="100px" height="150px"/></a>
<p>Scaled image (png) from subdirectory.</p>
</div>

<div class="figure">
<a class="reference internal image-reference" href="images/plantuml.svg"><img src="images/plantuml.svg" alt="unscaled plantuml svg" width="100px" height="150px"/></a>
<p>Scaled image (svg) from subdirectory.</p>
</div>

<div >
<a class="reference internal image-reference" href="abc.jpg"><img src="abc.jpg" alt="broken url jpg" /></a>
<p>Invalid URL.</p>
</div>

</body>
</html>


6 changes: 3 additions & 3 deletions test/library/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
project( QLiteHtmlBrowserTest )

find_package(Qt6 COMPONENTS Core Gui Widgets Test)
find_package(Qt6 COMPONENTS Core Gui Widgets Svg Test)
if(Qt6_FOUND)
set(QT_VERSION_MAJOR 6)
else()
find_package(Qt5 5.15 COMPONENTS Core Gui Widgets Test REQUIRED)
find_package(Qt5 5.15 COMPONENTS Core Gui Widgets Svg Test REQUIRED)
set(QT_VERSION_MAJOR 5)
endif()

Expand Down Expand Up @@ -60,7 +60,7 @@ foreach( name ${test_names})

target_include_directories( ${name} PRIVATE ${QLiteHtmlBrowser_SOURCE_DIR}/include ${QLiteHtmlBrowser_SOURCE_DIR}/src)

target_link_libraries(${name} PUBLIC litehtml Qt::Widgets Qt::Gui Qt::Test )
target_link_libraries(${name} PUBLIC litehtml Qt::Widgets Qt::Gui Qt::Svg Qt::Test )

target_compile_definitions(${name} PRIVATE TEST_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}")

Expand Down
Binary file added test/library/images/plantuml.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions test/library/images/plantuml.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading