IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> 为Evince-PDF添加灰色背景 -> 正文阅读

[系统运维]为Evince-PDF添加灰色背景

Ubuntu系统的默认PDF阅读软件

ubuntu操作系统自带了Evince-PDF阅读器,其优点是简单便捷,不过与其他常用的PDF阅读器相比,缺少很多定制化的选项,例如为避免眼睛疲劳的替换阅读界面背景颜色的功能。使用GNU/Linux操作系统的一个优势是,可以获取从内核至应用各个层面的软件源码,并加以修改后重新编译,可以方便地实现所需的新功能。笔者在该文章中记录了为Evince阅读器添加灰色背景的操作过程。作为对比,在添加该功能之前,笔者打开SystemTap教程文档的背景如下:
在这里插入图片描述
修改之后,在Evince的选项中使能Night Mode,阅读的背景会被修改为:
在这里插入图片描述

获取Evince的源代码

笔者使用的Ubuntu系统版本为20.04。获取Evince源码并能够编译,需要做一些准备工作。首先,将/etc/apt/sources.list中的# deb-src前面的#号删除:

sudo sed -e 's/# deb-src/deb-src/g' -i /etc/apt/sources.list
sudo apt-get update

之后,安装重新编译Evince所需的依赖和系统软件:

sudo apt-get install build-essential libtool autoconf make pbuilder
sudo apt-get build-dep evince

最后,下载Evince的源码包:

mkdir ~/evince-rebuild
cd ~/evince-rebuild
apt-get source evince

修改Evince源码以加载动态库

Evince有一个Night Mode的选项,可以将界面的亮度反转。其实现比较简单,是使用到了“差异算子”,将界面与最大亮度值求差:

/* libdocument/ev-document-misc.c */
void ev_document_misc_invert_surface (cairo_surface_t *surface) {    
    cairo_t *cr;    
    cr = cairo_create (surface);    
    /* white + DIFFERENCE -> invert */    
    cairo_set_operator (cr, CAIRO_OPERATOR_DIFFERENCE);    
    cairo_set_source_rgb (cr, 1., 1., 1.);    
    cairo_paint(cr);    
    cairo_destroy (cr);    
} 

笔者在实现该功能时,考虑到替换该函数的实现,就可以修改阅读界面的背景。不过,笔者没有在该函数中直接操作cairo_surface_t对象,而是通过加载动态库实现的,这样Evince只需被重新编译一次。笔者对该代码的修改如下:

diff --git a/libdocument/ev-document-misc.c b/libdocument/ev-document-misc.c
index 1d0acc5..da6c6ad 100644
--- a/libdocument/ev-document-misc.c
+++ b/libdocument/ev-document-misc.c
@@ -27,6 +27,9 @@
 
 #include "ev-document-misc.h"
 
+#include <sys/stat.h>
+#include <dlfcn.h>
+
 /* Returns a new GdkPixbuf that is suitable for placing in the thumbnail view.
  * It is four pixels wider and taller than the source.  If source_pixbuf is not
  * NULL, then it will fill the return pixbuf with the contents of
@@ -463,10 +466,32 @@ ev_document_misc_surface_rotate_and_scale (cairo_surface_t *surface,
        return new_surface;
 }
 
+typedef void (* ext_invert_func)(cairo_surface_t *);
+
 void
 ev_document_misc_invert_surface (cairo_surface_t *surface) {
        cairo_t *cr;
-
+       void * invhdl;
+       struct stat invst;
+       ext_invert_func extfunc;
+       const char * invlib = "/usr/lib/evince_invert.so";
+
+       if (stat(invlib, &invst) == -1)
+               goto next;
+       if (S_ISREG(invst.st_mode) == 0 || invst.st_size == 0)
+               goto next;
+       invhdl = dlopen(invlib, RTLD_NODELETE | RTLD_NOW);
+       if (invhdl == NULL)
+               goto next;
+       extfunc = (ext_invert_func) dlsym(invhdl, "external_evince_invert");
+       if (extfunc == NULL) {
+               dlclose(invhdl);
+               goto next;
+       }
+       extfunc(surface);
+       dlclose(invhdl);
+       return;
+next:
        cr = cairo_create (surface);
 
        /* white + DIFFERENCE -> invert */

重新编译Evince之前,需要提交以上修改:

dpkg-source --commit

dpkg-source --commit需要交互操作(可带一些命令行参数,非交互),过程如下:

~/evince-rebuild/evince-3.36.10$ dpkg-source --commit
dpkg-source: info: using patch list from debian/patches/series
dpkg-source: info: local changes detected, the modified files are:
 evince-3.36.10/libdocument/ev-document-misc.c
Enter the desired patch name: load-external-invert-library
dpkg-source: info: local changes have been recorded in a new patch: evince-3.36.10/debian/patches/load-external-invert-library

接下来,执行编译操作:

debuild -S # 请忽略 debsign相关的错误:debsign: gpg error occurred
debuild # 编译链接过程会出错

上面的debuild会链接出错,原因是我们加入了dlopen相关的调用:

./libdocument/ev-document-misc.c:483: undefined reference to `dlopen'
/usr/bin/ld: ./libdocument/ev-document-misc.c:486: undefined reference to `dlsym'
/usr/bin/ld: ./libdocument/ev-document-misc.c:492: undefined reference to `dlclose'
/usr/bin/ld: ./libdocument/ev-document-misc.c:488: undefined reference to `dlclose'
collect2: error: ld returned 1 exit status
make[4]: *** [Makefile:787: libevdocument3.la] Error 1

修改libdocument/Makefile的第787行,加入链接选项-ldl,之后执行make继续编译,将编译得到的动态库libevdocument3.so.4.0.0替换到系统路径下:

sed -e '787s/$/ -ldl/' -i libdocument/Makefile
make
sudo cp -v ./libdocument/.libs/libevdocument3.so.4.0.0 /usr/lib/x86_64-linux-gnu/

编写evince_invert.so动态库修改界面背景

笔者编写了一个简单的动态库,用于将Evince阅读界面的白色背景替换为灰色。笔者实现的灰度转换曲线如下:

在这里插入图片描述

其默认最大亮度为208,不过可以通过环境变量EVINCE_MAXVAL指定:

/* evince_invert.c
 * gcc -shared -o evince_invert.so evince_invert.c -Wall -O2 -fPIC -D_GNU_SOURCE -std=c99 -lcairo -lm
 */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <cairo/cairo.h>

extern void external_evince_invert(cairo_surface_t * surf);
extern void evince_invert_init(void) __attribute__((constructor));

#define EVINCE_GRAY_NUM 256
static unsigned char invert_map[EVINCE_GRAY_NUM];
static unsigned char rgb_map[EVINCE_GRAY_NUM][EVINCE_GRAY_NUM];

void external_evince_invert(cairo_surface_t * surf)
{
	int pixelsize;
	cairo_format_t cfmt;
	unsigned char * pdat;
	int iwid, ihei, istr, idx, jdx;

	cfmt = cairo_image_surface_get_format(surf);
	if (cfmt == CAIRO_FORMAT_ARGB32)
		pixelsize = 0x4;
	else if (cfmt == CAIRO_FORMAT_RGB24)
		pixelsize = 0x3;
	else {
		fprintf(stderr, "Unknown cairo surface format: %#x\n",
			(unsigned int) cfmt);
		fflush(stderr);
		return;
	}

	pdat = cairo_image_surface_get_data(surf);
	if (pdat == NULL)
		return;

	iwid = cairo_image_surface_get_width(surf);
	if (iwid <= 0)
		return;
	ihei = cairo_image_surface_get_height(surf);
	if (ihei <= 0)
		return;
	istr = cairo_image_surface_get_stride(surf);
	if (istr < (pixelsize * iwid))
		return;

	for (jdx = 0; jdx < ihei; ++jdx) {
		unsigned char * pd = pdat;
		for (idx = 0; idx < iwid; ++idx) {
			unsigned int newgray, gray;
			unsigned int red, blue, green;

			blue  = (unsigned int) pd[0];
			green = (unsigned int) pd[1];
			red   = (unsigned int) pd[2];

			/*
			 * Y = 0.2126 * R + 0.7152 * G + 0.0722 * B
			 *
			 * 0.2126 * 65536 = 13932.9536 
			 * 0.7152 * 65536 = 46871.3472
			 * 0.0722 * 65536 = 4731.6992
			 */
			gray  = (red * 13933 + green * 46871 + blue * 4732) >> 16;
#if 0 /* impossible, as 13933 + 46871 + 4732 = 65536 */
			if (gray >= 0x100)
				gray = 0xff;
#endif
			newgray = (unsigned int) invert_map[gray];
			if (newgray >= gray) {
				pd += pixelsize;
				continue;
			}

			/* important: `newgray is less than `gray: */
#if 1
			const unsigned char * rgbmap;
			rgbmap = rgb_map[gray];
			pd[0] = rgbmap[blue];
			pd[1] = rgbmap[green];
			pd[2] = rgbmap[red];
#else
			pd[0] = (unsigned char) (blue * newgray / gray);
			pd[1] = (unsigned char) (green * newgray / gray);
			pd[2] = (unsigned char) (red * newgray / gray);
#endif
			pd += pixelsize;
		}
		pdat += istr;
	}
}

#define EVINCE_MAXVAL_DEFAULT 208
void evince_invert_init(void)
{
	const char * evmax;
	int idx, jdx, maxval, step;
	const int half = EVINCE_GRAY_NUM / 2;

	maxval = EVINCE_MAXVAL_DEFAULT;
	evmax = getenv("EVINCE_MAXVAL");
	if (evmax != NULL) {
		maxval = (int) strtol(evmax, NULL, 0);
		if (maxval <= half || maxval >= EVINCE_GRAY_NUM)
			maxval = EVINCE_MAXVAL_DEFAULT;
	}
	for (idx = 0; idx < EVINCE_GRAY_NUM; ++idx)
		invert_map[idx] = (unsigned char) idx;

	/* NOTE: step is a positive integer: */
	step = maxval - half;
	for (idx = half; idx < EVINCE_GRAY_NUM; ++idx) {
		double dval = (double) half;
		double didx = (double) (idx - half);
		dval += step * log(1.0 + didx * (M_E - 1.0) / half);
		invert_map[idx] = (unsigned char) ((long) (dval + 0.5));
	}

	for (jdx = 0; jdx < EVINCE_GRAY_NUM; ++jdx) {
		int ngray;
		unsigned char * pgray;

		pgray = rgb_map[jdx];
		ngray = (int) invert_map[jdx];
		ngray &= 0xff;
		if (ngray >= jdx) {
			for (idx = 0; idx < EVINCE_GRAY_NUM; ++idx)
				*pgray++ = (unsigned char) idx;
			continue;
		}

		for (idx = 0; idx < EVINCE_GRAY_NUM; ++idx)
			*pgray++ = (unsigned char) (idx * ngray / jdx);
	}
}

按照注释的命令编译完成后,将evince_invert.so复制到/usr/lib/目录下,就可以通过EvinceNight Mode转换PDF的阅读界面为灰度背景了;该方法不会改变颜色的显示。不过该功能也有其缺陷,不建议反复多次设置Night Mode选项,否则界面会越来越暗。若有需要而不要重新编译Evince,可发邮件给笔者提供这两个动态库文件:xiaoqzye@qq.com。

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-03-21 21:35:42  更:2022-03-21 21:39:28 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/16 0:30:58-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码