简体   繁体   English

如何使用Webdriver(Java)在phantomjs上禁用字体抗锯齿功能?

[英]How can font anti aliasing be disabled on phantomjs with webdriver (Java)?

I am trying to write Selenium tests that check layout issues. 我正在尝试编写Selenium测试来检查布局问题。 For this I am using Selenium Webdriver on the Java side and phantomjs as the "browser". 为此,我在Java端使用Selenium Webdriver,并将phantomjs用作“浏览器”。 I want to use phantomjs because it is able to make screenshots of the actually rendered components. 我想使用phantomjs,因为它能够制作实际渲染的组件的屏幕截图。

By default phantomjs renders text using anti aliasing, and that makes it hard to scan texts (to find text baselines and to do simple OCR). 默认情况下,phantomjs使用反锯齿渲染文本,这使得扫描文本(查找文本基线和执行简单的OCR)变得困难。

How can I tell phantomJS to not use anti aliasing? 如何告诉phantomJS不使用抗锯齿?

I used the following dirty trick to disable phantomjs anti aliasing on Linux. 我使用以下肮脏的技巧在Linux上禁用phantomjs反别名。 PhantomJS is built using fontconfig, and this library looks for a file "fonts.conf" in several locations: see https://www.freedesktop.org/software/fontconfig/fontconfig-user.html . PhantomJS使用fontconfig构建,该库在多个位置查找文件“ fonts.conf”:请参阅https://www.freedesktop.org/software/fontconfig/fontconfig-user.html

By creating the following fonts.conf we can disable anti aliasing for fontconfig: 通过创建以下fonts.conf,我们可以为fontconfig禁用反别名:

<match target="font">
  <edit mode="assign" name="antialias">
    <bool>false</bool>
  </edit>
</match>

One of the locations is defined by an environment variable, according to the specs: $XDG_CONFIG_HOME/fontconfig/fonts.conf. 根据规范,位置之一由环境变量定义:$ XDG_CONFIG_HOME / fontconfig / fonts.conf。 So by creating a temp file like /tmp/phantomjs-config/fontconfig/fonts.conf with the above content, and then setting XDG_CONFIG_HOME to /tmp/phantomjs-config we tell fontconfig to read that file. 因此,通过使用上述内容创建一个类似于/tmp/phantomjs-config/fontconfig/fonts.conf的临时文件,然后将XDG_CONFIG_HOME设置为/ tmp / phantomjs-config,我们告诉fontconfig读取该文件。

There is one problem though: the PhantomJSDriver class, by default, does not allow environment variables to be set. 但是,存在一个问题:默认情况下,PhantomJSDriver类不允许设置环境变量。 Which is sad, because the underlying worker code, PhantomWebDriverService, does allow this. 令人遗憾的是,因为底层的工作程序代码PhantomWebDriverService确实允许这样做。

To solve this I created a small helper class in which I copied some protected methods from PhantomJSDriverService: 为了解决这个问题,我创建了一个小的帮助程序类,在其中我从PhantomJSDriverService复制了一些受保护的方法:

public class MyPhantomDriverService {
    public static PhantomJSDriverService createDefaultService(Capabilities desiredCapabilities, Map<String, String> env) {
        Proxy proxy = null;
        if (desiredCapabilities != null) {
            proxy = Proxy.extractFrom(desiredCapabilities);
        }

        File phantomjsfile = findPhantomJS(desiredCapabilities, "https://github.com/ariya/phantomjs/wiki", "http://phantomjs.org/download.html");
        File ghostDriverfile = findGhostDriver(desiredCapabilities, "https://github.com/detro/ghostdriver/blob/master/README.md", "https://github.com/detro/ghostdriver/downloads");
        Builder builder = new Builder();
        builder.usingPhantomJSExecutable(phantomjsfile)
            .usingGhostDriver(ghostDriverfile)
            .usingAnyFreePort()
            .withProxy(proxy)
            .withLogFile(new File("phantomjsdriver.log"))
            .usingCommandLineArguments(findCLIArgumentsFromCaps(desiredCapabilities, "phantomjs.cli.args"))
            .usingGhostDriverCommandLineArguments(findCLIArgumentsFromCaps(desiredCapabilities, "phantomjs.ghostdriver.cli.args"));
        if(null != env)
            builder.withEnvironment(env);
        return builder.build();
    }

    public static File findPhantomJS(Capabilities desiredCapabilities, String docsLink, String downloadLink) {
        String phantomjspath;
        if (desiredCapabilities != null && desiredCapabilities.getCapability("phantomjs.binary.path") != null) {
            phantomjspath = (String)desiredCapabilities.getCapability("phantomjs.binary.path");
        } else {
            phantomjspath = (new ExecutableFinder()).find("phantomjs");
            phantomjspath = System.getProperty("phantomjs.binary.path", phantomjspath);
        }

        Preconditions.checkState(phantomjspath != null, "The path to the driver executable must be set by the %s capability/system property/PATH variable; for more information, see %s. The latest version can be downloaded from %s", "phantomjs.binary.path", docsLink, downloadLink);
        File phantomjs = new File(phantomjspath);
        checkExecutable(phantomjs);
        return phantomjs;
    }

    protected static File findGhostDriver(Capabilities desiredCapabilities, String docsLink, String downloadLink) {
        String ghostdriverpath;
        if (desiredCapabilities != null && desiredCapabilities.getCapability("phantomjs.ghostdriver.path") != null) {
            ghostdriverpath = (String)desiredCapabilities.getCapability("phantomjs.ghostdriver.path");
        } else {
            ghostdriverpath = System.getProperty("phantomjs.ghostdriver.path");
        }

        if (ghostdriverpath != null) {
            File ghostdriver = new File(ghostdriverpath);
            Preconditions.checkState(ghostdriver.exists(), "The GhostDriver does not exist: %s", ghostdriver.getAbsolutePath());
            Preconditions.checkState(ghostdriver.isFile(), "The GhostDriver is a directory: %s", ghostdriver.getAbsolutePath());
            Preconditions.checkState(ghostdriver.canRead(), "The GhostDriver is not a readable file: %s", ghostdriver.getAbsolutePath());
            return ghostdriver;
        } else {
            return null;
        }
    }

    protected static void checkExecutable(File exe) {
        Preconditions.checkState(exe.exists(), "The driver executable does not exist: %s", exe.getAbsolutePath());
        Preconditions.checkState(!exe.isDirectory(), "The driver executable is a directory: %s", exe.getAbsolutePath());
        Preconditions.checkState(exe.canExecute(), "The driver is not executable: %s", exe.getAbsolutePath());
    }

    private static String[] findCLIArgumentsFromCaps(Capabilities desiredCapabilities, String capabilityName) {
        if (desiredCapabilities != null) {
            Object cap = desiredCapabilities.getCapability(capabilityName);
            if (cap != null) {
                if (cap instanceof String[]) {
                    return (String[])((String[])cap);
                }

                if (cap instanceof Collection) {
                    try {
                        Collection<String> capCollection = (Collection<String>)cap;
                        return (String[])capCollection.toArray(new String[capCollection.size()]);
                    } catch (Exception var4) {
                        System.err.println(String.format("Unable to set Capability '%s' as it was neither a String[] or a Collection<String>", capabilityName));
                    }
                }
            }
        }

        return new String[0];
    }
}

With this new code I can now create a PhantomJSDriver as follows: 有了这个新代码,我现在可以创建一个PhantomJSDriver,如下所示:

        //-- 1. Make a temp directory which will contain our fonts.conf
        String tmp = System.getProperty("java.io.tmpdir");
        if(tmp == null) {
            tmp = "/tmp";
        }
        File dir = new File(tmp + File.separator + "/_phantomjs-config/fontconfig");
        dir.mkdirs();
        if(! dir.exists()) {
            throw new IOException("Can't create fontconfig directory to override phantomjs font settings at " + dir);
        }

        File conf = new File(dir, "fonts.conf");
        String text = "<match target=\"font\">\n"
            + "<edit mode=\"assign\" name=\"antialias\">\n"
            + "<bool>false</bool>\n"
            + "</edit>\n"
            + "</match>";
        try(FileOutputStream fos = new FileOutputStream(conf)) {
            fos.write(text.getBytes("UTF-8"));
        }

        //-- Set the XDG_CONFIG_HOME envvar; this is used by fontconfig as one of its locations

        Map<String, String> env = new HashMap<>();
        env.put("XDG_CONFIG_HOME", dir.getParentFile().getAbsolutePath());

        PhantomJSDriverService service = MyPhantomDriverService.createDefaultService(capabilities, env);
        wd = new PhantomJSDriver(service, capabilities);

Issues with the code 代码问题

The most important issue with this code is that it might fail if there are fonts.conf files (like $HOME/.fonts.conf) that define anti aliasing. 这段代码最重要的问题是,如果存在定义反混叠的fonts.conf文件(例如$ HOME / .fonts.conf),它可能会失败。 But for my test case this works OK. 但是对于我的测试用例,这行得通。

The same trick also works for Headless Chrome ;) 同样的技巧也适用于无头Chrome;)

To disable anti aliasing on headless chrome use the same code above to generate the fonts.conf file, then allocate a Chrome webdriver as follows: 要在无头chrome上禁用反锯齿,请使用上面的相同代码生成fonts.conf文件,然后按以下方式分配Chrome网络驱动程序:

import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeDriverService;
import org.openqa.selenium.chrome.ChromeDriverService.Builder;

...

Map<String, String> env = new HashMap<>();
env.put("XDG_CONFIG_HOME", dir.getParentFile().getAbsolutePath());

Builder builder = new Builder();
builder.usingAnyFreePort();
builder.withEnvironment(env);
ChromeDriverService service = builder.build();
return new ChromeDriver(service, dc);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM