blob: aeabfbfa46a045c0150e6b216d5e80a8651939dd [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.graphics.pdf.cts;
import static android.graphics.pdf.cts.Utils.A4_HEIGHT_PTS;
import static android.graphics.pdf.cts.Utils.A4_PORTRAIT;
import static android.graphics.pdf.cts.Utils.A4_WIDTH_PTS;
import static android.graphics.pdf.cts.Utils.A5_PORTRAIT;
import static android.graphics.pdf.cts.Utils.createRenderer;
import static android.graphics.pdf.cts.Utils.renderAndCompare;
import static android.graphics.pdf.cts.Utils.renderWithTransform;
import static android.graphics.pdf.cts.Utils.verifyException;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.pdf.PdfRenderer;
import android.graphics.pdf.PdfRenderer.Page;
import android.graphics.pdf.cts.R;
import androidx.annotation.NonNull;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.IOException;
/**
* All test for {@link PdfRenderer} beside the valid transformation parameter tests of {@link
* PdfRenderer.Page#render}.
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
public class PdfRendererTest {
private static final int A5_PORTRAIT_PRINTSCALING_DEFAULT =
R.raw.a5_portrait_rgbb_1_6_printscaling_default;
private static final int A5_PORTRAIT_PRINTSCALING_NONE =
R.raw.a5_portrait_rgbb_1_6_printscaling_none;
private static final int TWO_PAGES = R.raw.two_pages;
private static final int PROTECTED_PDF = R.raw.protected_pdf;
private Context mContext;
@Before
public void setup() {
mContext = InstrumentationRegistry.getTargetContext();
}
@Test
public void constructRendererNull() throws Exception {
verifyException(() -> new PdfRenderer(null), NullPointerException.class);
}
@Test
public void constructRendererFromNonPDF() throws Exception {
// Open jpg as if it was a PDF
verifyException(() -> createRenderer(R.raw.testimage, mContext), IOException.class);
}
@Test
public void constructRendererFromProtectedPDF() throws Exception {
verifyException(() -> createRenderer(PROTECTED_PDF, mContext), SecurityException.class);
}
@Test
public void rendererRecoversAfterFailure() throws Exception {
// Create rendered to prevent lib from being unloaded
PdfRenderer firstRenderer = createRenderer(A4_PORTRAIT, mContext);
verifyException(() -> createRenderer(PROTECTED_PDF, mContext), SecurityException.class);
// We can create new renderers after we failed to create one
PdfRenderer renderer = createRenderer(TWO_PAGES, mContext);
renderer.close();
firstRenderer.close();
}
@Test
public void useRendererAfterClose() throws Exception {
PdfRenderer renderer = createRenderer(A4_PORTRAIT, mContext);
renderer.close();
verifyException(renderer::close, IllegalStateException.class);
verifyException(renderer::getPageCount, IllegalStateException.class);
verifyException(renderer::shouldScaleForPrinting, IllegalStateException.class);
verifyException(() -> renderer.openPage(0), IllegalStateException.class);
}
@Test
public void usePageAfterClose() throws Exception {
PdfRenderer renderer = createRenderer(A4_PORTRAIT, mContext);
Page page = renderer.openPage(0);
page.close();
// Legacy behavior: The properties are cached, hence they are still available after the page
// is closed
page.getHeight();
page.getWidth();
page.getIndex();
verifyException(page::close, IllegalStateException.class);
// Legacy support. An IllegalStateException would be nice by unfortunately the legacy
// implementation returned NullPointerException
verifyException(() -> page.render(null, null, null, Page.RENDER_MODE_FOR_DISPLAY),
NullPointerException.class);
renderer.close();
}
@Test
public void closeWithOpenPage() throws Exception {
PdfRenderer renderer = createRenderer(A4_PORTRAIT, mContext);
Page page = renderer.openPage(0);
verifyException(renderer::close, IllegalStateException.class);
page.close();
renderer.close();
}
@Test
public void openTwoPages() throws Exception {
try (PdfRenderer renderer = createRenderer(TWO_PAGES, mContext)) {
// Cannot open two pages at once
Page page = renderer.openPage(0);
verifyException(() -> renderer.openPage(1), IllegalStateException.class);
page.close();
}
}
@Test
public void testPageCount() throws Exception {
try (PdfRenderer renderer = createRenderer(TWO_PAGES, mContext)) {
assertEquals(2, renderer.getPageCount());
}
}
@Test
public void testOpenPage() throws Exception {
try (PdfRenderer renderer = createRenderer(TWO_PAGES, mContext)) {
verifyException(() -> renderer.openPage(-1), IllegalArgumentException.class);
Page page0 = renderer.openPage(0);
page0.close();
Page page1 = renderer.openPage(1);
page1.close();
verifyException(() -> renderer.openPage(2), IllegalArgumentException.class);
}
}
@Test
public void testPageSize() throws Exception {
try (PdfRenderer renderer = createRenderer(A4_PORTRAIT, mContext);
Page page = renderer.openPage(0)) {
assertEquals(A4_HEIGHT_PTS, page.getHeight());
assertEquals(A4_WIDTH_PTS, page.getWidth());
}
}
@Test
public void testPrintScaleDefault() throws Exception {
try (PdfRenderer renderer = createRenderer(A5_PORTRAIT, mContext)) {
assertTrue(renderer.shouldScaleForPrinting());
}
}
@Test
public void testPrintScalePDF16Default() throws Exception {
try (PdfRenderer renderer = createRenderer(A5_PORTRAIT_PRINTSCALING_DEFAULT, mContext)) {
assertTrue(renderer.shouldScaleForPrinting());
}
}
@Test
public void testPrintScalePDF16None() throws Exception {
try (PdfRenderer renderer = createRenderer(A5_PORTRAIT_PRINTSCALING_NONE, mContext)) {
assertFalse(renderer.shouldScaleForPrinting());
}
}
/**
* Take 16 color probes in the middle of the 16 segments of the page in the following pattern:
* <pre>
* +----+----+----+----+
* | 0 : 1 : 2 : 3 |
* +....:....:....:....+
* | 4 : 5 : 6 : 7 |
* +....:....:....:....+
* | 8 : 9 : 10 : 11 |
* +....:....:....:....+
* | 12 : 13 : 14 : 15 |
* +----+----+----+----+
* </pre>
*
* @param bm The bitmap to probe
*
* @return The color at the probes
*/
private @NonNull int[] getColorProbes(@NonNull Bitmap bm) {
int[] probes = new int[16];
for (int row = 0; row < 4; row++) {
for (int column = 0; column < 4; column++) {
probes[row * 4 + column] = bm.getPixel((int) (bm.getWidth() * (column + 0.5) / 4),
(int) (bm.getHeight() * (row + 0.5) / 4));
}
}
return probes;
}
/**
* Implementation for {@link #renderNoTransformationAndComparePointsForScreen} and {@link
* #renderNoTransformationAndComparePointsForPrint}.
*
* @param renderMode The render mode to use
*
* @throws Exception If anything was unexpected
*/
private void renderNoTransformationAndComparePoints(int renderMode) throws Exception {
Bitmap bm = renderWithTransform(A4_WIDTH_PTS, A4_HEIGHT_PTS, A4_PORTRAIT, null, null,
renderMode, mContext);
int[] probes = getColorProbes(bm);
// Compare rendering to expected result. This ensures that all other tests in this class do
// not accidentally all compare empty bitmaps.
assertEquals(Color.RED, probes[0]);
assertEquals(Color.RED, probes[1]);
assertEquals(Color.GREEN, probes[2]);
assertEquals(Color.GREEN, probes[3]);
assertEquals(Color.RED, probes[4]);
assertEquals(Color.RED, probes[5]);
assertEquals(Color.GREEN, probes[6]);
assertEquals(Color.GREEN, probes[7]);
assertEquals(Color.BLUE, probes[8]);
assertEquals(Color.BLUE, probes[9]);
assertEquals(Color.BLACK, probes[10]);
assertEquals(Color.BLACK, probes[11]);
assertEquals(Color.BLUE, probes[12]);
assertEquals(Color.BLUE, probes[13]);
assertEquals(Color.BLACK, probes[14]);
assertEquals(Color.BLACK, probes[15]);
}
@Test
public void renderNoTransformationAndComparePointsForScreen() throws Exception {
renderNoTransformationAndComparePoints(Page.RENDER_MODE_FOR_DISPLAY);
}
@Test
public void renderNoTransformationAndComparePointsForPrint() throws Exception {
renderNoTransformationAndComparePoints(Page.RENDER_MODE_FOR_PRINT);
}
@Test
public void renderPerspective() throws Exception {
Matrix transform = new Matrix();
transform.setValues(new float[] { 1, 1, 1, 1, 1, 1, 1, 1, 1 });
verifyException(
() -> renderWithTransform(A4_WIDTH_PTS, A4_HEIGHT_PTS, A4_PORTRAIT, null, transform,
Page.RENDER_MODE_FOR_DISPLAY, mContext), IllegalArgumentException.class);
}
@Test
public void render45degreeRotationTranslationAndScaleAndClip() throws Exception {
Matrix transform = new Matrix();
// Rotate on top left corner
transform.postRotate(45);
// Move
transform.postTranslate(A4_WIDTH_PTS / 4, A4_HEIGHT_PTS / 4);
// Scale to 75%
transform.postScale(0.75f, 0.75f);
// Clip
Rect clip = new Rect(20, 20, A4_WIDTH_PTS - 20, A4_HEIGHT_PTS - 20);
renderAndCompare(A4_WIDTH_PTS, A4_HEIGHT_PTS, A4_PORTRAIT, clip, transform,
Page.RENDER_MODE_FOR_DISPLAY, mContext);
}
@Test
public void renderStreched() throws Exception {
renderAndCompare(A4_WIDTH_PTS * 4 / 3, A4_HEIGHT_PTS * 3 / 4, A4_PORTRAIT, null, null,
Page.RENDER_MODE_FOR_DISPLAY, mContext);
}
@Test
public void renderWithClip() throws Exception {
Rect clip = new Rect(20, 20, A4_WIDTH_PTS - 50, A4_HEIGHT_PTS - 50);
renderAndCompare(A4_WIDTH_PTS, A4_HEIGHT_PTS, A4_PORTRAIT, clip, null,
Page.RENDER_MODE_FOR_DISPLAY, mContext);
}
@Test
public void renderWithAllClipped() throws Exception {
Rect clip = new Rect(A4_WIDTH_PTS / 2, A4_HEIGHT_PTS / 2, A4_WIDTH_PTS / 2,
A4_HEIGHT_PTS / 2);
renderAndCompare(A4_WIDTH_PTS, A4_HEIGHT_PTS, A4_PORTRAIT, clip, null,
Page.RENDER_MODE_FOR_DISPLAY, mContext);
}
@Test
public void renderWithBadLowerCornerOfClip() throws Exception {
Rect clip = new Rect(0, 0, A4_WIDTH_PTS + 20, A4_HEIGHT_PTS + 20);
verifyException(
() -> renderWithTransform(A4_WIDTH_PTS, A4_HEIGHT_PTS, A4_PORTRAIT, clip, null,
Page.RENDER_MODE_FOR_DISPLAY, mContext), IllegalArgumentException.class);
}
@Test
public void renderWithBadUpperCornerOfClip() throws Exception {
Rect clip = new Rect(-20, -20, A4_WIDTH_PTS, A4_HEIGHT_PTS);
verifyException(
() -> renderWithTransform(A4_WIDTH_PTS, A4_HEIGHT_PTS, A4_PORTRAIT, clip, null,
Page.RENDER_MODE_FOR_DISPLAY, mContext), IllegalArgumentException.class);
}
@Test
public void renderTwoModes() throws Exception {
verifyException(
() -> renderWithTransform(A4_WIDTH_PTS, A4_HEIGHT_PTS, A4_PORTRAIT, null, null,
Page.RENDER_MODE_FOR_DISPLAY | Page.RENDER_MODE_FOR_PRINT, mContext),
IllegalArgumentException.class);
}
@Test
public void renderBadMode() throws Exception {
verifyException(
() -> renderWithTransform(A4_WIDTH_PTS, A4_HEIGHT_PTS, A4_PORTRAIT, null, null,
1 << 30, mContext), IllegalArgumentException.class);
}
@Test
public void renderAllModes() throws Exception {
verifyException(
() -> renderWithTransform(A4_WIDTH_PTS, A4_HEIGHT_PTS, A4_PORTRAIT, null, null, -1,
mContext), IllegalArgumentException.class);
}
@Test
public void renderNoMode() throws Exception {
verifyException(
() -> renderWithTransform(A4_WIDTH_PTS, A4_HEIGHT_PTS, A4_PORTRAIT, null, null, 0,
mContext), IllegalArgumentException.class);
}
@Test
public void renderOnNullBitmap() throws Exception {
try (PdfRenderer renderer = createRenderer(A4_PORTRAIT, mContext);
Page page = renderer.openPage(0)) {
verifyException(() -> page.render(null, null, null, Page.RENDER_MODE_FOR_DISPLAY),
NullPointerException.class);
}
}
}