diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -304,13 +304,15 @@ core/script/kjs_app.cpp core/script/kjs_console.cpp core/script/kjs_data.cpp + core/script/kjs_display.cpp core/script/kjs_document.cpp core/script/kjs_field.cpp core/script/kjs_fullscreen.cpp core/script/kjs_field.cpp core/script/kjs_spell.cpp core/script/kjs_util.cpp core/script/kjs_event.cpp + core/script/kjs_ocg.cpp ) target_link_libraries(okularcore PRIVATE KF5::JS KF5::JSApi) endif() diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt --- a/autotests/CMakeLists.txt +++ b/autotests/CMakeLists.txt @@ -17,6 +17,11 @@ TEST_NAME "visibilitytest" LINK_LIBRARIES Qt5::Widgets Qt5::Test okularcore ) + + ecm_add_test(kjsfunctionstest.cpp + TEST_NAME "kjsfunctionstest" + LINK_LIBRARIES Qt5::Widgets Qt5::Test okularcore + ) endif() ecm_add_test(documenttest.cpp diff --git a/autotests/data/kjsfunctionstest.pdf b/autotests/data/kjsfunctionstest.pdf new file mode 100644 index 0000000000000000000000000000000000000000..c580283a6eba847a7c5dd9331a4e2ef2f5fb821d GIT binary patch literal 83477 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 literal 0 Hc$@ * + * * + * 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 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include "../settings_core.h" +#include "core/document.h" +#include "core/scripter.h" +#include +#include +#include + +#include "../generators/poppler/config-okular-poppler.h" + +class KJSFunctionsTest: public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void testNumFields(); + void testNthFieldName(); + void testDisplay(); + void testSetClearInterval(); + void testSetClearTimeOut(); + void testGetOCGs(); + void testOCGSetState(); + void cleanupTestCase(); + +private: + Okular::Document *m_document; + QMap m_fields; +}; + +void KJSFunctionsTest::initTestCase() +{ + Okular::SettingsCore::instance( QStringLiteral("kjsfunctionstest") ); + m_document = new Okular::Document( nullptr ); + + const QString testFile = QStringLiteral( KDESRCDIR "data/kjsfunctionstest.pdf" ); + QMimeDatabase db; + const QMimeType mime = db.mimeTypeForFile( testFile ); + QCOMPARE( m_document->openDocument( testFile, QUrl(), mime), Okular::Document::OpenSuccess ); + + const Okular::Page* page = m_document->page( 0 ); + for ( Okular::FormField *ff: page->formFields() ) + { + m_fields.insert( ff->name(), ff ); + } +} + +void KJSFunctionsTest::testNumFields() +{ + QString result = m_document->executeScript( "Doc.numFields" ); + QCOMPARE( "31", result ); +} + +void KJSFunctionsTest::testNthFieldName() +{ + for(int i = 0;i < 21;++i) + { + QString result = m_document->executeScript( QString( "Doc.getNthFieldName(%1)" ).arg( i ) ); + QCOMPARE( result, QString( "0.%1" ).arg(i) ); + } +} + +void KJSFunctionsTest::testDisplay() +{ + m_document->executeScript( "field = Doc.getField(\"0.0\");field.display=display.hidden;\ + field = Doc.getField(\"0.10\");field.display=display.visible;" ); + QCOMPARE( false, m_fields["0.0"]->isVisible() ); + QCOMPARE( true, m_fields["0.10"]->isVisible() ); + + m_document->executeScript( "field = Doc.getField(\"0.10\");field.display=display.hidden;\ + field = Doc.getField(\"0.15\");field.display=display.visible;" ); + QCOMPARE( false, m_fields["0.10"]->isVisible() ); + QCOMPARE( true, m_fields["0.15"]->isVisible() ); + + m_document->executeScript( "field = Doc.getField(\"0.15\");field.display=display.hidden;\ + field = Doc.getField(\"0.20\");field.display=display.visible;" ); + QCOMPARE( false, m_fields["0.15"]->isVisible() ); + QCOMPARE( true, m_fields["0.20"]->isVisible() ); + + m_document->executeScript( "field = Doc.getField(\"0.20\");field.display=display.hidden;\ + field = Doc.getField(\"0.0\");field.display=display.visible;" ); + QCOMPARE( false, m_fields["0.20"]->isVisible() ); + QCOMPARE( true, m_fields["0.0"]->isVisible() ); +} + +void delay() +{ + QTime dieTime= QTime::currentTime().addSecs( 2 ); + while (QTime::currentTime() < dieTime) + QCoreApplication::processEvents(QEventLoop::AllEvents, 100); +} + +void KJSFunctionsTest::testSetClearInterval() +{ + QString result = m_document->executeScript( "obj = new Object();obj.idx=0;\ + obj.inc=function(){obj.idx = obj.idx + 1;};\ + intv = app.setInterval('obj.inc()', 450);obj.idx;" ); + QCOMPARE( "0", result ); + delay(); + + result = m_document->executeScript( "app.clearInterval(intv);obj.idx;"); + QCOMPARE( "4", result ); +} + +void KJSFunctionsTest::testSetClearTimeOut() +{ + QString result = m_document->executeScript( "intv = app.setTimeOut('obj.inc()', 1);obj.idx;" ); + QCOMPARE( "4", result ); + delay(); + + result = m_document->executeScript( "obj.idx;" ); + QCOMPARE( "5", result ); + + result = m_document->executeScript( "intv = app.setTimeOut('obj.inc()', 2000);obj.idx;" ); + QCOMPARE( "5", result ); + + result = m_document->executeScript( "app.clearTimeOut(intv);obj.idx;" ); + QCOMPARE( "5", result ); + delay(); + QCOMPARE( "5", result ); +} + +void KJSFunctionsTest::testGetOCGs() +{ + QAbstractItemModel *model = m_document->layersModel(); + + QString result = m_document->executeScript( "var ocg = this.getOCGs(this.pageNum);\ + ocgName = ocg[0].name;" ); + QCOMPARE( model->data( model->index( 0, 0 ), Qt::DisplayRole ).toString() , result ); + + result = m_document->executeScript( "ocgName = ocg[1].name;" ); + QCOMPARE( model->data( model->index( 1, 0 ), Qt::DisplayRole ).toString() , result ); + + result = m_document->executeScript( "ocgName = ocg[2].name;" ); + QCOMPARE( model->data( model->index( 2, 0 ), Qt::DisplayRole ).toString() , result ); + + result = m_document->executeScript( "ocgName = ocg[3].name;" ); + QCOMPARE( model->data( model->index( 3, 0 ), Qt::DisplayRole ).toString() , result ); + + result = m_document->executeScript( "ocgName = ocg[0].initState;" ); + QCOMPARE( model->data( model->index( 0, 0 ), Qt::CheckStateRole ).toBool() ? "true" : "false" + , result ); + + result = m_document->executeScript( "ocgName = ocg[1].initState;" ); + QCOMPARE( model->data( model->index( 1, 0 ), Qt::CheckStateRole ).toBool() ? "true" : "false" + , result ); + + result = m_document->executeScript( "ocgName = ocg[2].initState;" ); + QCOMPARE( model->data( model->index( 2, 0 ), Qt::CheckStateRole ).toBool() ? "true" : "false" + , result ); + + result = m_document->executeScript( "ocgName = ocg[3].initState;" ); + QCOMPARE( model->data( model->index( 3, 0 ), Qt::CheckStateRole ).toBool() ? "true" : "false" + , result ); +} + +void KJSFunctionsTest::testOCGSetState() +{ + QAbstractItemModel *model = m_document->layersModel(); + + QString result = m_document->executeScript( "ocgName = ocg[0].state;" ); + QCOMPARE( model->data( model->index( 0, 0 ), Qt::CheckStateRole ).toBool() ? "true" : "false", result ); + + result = m_document->executeScript( "ocg[0].state = false;ocgName = ocg[0].state;"); + QCOMPARE( model->data( model->index( 0, 0 ), Qt::CheckStateRole ).toBool() ? "true" : "false", result ); + + result = m_document->executeScript( "ocgName = ocg[1].state;" ); + QCOMPARE( model->data( model->index( 1, 0 ), Qt::CheckStateRole ).toBool() ? "true" : "false", result ); + + result = m_document->executeScript( "ocg[1].state = false;ocgName = ocg[1].state;"); + QCOMPARE( model->data( model->index( 1, 0 ), Qt::CheckStateRole ).toBool() ? "true" : "false", result ); + + result = m_document->executeScript( "ocgName = ocg[2].state;" ); + QCOMPARE( model->data( model->index( 2, 0 ), Qt::CheckStateRole ).toBool() ? "true" : "false", result ); + + result = m_document->executeScript( "ocg[2].state = true;ocgName = ocg[2].state;"); + QCOMPARE( model->data( model->index( 2, 0 ), Qt::CheckStateRole ).toBool() ? "true" : "false", result ); + + result = m_document->executeScript( "ocgName = ocg[3].state;" ); + QCOMPARE( model->data( model->index( 3, 0 ), Qt::CheckStateRole ).toBool() ? "true" : "false", result ); + + result = m_document->executeScript( "ocg[3].state = true;ocgName = ocg[3].state;"); + QCOMPARE( model->data( model->index( 3, 0 ), Qt::CheckStateRole ).toBool() ? "true" : "false", result ); +} + +void KJSFunctionsTest::cleanupTestCase() +{ + m_document->closeDocument(); + delete m_document; +} + + +QTEST_MAIN( KJSFunctionsTest ) +#include "kjsfunctionstest.moc" diff --git a/core/document.h b/core/document.h --- a/core/document.h +++ b/core/document.h @@ -1038,6 +1038,13 @@ */ QByteArray requestSignedRevisionData( const Okular::SignatureInfo &info ); + /** + * Executes a JavaScript script from the setInterval function. + * + * @since 1.7 + */ + QString executeScript( QString function ); + Q_SIGNALS: /** * This signal is emitted whenever the document is about to close. diff --git a/core/document.cpp b/core/document.cpp --- a/core/document.cpp +++ b/core/document.cpp @@ -5112,6 +5112,13 @@ return data; } +QString Document::executeScript( QString function ) +{ + if( !d->m_scripter ) + d->m_scripter = new Scripter( d ); + return d->m_scripter->execute( JavaScript, function ); +} + void DocumentPrivate::requestDone( PixmapRequest * req ) { if ( !req ) diff --git a/core/script/executor_kjs.cpp b/core/script/executor_kjs.cpp --- a/core/script/executor_kjs.cpp +++ b/core/script/executor_kjs.cpp @@ -24,10 +24,12 @@ #include "kjs_app_p.h" #include "kjs_console_p.h" #include "kjs_data_p.h" +#include "kjs_display_p.h" #include "kjs_document_p.h" #include "kjs_event_p.h" #include "kjs_field_p.h" #include "kjs_fullscreen_p.h" +#include "kjs_ocg_p.h" #include "kjs_spell_p.h" #include "kjs_util_p.h" @@ -64,15 +66,19 @@ JSFullscreen::initType( ctx ); JSConsole::initType( ctx ); JSData::initType( ctx ); + JSDisplay::initType( ctx ); JSDocument::initType( ctx ); JSEvent::initType( ctx ); JSField::initType( ctx ); + JSOCG::initType( ctx ); JSSpell::initType( ctx ); JSUtil::initType( ctx ); m_docObject.setProperty( ctx, QStringLiteral("app"), JSApp::object( ctx, m_doc ) ); m_docObject.setProperty( ctx, QStringLiteral("console"), JSConsole::object( ctx ) ); m_docObject.setProperty( ctx, QStringLiteral("Doc"), m_docObject ); + m_docObject.setProperty( ctx, QStringLiteral("display"), JSDisplay::object( ctx ) ); + m_docObject.setProperty( ctx, QStringLiteral("OCG"), JSOCG::object( ctx ) ); m_docObject.setProperty( ctx, QStringLiteral("spell"), JSSpell::object( ctx ) ); m_docObject.setProperty( ctx, QStringLiteral("util"), JSUtil::object( ctx ) ); } @@ -84,6 +90,9 @@ ExecutorKJS::~ExecutorKJS() { + JSField::clearCachedFields(); + JSApp::clearCachedFields(); + JSOCG::clearCachedFields(); delete d; } @@ -106,6 +115,9 @@ KJSResult result = d->m_interpreter->evaluate( QStringLiteral("okular.js"), 1, script, &d->m_docObject ); + + resultValue = new QString( result.value().toString( ctx ) ); + if ( result.isException() || ctx->hasException() ) { qCDebug(OkularCoreDebug) << "JS exception" << result.errorMessage(); @@ -120,5 +132,9 @@ << event->type() << "value:" << event->value(); } } - JSField::clearCachedFields(); } + +QString ExecutorKJS::getResult() +{ + return *resultValue; +} \ No newline at end of file diff --git a/core/script/executor_kjs_p.h b/core/script/executor_kjs_p.h --- a/core/script/executor_kjs_p.h +++ b/core/script/executor_kjs_p.h @@ -28,10 +28,12 @@ ExecutorKJS &operator=(const ExecutorKJS &) = delete; void execute( const QString &script, Event *event ); + QString getResult(); private: friend class ExecutorKJSPrivate; ExecutorKJSPrivate* d; + QString* resultValue; }; } diff --git a/core/script/kjs_app.cpp b/core/script/kjs_app.cpp --- a/core/script/kjs_app.cpp +++ b/core/script/kjs_app.cpp @@ -21,11 +21,14 @@ #include #include "../document_p.h" +#include "../scripter.h" #include "kjs_fullscreen_p.h" using namespace Okular; static KJSPrototype *g_appProto; +typedef QHash< int, QTimer * > TimerCache; +Q_GLOBAL_STATIC( TimerCache, g_timerCache ) // the acrobat version we fake static const double fake_acroversion = 8.00; @@ -199,6 +202,77 @@ return KJSUndefined(); } +// app.setInterval() +static KJSObject appSetInterval( KJSContext *ctx, void *object, + const KJSArguments &arguments ) +{ + DocumentPrivate *doc = reinterpret_cast< DocumentPrivate * >( object ); + QString function = arguments.at( 0 ).toString( ctx ) + ';'; + int interval = arguments.at( 1 ).toInt32( ctx ); + + QTimer *timer = new QTimer(); + + QObject::connect( timer, &QTimer::timeout, [=](){ doc->m_parent->executeScript( function ); } ); + + timer->start( interval ); + + return JSApp::wrapTimer( ctx, timer ); +} + +// app.clearInterval() +static KJSObject appClearInterval( KJSContext *ctx, void *object, + const KJSArguments &arguments ) +{ + KJSObject timerObject = arguments.at( 0 ); + int timerId = timerObject.property( ctx, QStringLiteral( "timerID" ).toLatin1().toBase64() ).toInt32( ctx ); + QTimer *timer = g_timerCache->value( timerId ); + if( timer != nullptr ) + { + timer->stop(); + g_timerCache->remove( timerId ); + delete timer; + } + + return KJSUndefined(); +} + +// app.setTimeOut() +static KJSObject appSetTimeOut( KJSContext *ctx, void *object, + const KJSArguments &arguments ) +{ + DocumentPrivate *doc = reinterpret_cast< DocumentPrivate * >( object ); + QString function = arguments.at( 0 ).toString( ctx ) + ';'; + int interval = arguments.at( 1 ).toInt32( ctx ); + + QTimer *timer = new QTimer(); + timer->setSingleShot( true ); + + QObject::connect( timer, &QTimer::timeout, [=](){ doc->m_parent->executeScript( function ); } ); + + timer->start( interval ); + + return JSApp::wrapTimer( ctx, timer ); +} + +// app.clearTimeOut() +static KJSObject appClearTimeOut( KJSContext *ctx, void *object, + const KJSArguments &arguments ) +{ + KJSObject timerObject = arguments.at( 0 ); + int timerId = timerObject.property( ctx, QStringLiteral( "timerID" ).toLatin1().toBase64() ).toInt32( ctx ); + QTimer *timer = g_timerCache->value( timerId ); + + if( timer != nullptr ) + { + timer->stop(); + g_timerCache->remove( timerId ); + delete timer; + } + + return KJSUndefined(); +} + + void JSApp::initType( KJSContext *ctx ) { static bool initialized = false; @@ -223,9 +297,40 @@ g_appProto->defineFunction( ctx, QStringLiteral("getNthPlugInName"), appGetNthPlugInName ); g_appProto->defineFunction( ctx, QStringLiteral("goBack"), appGoBack ); g_appProto->defineFunction( ctx, QStringLiteral("goForward"), appGoForward ); + g_appProto->defineFunction( ctx, QStringLiteral("setInterval"), appSetInterval ); + g_appProto->defineFunction( ctx, QStringLiteral("clearInterval"), appClearInterval ); + g_appProto->defineFunction( ctx, QStringLiteral("setTimeOut"), appSetTimeOut ); + g_appProto->defineFunction( ctx, QStringLiteral("clearTimeOut"), appClearTimeOut ); } KJSObject JSApp::object( KJSContext *ctx, DocumentPrivate *doc ) { return g_appProto->constructObject( ctx, doc ); } + +KJSObject JSApp::wrapTimer( KJSContext *ctx, QTimer *timer) +{ + KJSObject timerObject = g_appProto->constructObject( ctx, timer ); + timerObject.setProperty( ctx, QStringLiteral("timerID").toLatin1().toBase64(), timer->timerId() ); + + g_timerCache->insert( timer->timerId(), timer ); + + return timerObject; +} + +void JSApp::clearCachedFields() +{ + if ( g_timerCache ) + { + for( auto it = g_timerCache->begin(); it != g_timerCache->end(); ++it ) + { + QTimer *timer = it.value(); + + if(timer == nullptr) + continue; + + delete timer; + } + g_timerCache->clear(); + } +} \ No newline at end of file diff --git a/core/script/kjs_app_p.h b/core/script/kjs_app_p.h --- a/core/script/kjs_app_p.h +++ b/core/script/kjs_app_p.h @@ -14,6 +14,8 @@ class KJSContext; class KJSObject; +#include + namespace Okular { class DocumentPrivate; @@ -23,8 +25,10 @@ public: static void initType( KJSContext *ctx ); static KJSObject object( KJSContext *ctx, DocumentPrivate *doc ); + static KJSObject wrapTimer( KJSContext *ctx, QTimer *timer ); + static void clearCachedFields(); }; } -#endif +#endif \ No newline at end of file diff --git a/core/script/kjs_display.cpp b/core/script/kjs_display.cpp new file mode 100644 --- /dev/null +++ b/core/script/kjs_display.cpp @@ -0,0 +1,49 @@ +/*************************************************************************** + * Copyright (C) 2019 by João Netto * + * * + * 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 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ + +#include "kjs_display_p.h" + +#include +#include + +#include + +#include + +using namespace Okular; + +std::unique_ptr < KJSPrototype > g_displayProto; + +// display.hidden +static KJSObject displayGetHidden( KJSContext *, void * ) +{ + return KJSBoolean( false ); +} + +// display.visible +static KJSObject displayGetVisible( KJSContext *, void * ) +{ + return KJSBoolean( true ); +} + +void JSDisplay::initType( KJSContext *ctx ) +{ + if ( g_displayProto ) + return; + + g_displayProto.reset(new KJSPrototype); + + g_displayProto->defineProperty( ctx, QStringLiteral("hidden"), displayGetHidden ); + g_displayProto->defineProperty( ctx, QStringLiteral("visible"), displayGetVisible ); +} + +KJSObject JSDisplay::object( KJSContext *ctx ) +{ + return g_displayProto->constructObject( ctx, nullptr ); +} \ No newline at end of file diff --git a/core/script/kjs_app_p.h b/core/script/kjs_display_p.h copy from core/script/kjs_app_p.h copy to core/script/kjs_display_p.h --- a/core/script/kjs_app_p.h +++ b/core/script/kjs_display_p.h @@ -1,30 +1,27 @@ /*************************************************************************** - * Copyright (C) 2008 by Pino Toscano * - * Copyright (C) 2008 by Harri Porten * + * Copyright (C) 2019 by João Netto * * * * 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 2 of the License, or * * (at your option) any later version. * ***************************************************************************/ -#ifndef OKULAR_SCRIPT_KJS_APP_P_H -#define OKULAR_SCRIPT_KJS_APP_P_H +#ifndef OKULAR_SCRIPT_KJS_DISPLAY_P_H +#define OKULAR_SCRIPT_KJS_DISPLAY_P_H class KJSContext; class KJSObject; namespace Okular { -class DocumentPrivate; - -class JSApp +class JSDisplay { public: static void initType( KJSContext *ctx ); - static KJSObject object( KJSContext *ctx, DocumentPrivate *doc ); + static KJSObject object( KJSContext *ctx ); }; } -#endif +#endif \ No newline at end of file diff --git a/core/script/kjs_document.cpp b/core/script/kjs_document.cpp --- a/core/script/kjs_document.cpp +++ b/core/script/kjs_document.cpp @@ -24,6 +24,7 @@ #include "../form.h" #include "kjs_data_p.h" #include "kjs_field_p.h" +#include "kjs_ocg_p.h" using namespace Okular; @@ -129,6 +130,21 @@ return KJSBoolean( !isShell ); } +// Document.numFields +static KJSObject docGetNumFields( KJSContext *, void *object ) +{ + DocumentPrivate *doc = reinterpret_cast< DocumentPrivate* >( object ); + + unsigned int numFields = 0; + + for ( const Page * pIt : qAsConst(doc->m_pagesVector) ) + { + numFields += pIt->formFields().size(); + } + + return KJSNumber( numFields ); +} + static KJSObject docGetInfo( KJSContext *ctx, void *object ) { @@ -248,6 +264,56 @@ return KJSUndefined(); } +// Document.getNthFieldName +static KJSObject docGetNthFieldName( KJSContext *ctx, void *object, + const KJSArguments &arguments ) +{ + DocumentPrivate *doc = reinterpret_cast< DocumentPrivate* >( object ); + + int numField = arguments.at( 0 ).toInt32( ctx ); + + for ( const Page * pIt : qAsConst(doc->m_pagesVector) ) + { + const QLinkedList< Okular::FormField * > pageFields = pIt->formFields(); + + if(numField < pageFields.size()) + { + auto ffIt = pageFields.begin(); + ffIt += numField; + + return KJSString( (*ffIt)->name() ); + } + + numField -= pageFields.size(); + } + + return KJSUndefined(); +} + +static KJSObject docGetOCGs( KJSContext *ctx, void *object, + const KJSArguments &arguments ) +{ + DocumentPrivate *doc = reinterpret_cast< DocumentPrivate* >( object ); + + QAbstractItemModel * model = doc->m_parent->layersModel(); + + KJSArray array( ctx, model->rowCount() ); + + for(int i = 0;i < model->rowCount();++i){ + for(int j = 0;j < model->columnCount();++j){ + QModelIndex index = model->index( i, j ); + + KJSObject item = JSOCG::wrapOCGObject( ctx, model, i, j ); + item.setProperty( ctx, QStringLiteral("name"), model->data( index , Qt::DisplayRole ).toString() ); + item.setProperty( ctx, QStringLiteral("initState"), model->data( index , Qt::CheckStateRole ).toBool() ); + + array.setProperty( ctx, QString::number( i ), item ); + } + } + + return array; +} + void JSDocument::initType( KJSContext *ctx ) { assert( g_docProto ); @@ -266,6 +332,7 @@ g_docProto->defineProperty( ctx, QStringLiteral("permStatusReady"), docGetPermStatusReady ); g_docProto->defineProperty( ctx, QStringLiteral("dataObjects"), docGetDataObjects ); g_docProto->defineProperty( ctx, QStringLiteral("external"), docGetExternal ); + g_docProto->defineProperty( ctx, QStringLiteral("numFields"), docGetNumFields ); // info properties g_docProto->defineProperty( ctx, QStringLiteral("info"), docGetInfo ); @@ -281,11 +348,13 @@ g_docProto->defineFunction( ctx, QStringLiteral("getPageRotation"), docGetPageRotation ); g_docProto->defineFunction( ctx, QStringLiteral("gotoNamedDest"), docGotoNamedDest ); g_docProto->defineFunction( ctx, QStringLiteral("syncAnnotScan"), docSyncAnnotScan ); + g_docProto->defineFunction( ctx, QStringLiteral("getNthFieldName"), docGetNthFieldName ); + g_docProto->defineFunction( ctx, QStringLiteral("getOCGs"), docGetOCGs ); } KJSGlobalObject JSDocument::wrapDocument( DocumentPrivate *doc ) { if ( !g_docProto ) g_docProto = new KJSPrototype(); return g_docProto->constructGlobalObject( doc ); -} +} \ No newline at end of file diff --git a/core/script/kjs_field.cpp b/core/script/kjs_field.cpp --- a/core/script/kjs_field.cpp +++ b/core/script/kjs_field.cpp @@ -17,6 +17,7 @@ #include #include +#include #include "../debug_p.h" #include "../document_p.h" @@ -30,6 +31,10 @@ typedef QHash< FormField *, Page * > FormCache; Q_GLOBAL_STATIC( FormCache, g_fieldCache ) +typedef QHash< QString, FormField * > ButtonCache; +Q_GLOBAL_STATIC( ButtonCache, g_buttonCache ) +typedef QHash< QString, QString > FrameCache; +Q_GLOBAL_STATIC( FrameCache, g_frameCache ) // Helper for modified fields @@ -224,6 +229,67 @@ updateField( field ); } +// Field.display (getter) +static KJSObject fieldGetDisplay( KJSContext *, void *object ) +{ + const FormField *field = reinterpret_cast< FormField * >( object ); + return KJSBoolean( field->isVisible() ); +} + +// Field.display (setter) +static void fieldSetDisplay( KJSContext *context, void *object, KJSObject value ) +{ + FormField *field = reinterpret_cast< FormField * >( object ); + bool b = value.toBoolean( context ); + field->setVisible( b ); + + updateField( field ); +} + +// Instead of getting the Icon, we pick the field instead to draw it. +static KJSObject fieldButtonGetIcon( KJSContext *ctx, void *object, + const KJSArguments & ) +{ + FormField *field = reinterpret_cast< FormField * >( object ); + + KJSObject fieldObject; + fieldObject.setProperty( ctx, QStringLiteral("name").toLatin1().toBase64(), field->name() ); + g_buttonCache->insert( field->name(), field ); + + return fieldObject; +} + +/* +* We don't have acess to the Icon Object because it's not given by Poppler +* Instead, we turn the current object invisible and draw the object +* that represents the new icon. +*/ +static KJSObject fieldButtonSetIcon( KJSContext *ctx, void *object, + const KJSArguments &arguments ) +{ + FormField *field = reinterpret_cast< FormField * >( object ); + field->setVisible( false ); + + QString lastField = g_frameCache->value( field->name() ); + QString fieldName = arguments.at( 0 ).property( ctx, QStringLiteral("name").toLatin1().toBase64() ).toString( ctx ); + + g_frameCache->insert( field->name(), fieldName ); + + if( !lastField.isEmpty() ) + field = g_buttonCache->value( lastField ); + + if( field ) + field->setVisible( false ); + + field = g_buttonCache->value( fieldName ); + + field->setVisible( true ); + + updateField( field ); + + return KJSUndefined(); +} + void JSField::initType( KJSContext *ctx ) { static bool initialized = false; @@ -241,6 +307,10 @@ g_fieldProto->defineProperty( ctx, QStringLiteral("type"), fieldGetType ); g_fieldProto->defineProperty( ctx, QStringLiteral("value"), fieldGetValue, fieldSetValue ); g_fieldProto->defineProperty( ctx, QStringLiteral("hidden"), fieldGetHidden, fieldSetHidden ); + g_fieldProto->defineProperty( ctx, QStringLiteral("display"), fieldGetDisplay, fieldSetDisplay ); + + g_fieldProto->defineFunction( ctx, QStringLiteral("buttonGetIcon"), fieldButtonGetIcon ); + g_fieldProto->defineFunction( ctx, QStringLiteral("buttonSetIcon"), fieldButtonSetIcon ); } KJSObject JSField::wrapField( KJSContext *ctx, FormField *field, Page *page ) @@ -255,7 +325,11 @@ void JSField::clearCachedFields() { if ( g_fieldCache.exists() ) - { g_fieldCache->clear(); - } -} + + if( g_buttonCache.exists() ) + g_buttonCache->clear(); + + if( g_frameCache.exists() ) + g_frameCache->clear(); +} \ No newline at end of file diff --git a/core/script/kjs_ocg.cpp b/core/script/kjs_ocg.cpp new file mode 100644 --- /dev/null +++ b/core/script/kjs_ocg.cpp @@ -0,0 +1,86 @@ +/*************************************************************************** + * Copyright (C) 2019 by João Netto * + * * + * 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 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ + +#include "kjs_ocg_p.h" + +#include +#include +#include + +#include +#include +#include +#include + +#include + +using namespace Okular; + +std::unique_ptr < KJSPrototype > g_OCGProto; + +typedef QHash< QPair< int, int > *, QAbstractItemModel* > OCGCache; +Q_GLOBAL_STATIC( OCGCache, g_OCGCache ) + +// OCG.state (getter) +static KJSObject OCGGetState( KJSContext *, void *object ) +{ + QPair< int, int > *pair = reinterpret_cast< QPair< int, int >* > ( object ); + QAbstractItemModel *model = g_OCGCache->value( pair ); + + QModelIndex index = model->index( pair->first, pair->second ); + + bool state = model->data( index, Qt::CheckStateRole ).toBool(); + + return KJSBoolean( state ); +} + +// OCG.state (setter) +static void OCGSetState( KJSContext* ctx, void* object, + KJSObject value ) +{ + QPair< int, int > *pair = reinterpret_cast< QPair< int, int >* > ( object ); + QAbstractItemModel *model = g_OCGCache->value( pair ); + + QModelIndex index = model->index( pair->first, pair->second ); + + bool state = value.toBoolean( ctx ); + + model->setData( index, QVariant( state ? Qt::Checked : Qt::Unchecked ), Qt::CheckStateRole ); +} + + +void JSOCG::initType( KJSContext *ctx ) +{ + if ( g_OCGProto ) + return; + + g_OCGProto.reset(new KJSPrototype); + + g_OCGProto->defineProperty( ctx, QStringLiteral("state"), OCGGetState, OCGSetState ); +} + +KJSObject JSOCG::object( KJSContext *ctx ) +{ + return g_OCGProto->constructObject( ctx, nullptr ); +} + +KJSObject JSOCG::wrapOCGObject( KJSContext *ctx, QAbstractItemModel *model, int i, int j ) +{ + QPair< int, int > *pair = new QPair< int ,int >( i, j ); + g_OCGCache->insert( pair, model ); + return g_OCGProto->constructObject( ctx, pair ); +} + +void JSOCG::clearCachedFields() +{ + if ( g_OCGCache.exists() ) + { + g_OCGCache->clear(); + } +} \ No newline at end of file diff --git a/core/script/kjs_app_p.h b/core/script/kjs_ocg_p.h copy from core/script/kjs_app_p.h copy to core/script/kjs_ocg_p.h --- a/core/script/kjs_app_p.h +++ b/core/script/kjs_ocg_p.h @@ -1,30 +1,30 @@ /*************************************************************************** - * Copyright (C) 2008 by Pino Toscano * - * Copyright (C) 2008 by Harri Porten * + * Copyright (C) 2019 by João Netto * * * * 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 2 of the License, or * * (at your option) any later version. * ***************************************************************************/ -#ifndef OKULAR_SCRIPT_KJS_APP_P_H -#define OKULAR_SCRIPT_KJS_APP_P_H +#ifndef OKULAR_SCRIPT_KJS_OCG_P_H +#define OKULAR_SCRIPT_KJS_OCG_P_H class KJSContext; class KJSObject; +class QAbstractItemModel; namespace Okular { -class DocumentPrivate; - -class JSApp +class JSOCG { public: static void initType( KJSContext *ctx ); - static KJSObject object( KJSContext *ctx, DocumentPrivate *doc ); + static KJSObject object( KJSContext *ctx ); + static KJSObject wrapOCGObject( KJSContext *ctx, QAbstractItemModel *model, int i, int j ); + static void clearCachedFields(); }; } -#endif +#endif \ No newline at end of file diff --git a/core/scripter.cpp b/core/scripter.cpp --- a/core/scripter.cpp +++ b/core/scripter.cpp @@ -79,7 +79,8 @@ d->m_kjs.reset(new ExecutorKJS( d->m_doc )); } d->m_kjs->execute( builtInScript + script, d->m_event ); - break; + + return d->m_kjs->getResult(); } #endif return QString(); diff --git a/ui/formwidgets.cpp b/ui/formwidgets.cpp --- a/ui/formwidgets.cpp +++ b/ui/formwidgets.cpp @@ -375,7 +375,7 @@ { return; } - setVisibility( form->isVisible() /*&& !form->isReadOnly()*/ ); + setVisibility( form->isVisible() && m_controller->shouldFormWidgetBeShown( form ) ); m_widget->setEnabled( !form->isReadOnly() ); } @@ -385,6 +385,12 @@ : QPushButton( parent ), FormWidgetIface( this, button ) { setText( button->caption() ); + + if( button->caption().isEmpty() ) + { + setFlat( true ); + } + setVisible( button->isVisible() ); setCursor( Qt::ArrowCursor ); } diff --git a/ui/pageview.cpp b/ui/pageview.cpp --- a/ui/pageview.cpp +++ b/ui/pageview.cpp @@ -1473,6 +1473,18 @@ Q_FOREACH ( VideoWidget *videoWidget, item->videoWidgets() ) videoWidget->pageLeft(); } + + // On close, run the widget scripts, needed for running animated PDF + const Okular::Page *page = d->document->page( previous ); + foreach( Okular::Annotation *annotation, page->annotations() ) + { + if ( annotation->subType() == Okular::Annotation::AWidget ) + { + Okular::WidgetAnnotation *widgetAnnotation = static_cast( annotation ); + d->document->processAction( widgetAnnotation->additionalAction( Okular::Annotation::PageClosing ) ); + } + } + } if ( current != -1 ) @@ -1487,6 +1499,17 @@ // update zoom text and factor if in a ZoomFit/* zoom mode if ( d->zoomMode != ZoomFixed ) updateZoomText(); + + // Opening any widget scripts, needed for running animated PDF + const Okular::Page *page = d->document->page( current ); + foreach( Okular::Annotation *annotation, page->annotations() ) + { + if ( annotation->subType() == Okular::Annotation::AWidget ) + { + Okular::WidgetAnnotation *widgetAnnotation = static_cast( annotation ); + d->document->processAction( widgetAnnotation->additionalAction( Okular::Annotation::PageOpening ) ); + } + } } } @@ -2571,7 +2594,7 @@ { // handle click over a image } -/* Enrico and me have decided this is not worth the trouble it generates +/* Enrico and me have decided this is not worth the trouble it generates else { // if not on a rect, the click selects the page @@ -5580,4 +5603,4 @@ #include "moc_pageview.cpp" -/* kate: replace-tabs on; indent-width 4; */ +/* kate: replace-tabs on; indent-width 4; */ \ No newline at end of file