From 85cac902ac5ac768289546206827ee4368866c6d Mon Sep 17 00:00:00 2001 From: Mavridis Philippe Date: Wed, 9 Mar 2022 13:30:28 +0200 Subject: [PATCH] TDEHTML: SVG rendering support for tag. Signed-off-by: Mavridis Philippe --- tdehtml/misc/CMakeLists.txt | 2 +- tdehtml/misc/loader.cpp | 107 ++++++++++++++++++++++++++--- tdehtml/misc/loader.h | 14 +++- tdehtml/misc/svgrender.cpp | 74 ++++++++++++++++++++ tdehtml/misc/svgrender.h | 33 +++++++++ tdehtml/rendering/render_image.cpp | 46 ++++++++----- 6 files changed, 247 insertions(+), 29 deletions(-) create mode 100644 tdehtml/misc/svgrender.cpp create mode 100644 tdehtml/misc/svgrender.h diff --git a/tdehtml/misc/CMakeLists.txt b/tdehtml/misc/CMakeLists.txt index 0babd6567..934953297 100644 --- a/tdehtml/misc/CMakeLists.txt +++ b/tdehtml/misc/CMakeLists.txt @@ -33,7 +33,7 @@ set( target tdehtmlmisc ) set( ${target}_SRCS decoder.cpp loader.cpp loader_jpeg.cpp guess_ja.cpp htmlhashes.cpp helper.cpp arena.cpp stringit.cpp - knsplugininstaller.cpp + knsplugininstaller.cpp svgrender.cpp ) tde_add_library( ${target} STATIC_PIC AUTOMOC diff --git a/tdehtml/misc/loader.cpp b/tdehtml/misc/loader.cpp index 7621b5e4f..c6fd83bf1 100644 --- a/tdehtml/misc/loader.cpp +++ b/tdehtml/misc/loader.cpp @@ -74,6 +74,8 @@ #include #endif +#include "svgrender.h" + #include "html/html_documentimpl.h" #include "css/css_stylesheetimpl.h" #include "xml/dom_docimpl.h" @@ -461,7 +463,7 @@ private: static TQString buildAcceptHeader() { - return "image/png, image/jpeg, video/x-mng, image/jp2, image/gif;q=0.5,*/*;q=0.1"; + return "image/png, image/jpeg, image/svg+xml, video/x-mng, image/jp2, image/gif;q=0.5,*/*;q=0.1"; } // ------------------------------------------------------------------------------------- @@ -490,6 +492,18 @@ CachedImage::CachedImage(DocLoader* dl, const DOMString &url, TDEIO::CacheContro m_wasBlocked = true; CachedObject::finish(); } + + /* SVG support */ + isSVGZ = url.string().lower().endsWith(".svgz"); // used for setting correct temp file extension + isSVG = url.string().lower().endsWith(".svg") || isSVGZ; + svgData = TQByteArray(); + svgRender = new TQPixmap(); +#ifndef HAVE_LIBART + if(isSVG) + { + kdDebug(6060) << url.string() << " is a SVG(Z) file but tdelibs was compiled without LIBART support" << endl; + } +#endif } CachedImage::~CachedImage() @@ -654,6 +668,12 @@ const TQPixmap &CachedImage::pixmap( ) const else return m->framePixmap(); } + + else if(isSVG) + { + return *svgRender; + } + else if(p) return *p; @@ -663,15 +683,56 @@ const TQPixmap &CachedImage::pixmap( ) const TQSize CachedImage::pixmap_size() const { - if (m_wasBlocked) return Cache::blockedPixmap->size(); - return (m_hadError ? Cache::brokenPixmap->size() : m ? m->framePixmap().size() : ( p ? p->size() : TQSize())); + if(m_wasBlocked) + { + return Cache::blockedPixmap->size(); + } + else if(m_hadError) + { + return Cache::brokenPixmap->size(); + } + else if(isSVG) + { + return svgRender->size(); + } + else if(m) + { + return m->framePixmap().size(); + } + else if(p) + { + return p->size(); + } + else + { + return TQSize(); + } } TQRect CachedImage::valid_rect() const { - if (m_wasBlocked) return Cache::blockedPixmap->rect(); - return (m_hadError ? Cache::brokenPixmap->rect() : m ? TQRect(m->getValidRect()) : ( p ? TQRect(p->rect()) : TQRect()) ); + if (m_wasBlocked) + { + return Cache::blockedPixmap->rect(); + } + else if (m_hadError) + { + return Cache::brokenPixmap->rect(); + } + else if(isSVG) + { + return TQRect(svgRender->rect()); + } + else if(m) + { + return TQRect(m->getValidRect()); + } + else if(p) + { + return TQRect(p->rect()); + } + return TQRect(); } @@ -681,7 +742,6 @@ void CachedImage::do_notify(const TQPixmap& p, const TQRect& r) it()->setPixmap( p, r, this); } - void CachedImage::movieUpdated( const TQRect& r ) { #ifdef LOADER_DEBUG @@ -820,6 +880,11 @@ void CachedImage::clear() bgSize = TQSize(-1,-1); delete pixPart; pixPart = 0; + isSVG = false; + isSVGZ = false; + svgData = TQByteArray(); + svgRender = 0; + formatType = 0; typeChecked = false; setSize(0); @@ -833,6 +898,32 @@ void CachedImage::data ( TQBuffer &_buffer, bool eof ) #ifdef LOADER_DEBUG kdDebug( 6060 ) << this << "in CachedImage::data(buffersize " << _buffer.buffer().size() <<", eof=" << eof << endl; #endif + + if( isSVG && eof ) + { + /* Store SVG data for later use */ + svgData = _buffer.buffer(); + +#ifdef HAVE_LIBART + /* Render and store SVG as pixmap */ + TQImage i = renderSVG(svgData, svgExtension()); + if(i.isNull()) + { + m_hadError = true; + return; + } + + if(!svgRender->convertFromImage(i)) + { + kdDebug(6060) << "Could not convert TQImage of rendered SVG to TQPixmap!" << endl; + m_hadError = true; + } +#else + m_hadError = true; +#endif + return; + } + if ( !typeChecked ) { // don't attempt incremental loading if we have all the data already @@ -905,8 +996,8 @@ void CachedImage::finish() Status oldStatus = m_status; CachedObject::finish(); if ( oldStatus != m_status ) { - const TQPixmap &pm = pixmap(); - do_notify( pm, pm.rect() ); + const TQPixmap &pm = pixmap(); + do_notify( pm, pm.rect() ); } TQSize s = pixmap_size(); setSize( s.width() * s.height() * 2); diff --git a/tdehtml/misc/loader.h b/tdehtml/misc/loader.h index c60c13a98..087b28ffd 100644 --- a/tdehtml/misc/loader.h +++ b/tdehtml/misc/loader.h @@ -277,8 +277,11 @@ namespace tdehtml bool isTransparent() const { return isFullyTransparent; } bool isErrorImage() const { return m_hadError; } bool isBlockedImage() const { return m_wasBlocked; } + bool isVectorImage() const { return isSVG; } const TQString& suggestedFilename() const { return m_suggestedFilename; } void setSuggestedFilename( const TQString& s ) { m_suggestedFilename = s; } + TQByteArray svg() const { return svgData; } + const TQString svgExtension() const { return (isSVGZ ? "svgz" : (isSVG ? "svg" : TQString::null)); } #ifdef IMAGE_TITLES const TQString& suggestedTitle() const { return m_suggestedTitle; } void setSuggestedTitle( const TQString& s ) { m_suggestedTitle = s; } @@ -313,10 +316,15 @@ namespace tdehtml #ifdef IMAGE_TITLES TQString m_suggestedTitle; #endif - TQMovie* m; + TQMovie* m; TQPixmap* p; - TQPixmap* scaled; - TQPixmap* bg; + TQPixmap* scaled; + TQPixmap* bg; + + bool isSVG, isSVGZ; + TQByteArray svgData; + TQPixmap *svgRender; + QRgb bgColor; TQSize bgSize; mutable TQPixmap* pixPart; diff --git a/tdehtml/misc/svgrender.cpp b/tdehtml/misc/svgrender.cpp new file mode 100644 index 000000000..94766cdcf --- /dev/null +++ b/tdehtml/misc/svgrender.cpp @@ -0,0 +1,74 @@ +/* + * This file is part of the Trinity libraries. + * + * Copyright (C) 2022 Mavridis Philippe + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "svgrender.h" + +#include + +#ifdef HAVE_LIBART +#include +#include +#include +#endif + + +TQImage renderSVG(TQByteArray ba, TQString svgExtension, int width, int height) +{ + if( svgExtension != TQString("svg") && svgExtension != TQString("svgz") ) + { + return TQImage(); + } + +#ifdef HAVE_LIBART + /* Write svg data to a temporary file so that we can process it with KSVGIconEngine */ + KTempFile tf(locateLocal("tmp", "tdehtml"), TQString(".%1").arg(svgExtension)); + if( tf.status() != 0 ) + { + kdDebug(6060) << "[renderSVG] Cannot access temp file: " << tf.name() << endl; + return TQImage(); + } + tf.dataStream()->writeRawBytes(ba, ba.size()); + tf.sync(); + + /* Render the image */ + TQImage *render = new TQImage(); + KSVGIconEngine *svgEngine = new KSVGIconEngine(); + if(svgEngine->load(width, height, tf.name()) ) + { + render = svgEngine->image(); + } + else + { + kdDebug(6060) << "[renderSVG] KSVGIconEngine could not load " << tf.name() << endl; + return TQImage(); + } + + /* Clean up */ + tf.unlink(); + delete svgEngine; + + return *render; +#else + kdDebug(6060) << "[renderSVG] tdelibs were built without libart support." << endl; + return TQImage(); +#endif +} \ No newline at end of file diff --git a/tdehtml/misc/svgrender.h b/tdehtml/misc/svgrender.h new file mode 100644 index 000000000..93a9cd46d --- /dev/null +++ b/tdehtml/misc/svgrender.h @@ -0,0 +1,33 @@ +/* + * This file is part of the Trinity libraries. + * + * Copyright (C) 2022 Mavridis Philippe + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef _tdehtml_svgrender_h +#define _tdehtml_svgrender_h + +#include // tqbytearray +#include + +#include "config.h" // HAVE_LIBART + +TQImage renderSVG(TQByteArray ba, TQString svgExtension, int width = 0, int height = 0); + +#endif \ No newline at end of file diff --git a/tdehtml/rendering/render_image.cpp b/tdehtml/rendering/render_image.cpp index ad8f18bb3..18bc62aed 100644 --- a/tdehtml/rendering/render_image.cpp +++ b/tdehtml/rendering/render_image.cpp @@ -38,6 +38,7 @@ #include "misc/helper.h" #include "misc/htmlattrs.h" #include "misc/loader.h" +#include "misc/svgrender.h" #include "misc/htmltags.h" #include "html/html_formimpl.h" #include "html/html_imageimpl.h" @@ -240,7 +241,7 @@ void RenderImage::paint(PaintInfo& paintInfo, int _tx, int _ty) ? m_oldImage : m_cachedImage; // paint frame around image as long as it is not completely loaded from web. - if (bUnfinishedImageFrame && paintInfo.phase == PaintActionForeground && cWidth > 2 && cHeight > 2 && !complete()) { + if (bUnfinishedImageFrame && paintInfo.phase == PaintActionForeground && cWidth > 2 && cHeight > 2 && !complete() && !i->isVectorImage()) { static TQPixmap *loadingIcon; TQColor bg = tdehtml::retrieveBackgroundColor(this); TQColor fg = tdehtml::hasSufficientContrast(Qt::gray, bg) ? Qt::gray : @@ -297,31 +298,42 @@ void RenderImage::paint(PaintInfo& paintInfo, int _tx, int _ty) if (resizeCache.isNull() && cWidth && cHeight && intrinsicWidth() && intrinsicHeight()) { TQRect scaledrect(i->valid_rect()); + scaledrect.setWidth( ( cWidth*scaledrect.width() ) / intrinsicWidth() ); + scaledrect.setHeight( ( cHeight*scaledrect.height() ) / intrinsicHeight() ); // kdDebug(6040) << "time elapsed: " << dt->elapsed() << endl; // kdDebug( 6040 ) << "have to scale: " << endl; // tqDebug("cw=%d ch=%d pw=%d ph=%d rcw=%d, rch=%d", // cWidth, cHeight, intrinsicWidth(), intrinsicHeight(), resizeCache.width(), resizeCache.height()); - TQWMatrix matrix; - matrix.scale( (float)(cWidth)/intrinsicWidth(), - (float)(cHeight)/intrinsicHeight() ); - resizeCache = pix.xForm( matrix ); - scaledrect.setWidth( ( cWidth*scaledrect.width() ) / intrinsicWidth() ); - scaledrect.setHeight( ( cHeight*scaledrect.height() ) / intrinsicHeight() ); + if(i->isVectorImage()) + { + TQImage newRender = renderSVG(i->svg(), i->svgExtension(), cWidth, cHeight); + if(!resizeCache.convertFromImage(newRender)) + { + kdDebug(6040) << "Could not convert TQImage of rendered SVG to TQPixmap!" << endl; + } + } + else + { + TQWMatrix matrix; + matrix.scale( (float)(cWidth)/intrinsicWidth(), + (float)(cHeight)/intrinsicHeight() ); + resizeCache = pix.xForm( matrix ); // tqDebug("resizeCache size: %d/%d", resizeCache.width(), resizeCache.height()); // tqDebug("valid: %d/%d, scaled: %d/%d", // i->valid_rect().width(), i->valid_rect().height(), // scaledrect.width(), scaledrect.height()); - // sometimes scaledrect.width/height are off by one because - // of rounding errors. if the i is fully loaded, we - // make sure that we don't do unnecessary resizes during painting - TQSize s(scaledrect.size()); - if(i->valid_rect().size() == TQSize( intrinsicWidth(), intrinsicHeight() )) // fully loaded - s = TQSize(cWidth, cHeight); - if(kAbs(s.width() - cWidth) < 2) // rounding errors - s.setWidth(cWidth); - if(resizeCache.size() != s) - resizeCache.resize(s); + // sometimes scaledrect.width/height are off by one because + // of rounding errors. if the i is fully loaded, we + // make sure that we don't do unnecessary resizes during painting + TQSize s(scaledrect.size()); + if(i->valid_rect().size() == TQSize( intrinsicWidth(), intrinsicHeight() )) // fully loaded + s = TQSize(cWidth, cHeight); + if(kAbs(s.width() - cWidth) < 2) // rounding errors + s.setWidth(cWidth); + if(resizeCache.size() != s) + resizeCache.resize(s); + } paintInfo.p->drawPixmap( TQPoint( _tx + leftBorder + leftPad, _ty + topBorder + topPad), resizeCache, scaledrect );