Improve Okular For Android
Okular has an Android version, but it has some issues. PDFs without embedded fonts don’t display properly due to the lack of an Android-specific font API in the Poppler backend. Icons also do not render, due to problems in KDE Frameworks or the Kirigami UI framework.
This project aims to implement an Android-specific font API in the Poppler backend, as well as solving the issue of icon rendering in Okular’s Android version.
Project Mentor: Albert Astals Cid <[email protected]>
Project Size: 175 Hours (medium)
Links to Blogs and other writing
I will be writing about my progress on my blog. It can be found at: https://shivoditg.hashnode.dev
The blog posts in chronological order:
- First blog: First blog for GSoC '23
- Second blog detailing work done during the first coding period: GSoC '23: Summary of work done over first coding period
- Third and final blog, wrapping up GSoC'23: GSoC '23: Wrapping Up
- Main merge request for font-matching functionality in Poppler
- Fix icon rendering in Okular on Android
- Package the Okular icon when building for Android
- Replace Kirigami.AboutPage with MobileForm.AboutPage for mobile build
- Port to the newer FormCard API from the MobileForm API
- Cherry picked commit from Qt6 in order to fix black screen issue in Okular Android
Week 1 and 2
- Work begins on font-matching functionality for Android version of Poppler
- Understood what is to be done
- Implement GlobalParams::findSystemFontFile() and GlobalParams::findBase14FontFile() for Android version of Poppler
- Set up development environment
kde-androiddocker image for building Poppler
- Use CMake script incliuded in the android build for Poppler project CI
- Use invent-registry.kde.org/sysadmin/ci-images/android-qt515 docker image for building Okular
- To test changes in Poppler:
- First make necessary changes to poppler
- Build poppler in kde-android docker container
- replace the poppler library in the android-qt515 docker container
- Build okular again so that it gets built with the new poppler library
- Package Okular and install it on android device/emulator to test changes
- Added conditional compilation for Android version of Poppler, to get the correct libraries and set the correct environment variables
- Environment variables included pre-processor variables which would compile the Android specific implementation of Poppler's font-matching backend when building for Android.
- THe android libraries included the
androidlibrary and the
loglibrary provided by the Android NDK.
- Forked Poppler's repository hosted at gitlab.freedesktop.org after acquiring the relevant permissions
- Successfully implemented a crude version of GlobalParams::findSystemFontFile() using AFontMatcher functionality which only returns sans-serif fonts.
- Accomplished this by adding the
androidlibrary in the CMake
- Still needs implementation for:
- Fetching the correct style of the required font (bold, italic, bold + italic, etc.)
- Fetching the font having the correct font weight
- Getting other generic font styles such as serif, fixed-width
- Accomplished this by adding the
- Since AFontMatcher uses the pre-installed fonts found on Android, and only matches fonts based on the generic font-family (sans-serif, serif, fixed-width, fantasy, etc.). It was inadequate for matching the base 14 fonts.
- The base 14 fonts are a set of 14 fonts that are expected to be supported by every PDF reader. These include:
- Times (v3) (in regular, italic, bold, and bold italic)
- Courier (in regular, oblique, bold and bold oblique)
- Helvetica (v3) (in regular, oblique, bold and bold oblique)
- Zapf Dingbats
- However, these fonts have proprietary licenses, so instead we use some substitute fonts that closely match the base-14 fonts in appearance. In Poppler's case, these fonts are:
- However, Android devices do not come with these base-14 fonts installed by default, so we must provide our own.
- Luckily, OKular already packages these fonts inside the assets folder of its apk. To access and use these files, we first transfer them to the internal storage of the app (Okular in this case) and then set the path where the font files can be found, so that Poppler can load and use the font files.
- However, as a sanity test, I first placed the font files in a directory inside the /data/local/tmp folder of android, which is read/write and has execute permissions, and specified the path in GlobalParams.cc myself. It worked, so I got to work on implementing a mechanism for copying the font files and setting the font file path.
- I implemented a mechanism to copy all the font files contained in the
assets/share/fontsfolder of the Okular APK.
- This code uses the QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) functionality of Qt to quickly find the internal storage directory of the app.
- Within this folder, another folder called fonts is created, and the font files in the
assets/share/fontsfolder of the APK are copied over to the fonts folder one-by-one
- This code was originally written in the mobile/app/main.cpp file of Okular, however I could not call GlobalParams functions, since they are private to Poppler. This presented a blocker to setting the path for the font directory in GlobalParams.
- I later shifted the code to the qt backend of Poppler, as I could access QStandardPaths and other Qt functions, while also being able to call GlobalParams functions. More specifically, the code was added in the DocumentData::init() method.
- I then created a static method in GlobalParams,
GlobalParams::setFontDir(), which takes a file path as a string input, and sets it as the path where font files should be searched.
- The midterm deadline was fast approaching, so I got to work on finalizing my work and created a Merge Request on Poppler's gitlab.
- I cleaned up the code by removing several print debugging statements, organising the code properly, and adding comments to detail what a piece of code does.
- I then created the MR, and fixed any issues that were found in my code by my mentor, and also fixed the code so that all the CI jobs for Poppler would pass. For example, I forgot to add some conditional compilation statements, which resulted in every CI besides clang-format and android failing to build. I also fixed some memory leak issues with AFontMatcher and AFont objects using unique_ptrs (thanks to Sune Vuorela for suggesting unique_ptrs!).
- I also wrote my blog entry detailing the work done in the first coding period: Link to blog
Week 7 - 9
- This week marked the start of the second coding period.
- I began work on the second problem that had to be fixed - Okular's icons not being rendered.
- During this time, I explored the source code of other KDE Android apps to figure out what the problem might be.
- I also disassembled the APK, and decompiled the .qrc file inside it. I discovered that they were getting packaged properly, and specifying their full path using the qrc://path/to/icon path worked properly. This meant that the issue was with Kirigami not finding the proper icons.
- I had also requested a deadline as I thought that debugging this issue would take weeks of breaking down and building up Okular piece-by-piece to find the troublesome part of the codebase.
- But before taking Okular apart piece-by-piece, I sought help from the KDE Android matrix room to diagnose the issue.
- Thanks to their help it was discovered that Okular was packaging KIconThemes as a dependency and thus was automatically setting Okular's fallback theme to "breeze". This interfered with Kirigami's icon-finding logic, and it was not able to find the icons properly. This is the line of code that was causing this issue.
- I placed a conditional compilation guard to prevent KIconThemes from doing this when built for Android.
- Relevant merge request: frameworks/kiconthemes!101
Week 10 - 11
- I added the Okular icon to be packaged in the apk, so that it would display in the global drawer and Okular's about page.
- Relevant merge request: graphics/okular!801
- During this time, I also noticed that Craft would not package any new icons unless I used the command:
craft --compile --install --qmerge okular
instead of just
craft --compile okular
Due to this, I wasted some time thinking that Kirigami's icon packaging was having some issues, when in fact it was working properly and the icons just needed an extra step to be packaged using Craft.
- I relayed this information to the KDE Android matrix channel, and they advised me to edit the wiki page for Craft to reflect this statement.
- I also ported Okular to use MobileForm.AboutPage from kirigami-addons over the regular Kirigami.AboutPage. Reason being, the MobileForm.AboutPage looked better and more modern.
- Relevant merge request: https://invent.kde.org/graphics/okular/-/merge_requests/802
- Later on, the kirigami-addons developers decided to port MobileForm over to FormCard, as explained by KDE Developer Carl Schwan in this blog post: https://carlschwan.eu/1/01/01/kirigami-addons-0.11.0/#mobileform-is-now-formcard-0110
- They (the kirigami addons developers) advised apps using MobileForm to port over to FormCard, and created a task to track the porting progress: libraries/kirigami-addons#10.
- So I ported to FormCard from MobileForm, and created a merge request for Okular: graphics/okular!806.
- While running Okular on my android device, I noticed that opening a PDF would result in a black screen that persisted until Okular would close or crash by itself.
- After some testing, it seemed to be the result of a regression in the qtbase component of Qt 5.15.9. The regressive commit ID is - 513fcf0d2ad3328830dbf73dc2a55ad1487393c0.
- It was fixed in a later commit in the Qt6 branch of qtbase - ac984bd8768b3d7e6439e0ffd98fd8b53e16b922
- To apply this fix to Okular, my mentor advised me to backport this patch to the kde/5.15 branch of qtbase, using the process detailed in this KDE wiki page: https://community.kde.org/Qt5PatchCollection#How_do_I_get_a_patch_merged
- I followed the directions and cherry-picked the commit ac984bd8768b3d7e6439e0ffd98fd8b53e16b922 to the kde/5.15 branch and opened a merge request at the KDE Invent qtbase repository.
- Relevant merge request : qt/qt/qtbase!278
- All the tasks for my GSoC project were completed at this point.
- So I started working on creating my final report and a blog post wrapping up my GSoC journey.