Initial commit

This commit is contained in:
Joe Blough 2011-08-27 22:02:23 -04:00
commit 36f380caac
162 changed files with 37409 additions and 0 deletions

8
.classpath Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="crypto-src"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry kind="output" path="bin"/>
</classpath>

33
.project Normal file
View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>PdfViewer</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

20
AndroidManifest.xml Normal file
View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.sf.andpdf.pdfviewer"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="4" />
<application android:icon="@drawable/icon" android:label="@string/app_name">
<!--
<activity android:name=".PdfViewerActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
-->
<activity android:name=".PdfViewerActivity" />
</application>
</manifest>

BIN
PdfViewer.jar Normal file

Binary file not shown.

37
README.txt Normal file
View File

@ -0,0 +1,37 @@
This is a packaging of the project "Android PDF Viewer" (http://andpdf.sourceforge.net/) into a reusable library to make PDF viewing easier from within your Android application.
Consistent with the Android PDF Viewer project, the licensing for the PdfViewer project is LGPL
Quickstart incorporating a PDF viewing activity into your project:
1) Add PdfViewer.jar into your project's build path
2) Copy the following drawable resources from PdfViewer/res/drawable into YourProject/res/drawable
left_arrow.png
right_arrow.png
zoom_in.png
zoom_out.png
3) Copy the following layout resources from PdfViewer/res/layout into YourProject/res/layout
dialog_pagenumber.xml
pdf_file_password.xml
4) Derive your PDF activity from net.sf.andpdf.pdfviewer.PdfViewerActivity
5) Using the default drawables and layouts:
public int getPreviousPageImageResource() { return R.drawable.left_arrow; }
public int getNextPageImageResource() { return R.drawable.right_arrow; }
public int getZoomInImageResource() { return R.drawable.zoom_in; }
public int getZoomOutImageResource() { return R.drawable.zoom_out; }
public int getPdfPasswordLayoutResource() { return R.layout.pdf_file_password; }
public int getPdfPageNumberResource() { return R.layout.dialog_pagenumber; }
public int getPdfPasswordEditField() { return R.id.etPassword; }
public int getPdfPasswordOkButton() { return R.id.btOK; }
public int getPdfPasswordExitButton() { return R.id.btExit; }
public int getPdfPageNumberEditField() { return R.id.pagenum_edit; }
6) Invoke your PdfViewActivity derived with the following code:
Intent intent = new Intent(this, TipPdfViewerActivity.class);
intent.putExtra(PdfViewerActivity.EXTRA_PDFFILENAME, "PATH TO PDF GOES HERE");
startActivity(intent);

17
build.xml Normal file
View File

@ -0,0 +1,17 @@
<project name="PdfViewer" default="dist" basedir=".">
<description>PdfViewer</description>
<!-- Setting global properties for this build -->
<property name="src" location="src" />
<property name="bin" location="bin" />
<target name="dist">
<jar destfile="PdfViewer.jar" basedir="bin/">
<!-- Use ** to include the directory recursively -->
<include name="com/**" />
<include name="net/**" />
<include name="de/**" />
<include name="androswing/**" />
</jar>
</target>
</project>

View File

@ -0,0 +1,8 @@
package org.bouncycastle.crypto;
/**
* all parameter classes implement this.
*/
public interface CipherParameters
{
}

View File

@ -0,0 +1,29 @@
package org.bouncycastle.crypto;
/**
* this exception is thrown if a buffer that is meant to have output
* copied into it turns out to be too short, or if we've been given
* insufficient input. In general this exception will get thrown rather
* than an ArrayOutOfBounds exception.
*/
public class DataLengthException
extends RuntimeCryptoException
{
/**
* base constructor.
*/
public DataLengthException()
{
}
/**
* create a DataLengthException with the given message.
*
* @param message the message to be carried with the exception.
*/
public DataLengthException(
String message)
{
super(message);
}
}

View File

@ -0,0 +1,26 @@
package org.bouncycastle.crypto;
/**
* the foundation class for the exceptions thrown by the crypto packages.
*/
public class RuntimeCryptoException
extends RuntimeException
{
/**
* base constructor.
*/
public RuntimeCryptoException()
{
}
/**
* create a RuntimeCryptoException with the given message.
*
* @param message the message to be carried with the exception.
*/
public RuntimeCryptoException(
String message)
{
super(message);
}
}

View File

@ -0,0 +1,53 @@
package org.bouncycastle.crypto;
/**
* the interface stream ciphers conform to.
*/
public interface StreamCipher
{
/**
* Initialise the cipher.
*
* @param forEncryption if true the cipher is initialised for
* encryption, if false for decryption.
* @param params the key and other data required by the cipher.
* @exception IllegalArgumentException if the params argument is
* inappropriate.
*/
public void init(boolean forEncryption, CipherParameters params)
throws IllegalArgumentException;
/**
* Return the name of the algorithm the cipher implements.
*
* @return the name of the algorithm the cipher implements.
*/
public String getAlgorithmName();
/**
* encrypt/decrypt a single byte returning the result.
*
* @param in the byte to be processed.
* @return the result of processing the input byte.
*/
public byte returnByte(byte in);
/**
* process a block of bytes from in putting the result into out.
*
* @param in the input byte array.
* @param inOff the offset into the in array where the data to be processed starts.
* @param len the number of bytes to be processed.
* @param out the output buffer the processed bytes go into.
* @param outOff the offset into the output byte array the processed data starts at.
* @exception DataLengthException if the output buffer is too small.
*/
public void processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)
throws DataLengthException;
/**
* reset the cipher. This leaves it in the same state
* it was at after the last init (if there was one).
*/
public void reset();
}

View File

@ -0,0 +1,143 @@
package org.bouncycastle.crypto.engines;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.StreamCipher;
import org.bouncycastle.crypto.params.KeyParameter;
public class RC4Engine implements StreamCipher
{
private final static int STATE_LENGTH = 256;
/*
* variables to hold the state of the RC4 engine
* during encryption and decryption
*/
private byte[] engineState = null;
private int x = 0;
private int y = 0;
private byte[] workingKey = null;
/**
* initialise a RC4 cipher.
*
* @param forEncryption whether or not we are for encryption.
* @param params the parameters required to set up the cipher.
* @exception IllegalArgumentException if the params argument is
* inappropriate.
*/
public void init(
boolean forEncryption,
CipherParameters params
)
{
if (params instanceof KeyParameter)
{
/*
* RC4 encryption and decryption is completely
* symmetrical, so the 'forEncryption' is
* irrelevant.
*/
workingKey = ((KeyParameter)params).getKey();
setKey(workingKey);
return;
}
throw new IllegalArgumentException("invalid parameter passed to RC4 init - " + params.getClass().getName());
}
public String getAlgorithmName()
{
return "RC4";
}
public byte returnByte(byte in)
{
x = (x + 1) & 0xff;
y = (engineState[x] + y) & 0xff;
// swap
byte tmp = engineState[x];
engineState[x] = engineState[y];
engineState[y] = tmp;
// xor
return (byte)(in ^ engineState[(engineState[x] + engineState[y]) & 0xff]);
}
public void processBytes(
byte[] in,
int inOff,
int len,
byte[] out,
int outOff)
{
if ((inOff + len) > in.length)
{
throw new DataLengthException("input buffer too short");
}
if ((outOff + len) > out.length)
{
throw new DataLengthException("output buffer too short");
}
for (int i = 0; i < len ; i++)
{
x = (x + 1) & 0xff;
y = (engineState[x] + y) & 0xff;
// swap
byte tmp = engineState[x];
engineState[x] = engineState[y];
engineState[y] = tmp;
// xor
out[i+outOff] = (byte)(in[i + inOff]
^ engineState[(engineState[x] + engineState[y]) & 0xff]);
}
}
public void reset()
{
setKey(workingKey);
}
// Private implementation
private void setKey(byte[] keyBytes)
{
workingKey = keyBytes;
// System.out.println("the key length is ; "+ workingKey.length);
x = 0;
y = 0;
if (engineState == null)
{
engineState = new byte[STATE_LENGTH];
}
// reset the state of the engine
for (int i=0; i < STATE_LENGTH; i++)
{
engineState[i] = (byte)i;
}
int i1 = 0;
int i2 = 0;
for (int i=0; i < STATE_LENGTH; i++)
{
i2 = ((keyBytes[i1] & 0xff) + engineState[i] + i2) & 0xff;
// do the byte-swap inline
byte tmp = engineState[i];
engineState[i] = engineState[i2];
engineState[i2] = tmp;
i1 = (i1+1) % keyBytes.length;
}
}
}

View File

@ -0,0 +1,30 @@
package org.bouncycastle.crypto.params;
import org.bouncycastle.crypto.CipherParameters;
public class KeyParameter
implements CipherParameters
{
private byte[] key;
public KeyParameter(
byte[] key)
{
this(key, 0, key.length);
}
public KeyParameter(
byte[] key,
int keyOff,
int keyLen)
{
this.key = new byte[keyLen];
System.arraycopy(key, keyOff, this.key, 0, keyLen);
}
public byte[] getKey()
{
return key;
}
}

12
default.properties Normal file
View File

@ -0,0 +1,12 @@
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system use,
# "build.properties", and override values to adapt the script to your
# project structure.
# Project target.
target=android-4
android.library=true

36
proguard.cfg Normal file
View File

@ -0,0 +1,36 @@
-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontpreverify
-verbose
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class com.android.vending.licensing.ILicensingService
-keepclasseswithmembernames class * {
native <methods>;
}
-keepclasseswithmembernames class * {
public <init>(android.content.Context, android.util.AttributeSet);
}
-keepclasseswithmembernames class * {
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}

27
res/anim/slide_in.xml Executable file
View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2008 Romain Guy
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.
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="200"
android:fromYDelta="100%"
android:toYDelta="0%" />
<alpha
android:duration="200"
android:fromAlpha="0.0"
android:toAlpha="1.0" />
</set>

27
res/anim/slide_out.xml Executable file
View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2008 Romain Guy
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.
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="200"
android:fromYDelta="0%"
android:toYDelta="100%" />
<alpha
android:duration="200"
android:fromAlpha="1.0"
android:toAlpha="0.0" />
</set>

BIN
res/drawable-hdpi/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
res/drawable-ldpi/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
res/drawable-mdpi/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
res/drawable/back01.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
res/drawable/back02.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
res/drawable/doc.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
res/drawable/folder.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
res/drawable/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
res/drawable/left_arrow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
res/drawable/pdf.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
res/drawable/zoom_in.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 983 B

BIN
res/drawable/zoom_out.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 904 B

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2008 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/username_view"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_marginLeft="20dip"
android:layout_marginRight="20dip"
android:text="Page number:"
android:inputType="number"
android:gravity="left"
android:textAppearance="?android:attr/textAppearanceMedium" />
<EditText
android:id="@+id/pagenum_edit"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_marginLeft="20dip"
android:layout_marginRight="20dip"
android:scrollHorizontally="true"
android:autoText="false"
android:capitalize="none"
android:inputType="phone"
android:gravity="fill_horizontal"
android:textAppearance="?android:attr/textAppearanceMedium" />
</LinearLayout>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ImageView android:id="@+id/pdf_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:padding="5px"/>
</LinearLayout>

12
res/layout/main.xml Normal file
View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
</LinearLayout>

View File

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="10dip"
android:paddingTop="5dip"
android:paddingRight="10dip"
android:paddingBottom="4dip"
android:background="#99000000" >
<ImageButton android:id="@+id/zoom_out"
android:text="Zoom Out"
android:src="@drawable/zoom_out"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="10" />
<ImageButton android:id="@+id/zoom_in"
android:text="Zoom In"
android:src="@drawable/zoom_in"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="10" />
<TextView android:id="@+id/navigation_page_position_label"
android:text="1/1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="60"
android:gravity="center"
android:layout_gravity="center" />
<ImageButton android:id="@+id/navigation_previous"
android:text="Previous"
android:src="@drawable/left_arrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="10" />
<ImageButton android:id="@+id/navigation_next"
android:text="Next"
android:src="@drawable/right_arrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="10" />
</LinearLayout>

View File

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (c) 2009 Ferenc Hechler - ferenc_hechler@users.sourceforge.net
This file is part of the Android PDF Viewer
The Android PDF Viewer 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.
The Android PDF Viewer is distributed
in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with the Android PDF Viewer
if not, write to the Free Software Foundation, Inc.,
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<!-- filename -->
<TextView
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_weight="0"
android:paddingBottom="0dip"
android:text="Password protected PDF-File:"/>
<EditText
android:id="@+id/etPassword"
android:layout_width="fill_parent"
android:layout_height="60px"
android:text=""
android:layout_weight="10"
android:hint="please enter password for PDF">
<requestFocus />
</EditText>
<!-- Buttons 'OK' + 'Exit' -->
<LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="center_horizontal" android:orientation="horizontal" >
<Button android:id="@+id/btExit" android:layout_width="120px" android:layout_height="40px" android:text="Exit"></Button>
<Button android:id="@+id/btOK" android:layout_width="120px" android:layout_height="40px" android:text="OK"></Button>
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (c) 2009 Ferenc Hechler - ferenc_hechler@users.sourceforge.net
This file is part of the Android PDF Viewer
The Android PDF Viewer 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.
The Android PDF Viewer is distributed
in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with the Android PDF Viewer
if not, write to the Free Software Foundation, Inc.,
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</LinearLayout>
</ScrollView>
</ScrollView>

7
res/values/color.xml Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<drawable name="white">#FFFFFFFF</drawable>
<drawable name="black">#000000</drawable>
<drawable name="blue">#000000FF</drawable>
<drawable name="violet">#9370DB</drawable>
</resources>

5
res/values/strings.xml Normal file
View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="hello">Hello World, PdfViewerActivity!</string>
<string name="app_name">PdfViewer</string>
</resources>

View File

@ -0,0 +1,30 @@
package androswing.tree;
import java.util.ArrayList;
public class DefaultMutableTreeNode {
private DefaultMutableTreeNode parent;
private Object userObject;
private ArrayList<DefaultMutableTreeNode> children;
protected DefaultMutableTreeNode(){
parent = null;
userObject = null;
children = new ArrayList<DefaultMutableTreeNode>();
}
protected Object getUserObject() {
return userObject;
}
protected void setUserObject(Object userObject) {
this.userObject = userObject;
}
public void add(DefaultMutableTreeNode newChild) {
newChild.parent = this;
children.add(newChild);
}
public DefaultMutableTreeNode getParent() {
return parent;
}
}

View File

@ -0,0 +1 @@
.DS_Store

View File

@ -0,0 +1,378 @@
/*
* $Id: BaseWatchable.java,v 1.5 2009/02/09 17:14:32 tomoke Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview;
/**
* An abstract implementation of the watchable interface, that is extended
* by the parser and renderer to do their thing.
*/
public abstract class BaseWatchable implements Watchable, Runnable {
/** the current status, from the list in Watchable */
private int status = Watchable.UNKNOWN;
/** a lock for status-related operations */
private Object statusLock = new Object();
/** a lock for parsing operations */
private Object parserLock = new Object();
/** when to stop */
private Gate gate;
/** suppress local stack trace on setError. */
private static boolean SuppressSetErrorStackTrace = false;
/** the thread we are running in */
private Thread thread;
/**
* Creates a new instance of BaseWatchable
*/
protected BaseWatchable() {
setStatus(Watchable.NOT_STARTED);
}
/**
* Perform a single iteration of this watchable. This is the minimum
* granularity which the go() commands operate over.
*
* @return one of three values: <ul>
* <li> Watchable.RUNNING if there is still data to be processed
* <li> Watchable.NEEDS_DATA if there is no data to be processed but
* the execution is not yet complete
* <li> Watchable.COMPLETED if the execution is complete
* </ul>
*/
protected abstract int iterate() throws Exception;
/**
* Prepare for a set of iterations. Called before the first iterate() call
* in a sequence. Subclasses should extend this method if they need to do
* anything to setup.
*/
protected void setup() {
// do nothing
}
/**
* Clean up after a set of iterations. Called after iteration has stopped
* due to completion, manual stopping, or error.
*/
protected void cleanup() {
// do nothing
}
public void run() {
// System.out.println(Thread.currentThread().getName() + " starting");
// call setup once we started
if (getStatus() == Watchable.NOT_STARTED) {
setup();
}
setStatus(Watchable.PAUSED);
synchronized (parserLock) {
while (!isFinished() && getStatus() != Watchable.STOPPED) {
if (isExecutable()) {
// set the status to running
setStatus(Watchable.RUNNING);
try {
// keep going until the status is no longer running,
// our gate tells us to stop, or no-one is watching
while ((getStatus() == Watchable.RUNNING) &&
(gate == null || !gate.iterate())) {
// update the status based on this iteration
setStatus(iterate());
}
// make sure we are paused
if (getStatus() == Watchable.RUNNING) {
setStatus(Watchable.PAUSED);
}
} catch (Exception ex) {
setError(ex);
}
} else {
// System.out.println(getName() + " waiting: status = " + getStatusString());
// wait for our status to change
synchronized (statusLock) {
if (!isExecutable()) {
try {
statusLock.wait();
} catch (InterruptedException ie) {
// ignore
}
}
}
}
}
}
// System.out.println(Thread.currentThread().getName() + " exiting: status = " + getStatusString());
// call cleanup when we are done
if (getStatus() == Watchable.COMPLETED ||
getStatus() == Watchable.ERROR) {
cleanup();
}
// notify that we are no longer running
thread = null;
}
/**
* Get the status of this watchable
*
* @return one of the well-known statuses
*/
public int getStatus() {
return status;
}
/**
* Return whether this watchable has finished. A watchable is finished
* when its status is either COMPLETED, STOPPED or ERROR
*/
public boolean isFinished() {
int s = getStatus();
return (s == Watchable.COMPLETED ||
s == Watchable.ERROR);
}
/**
* return true if this watchable is ready to be executed
*/
public boolean isExecutable() {
return ((status == Watchable.PAUSED || status == Watchable.RUNNING) &&
(gate == null || !gate.stop()));
}
/**
* Stop this watchable. Stop will cause all processing to cease,
* and the watchable to be destroyed.
*/
public void stop() {
setStatus(Watchable.STOPPED);
}
/**
* Start this watchable and run in a new thread until it is finished or
* stopped.
* Note the watchable may be stopped if go() with a
* different time is called during execution.
*/
public synchronized void go() {
gate = null;
execute(false);
}
/**
* Start this watchable and run until it is finished or stopped.
* Note the watchable may be stopped if go() with a
* different time is called during execution.
*
* @param synchronous if true, run in this thread
*/
public synchronized void go(boolean synchronous) {
gate = null;
execute(synchronous);
}
/**
* Start this watchable and run for the given number of steps or until
* finished or stopped.
*
* @param steps the number of steps to run for
*/
public synchronized void go(int steps) {
gate = new Gate();
gate.setStopIterations(steps);
execute(false);
}
/**
* Start this watchable and run for the given amount of time, or until
* finished or stopped.
*
* @param millis the number of milliseconds to run for
*/
public synchronized void go(long millis) {
gate = new Gate();
gate.setStopTime(millis);
execute(false);
}
/**
* Wait for this watchable to finish
*/
public void waitForFinish() {
synchronized (statusLock) {
while (!isFinished() && getStatus() != Watchable.STOPPED) {
try {
statusLock.wait();
} catch (InterruptedException ex) {
// ignore
}
}
}
}
/**
* Start executing this watchable
*
* @param synchronous if true, run in this thread
*/
protected synchronized void execute(boolean synchronous) {
// see if we're already running
if (thread != null) {
// we're already running. Make sure we wake up on any change.
synchronized (statusLock) {
statusLock.notifyAll();
}
return;
} else if (isFinished()) {
// we're all finished
return;
}
// we'return not running. Start up
if (synchronous) {
thread = Thread.currentThread();
run();
} else {
thread = new Thread(this);
thread.setName(getClass().getName());
thread.start();
}
}
/**
* Set the status of this watchable
*/
protected void setStatus(int status) {
synchronized (statusLock) {
this.status = status;
// System.out.println(getName() + " status set to " + getStatusString());
statusLock.notifyAll();
}
}
/**
* return true if we would be suppressing setError stack traces.
*
* @return boolean
*/
public static boolean isSuppressSetErrorStackTrace () {
return SuppressSetErrorStackTrace;
}
/**
* set suppression of stack traces from setError.
*
* @param suppressTrace
*/
public static void setSuppressSetErrorStackTrace(boolean suppressTrace) {
SuppressSetErrorStackTrace = suppressTrace;
}
/**
* Set an error on this watchable
*/
protected void setError(Exception error) {
if (!SuppressSetErrorStackTrace) {
error.printStackTrace();
}
setStatus(Watchable.ERROR);
}
private String getStatusString() {
switch (getStatus()) {
case Watchable.NOT_STARTED:
return "Not started";
case Watchable.RUNNING:
return "Running";
case Watchable.NEEDS_DATA:
return "Needs Data";
case Watchable.PAUSED:
return "Paused";
case Watchable.STOPPED:
return "Stopped";
case Watchable.COMPLETED:
return "Completed";
case Watchable.ERROR:
return "Error";
default:
return "Unknown";
}
}
/** A class that lets us give it a target time or number of steps,
* and will tell us to stop after that much time or that many steps
*/
class Gate {
/** whether this is a time-based (true) or step-based (false) gate */
private boolean timeBased;
/** the next gate, whether time or iterations */
private long nextGate;
/** set the stop time */
public void setStopTime(long millisFromNow) {
timeBased = true;
nextGate = System.currentTimeMillis() + millisFromNow;
}
/** set the number of iterations until we stop */
public void setStopIterations(int iterations) {
timeBased = false;
nextGate = iterations;
}
/** check whether we should stop.
*/
public boolean stop() {
if (timeBased) {
return (System.currentTimeMillis() >= nextGate);
} else {
return (nextGate < 0);
}
}
/** Notify the gate of one iteration. Returns true if we should
* stop or false if not
*/
public boolean iterate() {
if (!timeBased) {
nextGate--;
}
return stop();
}
}
}

View File

@ -0,0 +1,315 @@
/*
* $Id: Cache.java,v 1.4 2009/02/12 13:53:56 tomoke Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview;
import net.sf.andpdf.refs.SoftReference;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import android.graphics.Bitmap;
/**
* A cache of PDF pages and images.
*/
public class Cache {
/** the pages in the cache, mapped by page number */
private Map<Integer,SoftReference> pages;
/** Creates a new instance of a Cache */
public Cache() {
pages = Collections.synchronizedMap(new HashMap<Integer,SoftReference>());
}
/**
* Add a page to the cache. This method should be used for
* pages which have already been completely rendered.
*
* @param pageNumber the page number of this page
* @param page the page to add
*/
public void addPage(Integer pageNumber, PDFPage page) {
addPageRecord(pageNumber, page, null);
}
/**
* Add a page to the cache. This method should be used for
* pages which are still in the process of being rendered.
*
* @param pageNumber the page number of this page
* @param page the page to add
* @param parser the parser which is parsing this page
*/
public void addPage(Integer pageNumber, PDFPage page, PDFParser parser) {
addPageRecord(pageNumber, page, parser);
}
/**
* Add an image to the cache. This method should be used for images
* which have already been completely rendered
*
* @param page page this image is associated with
* @param info the image info associated with this image
* @param image the image to add
*/
public void addImage(PDFPage page, ImageInfo info, Bitmap image) {
addImageRecord(page, info, image, null);
}
/**
* Add an image to the cache. This method should be used for images
* which are still in the process of being rendered.
*
* @param page the page this image is associated with
* @param info the image info associated with this image
* @param image the image to add
* @param renderer the renderer which is rendering this page
*/
public void addImage(PDFPage page, ImageInfo info, Bitmap image,
PDFRenderer renderer) {
addImageRecord(page, info, image, renderer);
}
/**
* Get a page from the cache
*
* @param pageNumber the number of the page to get
* @return the page, if it is in the cache, or null if not
*/
public PDFPage getPage(Integer pageNumber) {
PageRecord rec = getPageRecord(pageNumber);
if (rec != null) {
return (PDFPage) rec.value;
}
// not found
return null;
}
/**
* Get a page's parser from the cache
*
* @param pageNumber the number of the page to get the parser for
* @return the parser, or null if it is not in the cache
*/
public PDFParser getPageParser(Integer pageNumber) {
PageRecord rec = getPageRecord(pageNumber);
if (rec != null) {
return (PDFParser) rec.generator;
}
// not found
return null;
}
/**
* Get an image from the cache
*
* @param page the page the image is associated with
* @param info the image info that describes the image
*
* @return the image if it is in the cache, or null if not
*/
public Bitmap getImage(PDFPage page, ImageInfo info) {
Record rec = getImageRecord(page, info);
if (rec != null) {
return (Bitmap) rec.value;
}
// not found
return null;
}
/**
* Get an image's renderer from the cache
*
* @param page the page this image was generated from
* @param info the image info describing the image
* @return the renderer, or null if it is not in the cache
*/
public PDFRenderer getImageRenderer(PDFPage page, ImageInfo info) {
Record rec = getImageRecord(page, info);
if (rec != null) {
return (PDFRenderer) rec.generator;
}
// not found
return null;
}
/**
* Remove a page and all its associated images, as well as its parser
* and renderers, from the cache
*
* @param pageNumber the number of the page to remove
*/
public void removePage(Integer pageNumber) {
removePageRecord(pageNumber);
}
/**
* Remove an image and its associated renderer from the cache
*
* @param page the page the image is generated from
* @param info the image info of the image to remove
*/
public void removeImage(PDFPage page, ImageInfo info) {
removeImageRecord(page, info);
}
/**
* The internal routine to add a page to the cache, and return the
* page record which was generated
*/
PageRecord addPageRecord(Integer pageNumber, PDFPage page,
PDFParser parser) {
PageRecord rec = new PageRecord();
rec.value = page;
rec.generator = parser;
pages.put(pageNumber, new SoftReference<Record>(rec));
return rec;
}
/**
* Get a page's record from the cache
*
* @return the record, or null if it's not in the cache
*/
PageRecord getPageRecord(Integer pageNumber) {
// System.out.println("Request for page " + pageNumber);
SoftReference ref = pages.get(pageNumber);
if (ref != null) {
String val = (ref.get() == null) ? " not in " : " in ";
// System.out.println("Page " + pageNumber + val + "cache");
return (PageRecord) ref.get();
}
// System.out.println("Page " + pageNumber + " not in cache");
// not in cache
return null;
}
/**
* Remove a page's record from the cache
*/
PageRecord removePageRecord(Integer pageNumber) {
SoftReference ref = pages.remove(pageNumber);
if (ref != null) {
return (PageRecord) ref.get();
}
// not in cache
return null;
}
/**
* The internal routine to add an image to the cache and return the
* record that was generated.
*/
Record addImageRecord(PDFPage page, ImageInfo info,
Bitmap image, PDFRenderer renderer) {
// first, find or create the relevant page record
Integer pageNumber = new Integer(page.getPageNumber());
PageRecord pageRec = getPageRecord(pageNumber);
if (pageRec == null) {
pageRec = addPageRecord(pageNumber, page, null);
}
// next, create the image record
Record rec = new Record();
rec.value = image;
rec.generator = renderer;
// add it to the cache
pageRec.images.put(info, new SoftReference<Record>(rec));
return rec;
}
/**
* Get an image's record from the cache
*
* @return the record, or null if it's not in the cache
*/
Record getImageRecord(PDFPage page, ImageInfo info) {
// first find the relevant page record
Integer pageNumber = new Integer(page.getPageNumber());
// System.out.println("Request for image on page " + pageNumber);
PageRecord pageRec = getPageRecord(pageNumber);
if (pageRec != null) {
SoftReference ref = pageRec.images.get(info);
if (ref != null) {
String val = (ref.get() == null) ? " not in " : " in ";
// System.out.println("Image on page " + pageNumber + val + " cache");
return (Record) ref.get();
}
}
// System.out.println("Image on page " + pageNumber + " not in cache");
// not found
return null;
}
/**
* Remove an image's record from the cache
*/
Record removeImageRecord(PDFPage page, ImageInfo info) {
// first find the relevant page record
Integer pageNumber = new Integer(page.getPageNumber());
PageRecord pageRec = getPageRecord(pageNumber);
if (pageRec != null) {
SoftReference ref = pageRec.images.remove(info);
if (ref != null) {
return (Record) ref.get();
}
}
return null;
}
/** the basic information about a page or image */
class Record {
/** the page or image itself */
Object value;
/** the thing generating the page, or null if done/not provided */
BaseWatchable generator;
}
/** the record stored for each page in the cache */
class PageRecord extends Record {
/** any images associated with the page */
Map<ImageInfo, SoftReference<Record>> images;
/** create a new page record */
public PageRecord() {
images = Collections.synchronizedMap(new HashMap<ImageInfo, SoftReference<Record>>());
}
}
}

View File

@ -0,0 +1,86 @@
/*
* $Id: HexDump.java,v 1.3 2009/01/16 16:26:12 tomoke Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview;
import java.io.IOException;
import java.io.RandomAccessFile;
public class HexDump {
public static void printData(byte[] data) {
char[] parts = new char[17];
int partsloc = 0;
for (int i = 0; i < data.length; i++) {
int d = ((int) data[i]) & 0xff;
if (d == 0) {
parts[partsloc++] = '.';
} else if (d < 32 || d >= 127) {
parts[partsloc++] = '?';
} else {
parts[partsloc++] = (char) d;
}
if (i % 16 == 0) {
int start = Integer.toHexString(data.length).length();
int end = Integer.toHexString(i).length();
for (int j = start; j > end; j--) {
System.out.print("0");
}
System.out.print(Integer.toHexString(i) + ": ");
}
if (d < 16) {
System.out.print("0" + Integer.toHexString(d));
} else {
System.out.print(Integer.toHexString(d));
}
if ((i & 15) == 15 || i == data.length - 1) {
System.out.println(" " + new String(parts));
partsloc = 0;
} else if ((i & 7) == 7) {
System.out.print(" ");
parts[partsloc++] = ' ';
} else if ((i & 1) == 1) {
System.out.print(" ");
}
}
System.out.println();
}
public static void main(String args[]) {
if (args.length != 1) {
System.out.println("Usage: ");
System.out.println(" HexDump <filename>");
System.exit(-1);
}
try {
RandomAccessFile raf = new RandomAccessFile(args[0], "r");
int size = (int) raf.length();
byte[] data = new byte[size];
raf.readFully(data);
printData(data);
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}

View File

@ -0,0 +1,65 @@
/*
* Copyright 2008 Pirion Systems Pty Ltd, 139 Warry St,
* Fortitude Valley, Queensland, Australia
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview;
import com.sun.pdfview.PDFStringUtil;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CoderResult;
import java.nio.charset.Charset;
import java.nio.CharBuffer;
import java.nio.ByteBuffer;
import java.util.Map;
import java.util.HashMap;
/**
* A {@link CharsetEncoder} that attempts to write out the lower 8 bits
* of any character. Characters &gt;= 256 in value are regarded
* as unmappable.
*
* @author Luke Kirby
*/
public class Identity8BitCharsetEncoder extends CharsetEncoder {
public Identity8BitCharsetEncoder() {
super(null, 1, 1);
}
protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) {
while (in.remaining() > 0) {
if (out.remaining() < 1) {
return CoderResult.OVERFLOW;
}
final char c = in.get();
if (c >= 0 && c < 256) {
out.put((byte) c);
} else {
return CoderResult.unmappableForLength(1);
}
}
return CoderResult.UNDERFLOW;
}
@Override
public boolean isLegalReplacement(byte[] repl) {
// avoid referencing the non-existent character set
return true;
}
}

View File

@ -0,0 +1,78 @@
/*
* $Id: ImageInfo.java,v 1.3 2009/01/16 16:26:11 tomoke Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview;
import android.graphics.Color;
import android.graphics.RectF;
public class ImageInfo {
int width;
int height;
RectF clip;
int bgColor;
public ImageInfo(int width, int height, RectF clip) {
this(width, height, clip, Color.WHITE);
}
public ImageInfo(int width, int height, RectF clip, int bgColor) {
this.width = width;
this.height = height;
this.clip = clip;
this.bgColor = bgColor;
}
// a hashcode that uses width, height and clip to generate its number
@Override
public int hashCode() {
int code = (width ^ height << 16);
if (clip != null) {
code ^= ((int) clip.width() | (int) clip.height()) << 8;
code ^= ((int) clip.left | (int) clip.top);
}
return code;
}
// an equals method that compares values
@Override
public boolean equals(Object o) {
if (!(o instanceof ImageInfo)) {
return false;
}
ImageInfo ii = (ImageInfo) o;
if (width != ii.width || height != ii.height) {
return false;
} else if (clip != null && ii.clip != null) {
return clip.equals(ii.clip);
} else if (clip == null && ii.clip == null) {
return true;
} else {
return false;
}
}
}

View File

@ -0,0 +1,126 @@
/*
* $Id: NameTree.java,v 1.3 2009/01/16 16:26:09 tomoke Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview;
import java.io.IOException;
/**
* A PDF name tree consists of three kinds of nodes:
* <ul>
* <li> The root node contains only a kids entry, pointing to many
* other objects
* <li> An intermediate node contains the limits of all the children in
* its subtree, and a kids entry for each child
* <li> A leaf node contains a set of name-to-object mappings in a dictionary,
* as well as the limits of the data contained in that child.
* </ul>
* A PDF name tree is sorted in accordance with the String.compareTo() method.
*/
public class NameTree {
/** the root object */
private PDFObject root;
/** Creates a new instance of NameTree */
public NameTree(PDFObject root) {
this.root = root;
}
/**
* Find the PDF object corresponding to the given String in a name tree
*
* @param key the key we are looking for in the name tree
* @return the object associated with str, if found, or null if not
*/
public PDFObject find(String key) throws IOException {
return find(root, key);
}
/**
* Recursively walk the name tree looking for a given value
*/
private PDFObject find(PDFObject root, String key)
throws IOException {
// first, look for a Names entry, meaning this is a leaf
PDFObject names = root.getDictRef("Names");
if (names != null) {
return findInArray(names.getArray(), key);
}
// no names given, look for kids
PDFObject kidsObj = root.getDictRef("Kids");
if (kidsObj != null) {
PDFObject[] kids = kidsObj.getArray();
for (int i = 0; i < kids.length; i++) {
// find the limits of this kid
PDFObject limitsObj = kids[i].getDictRef("Limits");
if (limitsObj != null) {
String lowerLimit = limitsObj.getAt(0).getStringValue();
String upperLimit = limitsObj.getAt(1).getStringValue();
// are we in range?
if ((key.compareTo(lowerLimit) >= 0) &&
(key.compareTo(upperLimit) <= 0)) {
// we are, so find in this child
return find(kids[i], key);
}
}
}
}
// no luck
return null;
}
/**
* Find an object in a (key,value) array. Do this by splitting in half
* repeatedly.
*/
private PDFObject findInArray(PDFObject[] array, String key)
throws IOException {
int start = 0;
int end = array.length / 2;
while (end >= start && start >= 0 && end < array.length) {
// find the key at the midpoint
int pos = start + ((end - start) / 2);
String posKey = array[pos * 2].getStringValue();
// compare the key to the key we are looking for
int comp = key.compareTo(posKey);
if (comp == 0) {
// they match. Return the value
return array[(pos * 2) + 1];
} else if (comp > 0) {
// too big, search the top half of the tree
start = pos + 1;
} else if (comp < 0) {
// too small, search the bottom half of the tree
end = pos - 1;
}
}
// not found
return null;
}
}

View File

@ -0,0 +1,62 @@
/*
* $Id: OutlineNode.java,v 1.3 2009/01/16 16:26:10 tomoke Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview;
import androswing.tree.DefaultMutableTreeNode;
import com.sun.pdfview.action.PDFAction;
public class OutlineNode extends DefaultMutableTreeNode {
// the name of this node
private String title;
/**
* Create a new outline node
*
* @param title the node's visible name in the tree
*/
public OutlineNode(String title) {
this.title = title;
}
/**
* Get the PDF action associated with this node
*/
public PDFAction getAction() {
return (PDFAction) getUserObject();
}
/**
* Set the PDF action associated with this node
*/
public void setAction(PDFAction action) {
setUserObject(action);
}
/**
* Return the node's visible name in the tree
*/
@Override
public String toString() {
return title;
}
}

View File

@ -0,0 +1,61 @@
/*
* $Id: PDFCmd.java,v 1.3 2009/01/16 16:26:13 tomoke Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview;
import android.graphics.RectF;
/**
* The abstract superclass of all drawing commands for a PDFPage.
* @author Mike Wessler
*/
public abstract class PDFCmd {
/**
* mark the page or change the graphics state
* @param state the current graphics state; may be modified during
* execution.
* @return the region of the page made dirty by executing this command
* or null if no region was touched. Note this value should be
* in the coordinates of the image touched, not the page.
*/
public abstract RectF execute(PDFRenderer state);
/**
* a human readable representation of this command
*/
@Override
public String toString() {
String name = getClass().getName();
int lastDot = name.lastIndexOf('.');
if (lastDot >= 0) {
return name.substring(lastDot + 1);
} else {
return name;
}
}
/**
* the details of this command
*/
public String getDetails() {
return super.toString();
}
}

View File

@ -0,0 +1,284 @@
/*
* $Id: PDFDestination.java,v 1.3 2009/01/16 16:26:09 tomoke Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview;
import java.io.IOException;
/**
* Represents a destination in a PDF file. Destinations take 3 forms:
* <ul>
* <li> An explicit destination, which contains a reference to a page
* as well as some stuff about how to fit it into the window.
* <li> A named destination, which uses the PDF file's Dests entry
* in the document catalog to map a name to an explicit destination
* <li> A string destintation, which uses the PDF file's Dests entry.
* in the name directory to map a string to an explicit destination.
* </ul>
*
* All three of these cases are handled by the getDestination() method.
*/
public class PDFDestination {
/** The known types of destination */
public static final int XYZ = 0;
public static final int FIT = 1;
public static final int FITH = 2;
public static final int FITV = 3;
public static final int FITR = 4;
public static final int FITB = 5;
public static final int FITBH = 6;
public static final int FITBV = 7;
/** the type of this destination (from the list above) */
private int type;
/** the page we refer to */
private PDFObject pageObj;
/** the left coordinate of the fit area, if applicable */
private float left;
/** the right coordinate of the fit area, if applicable */
private float right;
/** the top coordinate of the fit area, if applicable */
private float top;
/** the bottom coordinate of the fit area, if applicable */
private float bottom;
/** the zoom, if applicable */
private float zoom;
/**
* Creates a new instance of PDFDestination
*
* @param pageObj the page object this destination refers to
* @param type the type of page this object refers to
*/
protected PDFDestination(PDFObject pageObj, int type) {
this.pageObj = pageObj;
this.type = type;
}
/**
* Get a destination from either an array (explicit destination), a
* name (named destination) or a string (name tree destination).
*
* @param obj the PDFObject representing this destination
* @param root the root of the PDF object tree
*/
public static PDFDestination getDestination(PDFObject obj, PDFObject root)
throws IOException {
// resolve string and name issues
if (obj.getType() == PDFObject.NAME) {
obj = getDestFromName(obj, root);
} else if (obj.getType() == PDFObject.STRING) {
obj = getDestFromString(obj, root);
}
// make sure we have the right kind of object
if (obj == null || obj.getType() != PDFObject.ARRAY) {
throw new PDFParseException("Can't create destination from: " + obj);
}
// the array is in the form [page type args ... ]
PDFObject[] destArray = obj.getArray();
// create the destination based on the type
PDFDestination dest = null;
String type = destArray[1].getStringValue();
if (type.equals("XYZ")) {
dest = new PDFDestination(destArray[0], XYZ);
} else if (type.equals("Fit")) {
dest = new PDFDestination(destArray[0], FIT);
} else if (type.equals("FitH")) {
dest = new PDFDestination(destArray[0], FITH);
} else if (type.equals("FitV")) {
dest = new PDFDestination(destArray[0], FITV);
} else if (type.equals("FitR")) {
dest = new PDFDestination(destArray[0], FITR);
} else if (type.equals("FitB")) {
dest = new PDFDestination(destArray[0], FITB);
} else if (type.equals("FitBH")) {
dest = new PDFDestination(destArray[0], FITBH);
} else if (type.equals("FitBV")) {
dest = new PDFDestination(destArray[0], FITBV);
} else {
throw new PDFParseException("Unknown destination type: " + type);
}
// now fill in the arguments based on the type
switch (dest.getType()) {
case XYZ:
dest.setLeft(destArray[2].getFloatValue());
dest.setTop(destArray[3].getFloatValue());
dest.setZoom(destArray[4].getFloatValue());
break;
case FITH:
dest.setTop(destArray[2].getFloatValue());
break;
case FITV:
dest.setLeft(destArray[2].getFloatValue());
break;
case FITR:
dest.setLeft(destArray[2].getFloatValue());
dest.setBottom(destArray[3].getFloatValue());
dest.setRight(destArray[4].getFloatValue());
dest.setTop(destArray[5].getFloatValue());
break;
case FITBH:
dest.setTop(destArray[2].getFloatValue());
break;
case FITBV:
dest.setLeft(destArray[2].getFloatValue());
break;
}
return dest;
}
/**
* Get the type of this destination
*/
public int getType() {
return type;
}
/**
* Get the PDF Page object associated with this destination
*/
public PDFObject getPage() {
return pageObj;
}
/**
* Get the left coordinate value
*/
public float getLeft() {
return left;
}
/**
* Set the left coordinate value
*/
public void setLeft(float left) {
this.left = left;
}
/**
* Get the right coordinate value
*/
public float getRight() {
return right;
}
/**
* Set the right coordinate value
*/
public void setRight(float right) {
this.right = right;
}
/**
* Get the top coordinate value
*/
public float getTop() {
return top;
}
/**
* Set the top coordinate value
*/
public void setTop(float top) {
this.top = top;
}
/**
* Get the bottom coordinate value
*/
public float getBottom() {
return bottom;
}
/**
* Set the bottom coordinate value
*/
public void setBottom(float bottom) {
this.bottom = bottom;
}
/**
* Get the zoom value
*/
public float getZoom() {
return zoom;
}
/**
* Set the zoom value
*/
public void setZoom(float zoom) {
this.zoom = zoom;
}
/**
* Get a destination, given a name. This means the destination is in
* the root node's dests dictionary.
*/
private static PDFObject getDestFromName(PDFObject name, PDFObject root)
throws IOException {
// find the dests object in the root node
PDFObject dests = root.getDictRef("Dests");
if (dests != null) {
// find this name in the dests dictionary
return dests.getDictRef(name.getStringValue());
}
// not found
return null;
}
/**
* Get a destination, given a string. This means the destination is in
* the root node's names dictionary.
*/
private static PDFObject getDestFromString(PDFObject str, PDFObject root)
throws IOException {
// find the names object in the root node
PDFObject names = root.getDictRef("Names");
if (names != null) {
// find the dests entry in the names dictionary
PDFObject dests = names.getDictRef("Dests");
if (dests != null) {
// create a name tree object
NameTree tree = new NameTree(dests);
// find the value we're looking for
PDFObject obj = tree.find(str.getStringValue());
// if we get back a dictionary, look for the /D value
if (obj != null && obj.getType() == PDFObject.DICTIONARY) {
obj = obj.getDictRef("D");
}
// found it
return obj;
}
}
// not found
return null;
}
}

View File

@ -0,0 +1,106 @@
/*
* Copyright 2008 Pirion Systems Pty Ltd, 139 Warry St,
* Fortitude Valley, Queensland, Australia
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CoderResult;
import java.util.HashMap;
import java.util.Map;
/**
* Encodes into a PDFDocEncoding representation. Note that only 256 characters
* (if that) are represented in the PDFDocEncoding, so users should be
* prepared to deal with unmappable character exceptions.
*
* @see "PDF Reference version 1.7, Appendix D"
*
* @author Luke Kirby
*/
public class PDFDocCharsetEncoder extends CharsetEncoder {
/**
* Identify whether a particular character preserves the same byte value
* upon encoding in PDFDocEncoding
* @param ch the character
* @return whether the character is identity encoded
*/
public static boolean isIdentityEncoding(char ch) {
return ch >= 0 && ch <= 255 && IDENT_PDF_DOC_ENCODING_MAP[ch];
}
/**
* For each character that exists in PDFDocEncoding, identifies whether
* the byte value in UTF-16BE is the same as it is in PDFDocEncoding
*/
final static boolean[] IDENT_PDF_DOC_ENCODING_MAP = new boolean[256];
/**
* For non-identity encoded characters, maps from the character to
* the byte value in PDFDocEncoding. If an entry for a non-identity
* coded character is absent from this map, that character is unmappable
* in the PDFDocEncoding.
*/
final static Map<Character,Byte> EXTENDED_TO_PDF_DOC_ENCODING_MAP =
new HashMap<Character,Byte>();
static
{
for (byte i = 0; i < PDFStringUtil.PDF_DOC_ENCODING_MAP.length; ++i) {
final char c = PDFStringUtil.PDF_DOC_ENCODING_MAP[i];
final boolean identical = (c == i);
IDENT_PDF_DOC_ENCODING_MAP[i] = identical;
if (!identical) {
EXTENDED_TO_PDF_DOC_ENCODING_MAP.put(c, i);
}
}
}
public PDFDocCharsetEncoder() {
super(null, 1, 1);
}
protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) {
while (in.remaining() > 0) {
if (out.remaining() < 1) {
return CoderResult.OVERFLOW;
}
final char c = in.get();
if (c >= 0 && c < 256 && IDENT_PDF_DOC_ENCODING_MAP[c]) {
out.put((byte) c);
} else {
final Byte mapped = EXTENDED_TO_PDF_DOC_ENCODING_MAP.get(c);
if (mapped != null) {
out.put(mapped);
} else {
return CoderResult.unmappableForLength(1);
}
}
}
return CoderResult.UNDERFLOW;
}
@Override
public boolean isLegalReplacement(byte[] repl) {
// avoid referencing the non-existent character set
return true;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,710 @@
/*
* $Id: PDFImage.java,v 1.9 2009/03/12 13:23:54 tomoke Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
import com.sun.pdfview.colorspace.PDFColorSpace;
import com.sun.pdfview.function.FunctionType0;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Bitmap.Config;
import android.util.Log;
/**
* Encapsulates a PDF Image
*/
public class PDFImage {
private static final String TAG = "AWTPDF.pdfimage";
public static boolean sShowImages;
public static void dump(PDFObject obj) throws IOException {
p("dumping PDF object: " + obj);
if (obj == null) {
return;
}
HashMap dict = obj.getDictionary();
p(" dict = " + dict);
for (Object key : dict.keySet()) {
p("key = " + key + " value = " + dict.get(key));
}
}
public static void p(String string) {
System.out.println(string);
}
/** color key mask. Array of start/end pairs of ranges of color components to
* mask out. If a component falls within any of the ranges it is clear. */
private int[] colorKeyMask = null;
/** the width of this image in pixels */
private int width;
/** the height of this image in pixels */
private int height;
/** the colorspace to interpret the samples in */
private PDFColorSpace colorSpace;
/** the number of bits per sample component */
private int bpc;
/** whether this image is a mask or not */
private boolean imageMask = false;
/** the SMask image, if any */
private PDFImage sMask;
/** the decode array */
private float[] decode;
/** the actual image data */
private PDFObject imageObj;
/**
* Create an instance of a PDFImage
*/
protected PDFImage(PDFObject imageObj) {
this.imageObj = imageObj;
}
/**
* Read a PDFImage from an image dictionary and stream
*
* @param obj the PDFObject containing the image's dictionary and stream
* @param resources the current resources
*/
public static PDFImage createImage(PDFObject obj, Map resources)
throws IOException {
// create the image
PDFImage image = new PDFImage(obj);
// get the width (required)
PDFObject widthObj = obj.getDictRef("Width");
if (widthObj == null) {
throw new PDFParseException("Unable to read image width: " + obj);
}
image.setWidth(widthObj.getIntValue());
// get the height (required)
PDFObject heightObj = obj.getDictRef("Height");
if (heightObj == null) {
throw new PDFParseException("Unable to get image height: " + obj);
}
image.setHeight(heightObj.getIntValue());
// figure out if we are an image mask (optional)
PDFObject imageMaskObj = obj.getDictRef("ImageMask");
if (imageMaskObj != null) {
image.setImageMask(imageMaskObj.getBooleanValue());
}
// read the bpc and colorspace (required except for masks)
if (image.isImageMask()) {
image.setBitsPerComponent(1);
// create the indexed color space for the mask
// [PATCHED by michal.busta@gmail.com] - default value od Decode according to PDF spec. is [0, 1]
// so the color arry should be:
int[] colors = {Color.BLACK, Color.WHITE};
PDFObject imageMaskDecode = obj.getDictRef("Decode");
if (imageMaskDecode != null) {
PDFObject[] array = imageMaskDecode.getArray();
float decode0 = array[0].getFloatValue();
if (decode0 == 1.0f) {
colors = new int[]{Color.WHITE, Color.BLACK};
}
}
// TODO [FHe]: support for indexed colorspace
image.setColorSpace(PDFColorSpace.getColorSpace(PDFColorSpace.COLORSPACE_GRAY));
// image.setColorSpace(new IndexedColor(colors));
} else {
// get the bits per component (required)
PDFObject bpcObj = obj.getDictRef("BitsPerComponent");
if (bpcObj == null) {
throw new PDFParseException("Unable to get bits per component: " + obj);
}
image.setBitsPerComponent(bpcObj.getIntValue());
// get the color space (required)
PDFObject csObj = obj.getDictRef("ColorSpace");
if (csObj == null) {
throw new PDFParseException("No ColorSpace for image: " + obj);
}
PDFColorSpace cs = PDFColorSpace.getColorSpace(csObj, resources);
image.setColorSpace(cs);
}
// read the decode array
PDFObject decodeObj = obj.getDictRef("Decode");
if (decodeObj != null) {
PDFObject[] decodeArray = decodeObj.getArray();
float[] decode = new float[decodeArray.length];
for (int i = 0; i < decodeArray.length; i++) {
decode[i] = decodeArray[i].getFloatValue();
}
image.setDecode(decode);
}
// read the soft mask.
// If ImageMask is true, this entry must not be present.
// (See implementation note 52 in Appendix H.)
if (imageMaskObj == null) {
PDFObject sMaskObj = obj.getDictRef("SMask");
if (sMaskObj == null) {
// try the explicit mask, if there is no SoftMask
sMaskObj = obj.getDictRef("Mask");
}
if (sMaskObj != null) {
if (sMaskObj.getType() == PDFObject.STREAM) {
try {
PDFImage sMaskImage = PDFImage.createImage(sMaskObj, resources);
image.setSMask(sMaskImage);
} catch (IOException ex) {
p("ERROR: there was a problem parsing the mask for this object");
dump(obj);
ex.printStackTrace(System.out);
}
} else if (sMaskObj.getType() == PDFObject.ARRAY) {
// retrieve the range of the ColorKeyMask
// colors outside this range will not be painted.
try {
image.setColorKeyMask(sMaskObj);
} catch (IOException ex) {
p("ERROR: there was a problem parsing the color mask for this object");
dump(obj);
ex.printStackTrace(System.out);
}
}
}
}
return image;
}
/**
* Get the image that this PDFImage generates.
*
* @return a buffered image containing the decoded image data
*/
public Bitmap getImage() {
try {
Bitmap bi = (Bitmap) imageObj.getCache();
if (bi == null) {
if (!sShowImages)
throw new UnsupportedOperationException("do not show images");
byte[] imgBytes = imageObj.getStream();
bi = parseData(imgBytes);
// TODO [FHe]: is the cache useful on Android?
imageObj.setCache(bi);
}
// if(bi != null)
// ImageIO.write(bi, "png", new File("/tmp/test/" + System.identityHashCode(this) + ".png"));
return bi;
} catch (IOException ioe) {
System.out.println("Error reading image");
ioe.printStackTrace();
return null;
} catch (OutOfMemoryError e) {
// fix for too large images
Log.e(TAG, "image too large (OutOfMemoryError)");
int size = 15;
int max = size-1;
int half = size/2-1;
Bitmap bi = Bitmap.createBitmap(size, size, Config.RGB_565);
Canvas c = new Canvas(bi);
c.drawColor(Color.RED);
Paint p = new Paint();
p.setColor(Color.WHITE);
c.drawLine(0, 0, max, max, p);
c.drawLine(0, max, max, 0, p);
c.drawLine(half, 0, half, max, p);
c.drawLine(0, half, max, half, p);
return bi;
}
}
private Bitmap parseData(byte[] imgBytes) {
Bitmap bi;
long startTime = System.currentTimeMillis();
// parse the stream data into an actual image
Log.i(TAG, "Creating Image width="+getWidth() + ", Height="+getHeight()+", bpc="+getBitsPerComponent()+",cs="+colorSpace);
if (colorSpace == null) {
throw new UnsupportedOperationException("image without colorspace");
} else if (colorSpace.getType() == PDFColorSpace.COLORSPACE_RGB) {
int maxH = getHeight();
int maxW = getWidth();
if (imgBytes.length == 2*maxW*maxH) {
// decoded JPEG as RGB565
bi = Bitmap.createBitmap(maxW, maxH, Config.RGB_565);
bi.copyPixelsFromBuffer(ByteBuffer.wrap(imgBytes));
}
else {
// create RGB image
bi = Bitmap.createBitmap(getWidth(), getHeight(), Config.ARGB_8888);
int[] line = new int[maxW];
int n=0;
for (int h = 0; h<maxH; h++) {
for (int w = 0; w<getWidth(); w++) {
line[w] = ((0xff&(int)imgBytes[n])<<8|(0xff&(int)imgBytes[n+1]))<<8|(0xff&(int)imgBytes[n+2])|0xFF000000;
// line[w] = Color.rgb(0xff&(int)imgBytes[n], 0xff&(int)imgBytes[n+1],0xff&(int)imgBytes[n+2]);
n+=3;
}
bi.setPixels(line, 0, maxW, 0, h, maxW, 1);
}
}
}
else if (colorSpace.getType() == PDFColorSpace.COLORSPACE_GRAY) {
// create gray image
bi = Bitmap.createBitmap(getWidth(), getHeight(), Config.ARGB_8888);
int maxH = getHeight();
int maxW = getWidth();
int[] line = new int[maxW];
int n=0;
for (int h = 0; h<maxH; h++) {
for (int w = 0; w<getWidth(); w++) {
int gray = 0xff&(int)imgBytes[n];
line[w] = (gray<<8|gray)<<8|gray|0xFF000000;
n+=1;
}
bi.setPixels(line, 0, maxW, 0, h, maxW, 1);
}
}
else if (colorSpace.getType() == PDFColorSpace.COLORSPACE_INDEXED) {
// create indexed image
bi = Bitmap.createBitmap(getWidth(), getHeight(), Config.ARGB_8888);
int maxH = getHeight();
int maxW = getWidth();
int[] line = new int[maxW];
int[] comps = new int[1];
int n=0;
for (int h = 0; h<maxH; h++) {
for (int w = 0; w<getWidth(); w++) {
comps[0] = imgBytes[n]&0xff;
line[w] = colorSpace.toColor(comps);
n+=1;
}
bi.setPixels(line, 0, maxW, 0, h, maxW, 1);
}
}
else {
throw new UnsupportedOperationException("image with unsupported colorspace "+colorSpace);
}
long stopTime = System.currentTimeMillis();
Log.i(TAG, "millis for converting image="+(stopTime-startTime));
return bi;
}
// /**
// * <p>Parse the image stream into a buffered image. Note that this is
// * guaranteed to be called after all the other setXXX methods have been
// * called.</p>
// *
// * <p>NOTE: the color convolving is extremely slow on large images.
// * It would be good to see if it could be moved out into the rendering
// * phases, where we might be able to scale the image down first.</p
// */
// protected Bitmap parseData(byte[] data) {
//// String hex;
//// String name;
//// synchronized (System.out) {
//// System.out.println("\n\n" + name + ": " + data.length);
//// for (int i = 0; i < data.length; i++) {
//// hex = "0x" + Integer.toHexString(0xFF & data[i]);
//// System.out.print(hex);
//// if (i < data.length - 1) {
//// System.out.print(", ");
//// }
//// if ((i + 1) % 25 == 0) {
//// System.out.print("\n");
//// }
//// }
//// System.out.println("\n");
//// System.out.flush();
//// }
// // create the data buffer
// DataBuffer db = new DataBufferByte(data, data.length);
//
// // pick a color model, based on the number of components and
// // bits per component
// ColorModel cm = getColorModel();
//
// // create a compatible raster
// SampleModel sm =
// cm.createCompatibleSampleModel (getWidth (), getHeight ());
// WritableRaster raster;
// try {
// raster =
// Raster.createWritableRaster (sm, db, new Point (0, 0));
// } catch (RasterFormatException e) {
// int tempExpectedSize =
// getWidth () * getHeight () *
// getColorSpace ().getNumComponents () *
// getBitsPerComponent () / 8;
// if (tempExpectedSize > data.length) {
// byte[] tempLargerData = new byte[tempExpectedSize];
// System.arraycopy (data, 0, tempLargerData, 0, data.length);
// db = new DataBufferByte (tempLargerData, tempExpectedSize);
// raster =
// Raster.createWritableRaster (sm, db, new Point (0, 0));
// } else {
// throw e;
// }
// }
//
// /*
// * Workaround for a bug on the Mac -- a class cast exception in
// * drawImage() due to the wrong data buffer type (?)
// */
// BufferedImage bi = null;
// if (cm instanceof IndexColorModel) {
// IndexColorModel icm = (IndexColorModel) cm;
//
// // choose the image type based on the size
// int type = BufferedImage.TYPE_BYTE_BINARY;
// if (getBitsPerComponent() == 8) {
// type = BufferedImage.TYPE_BYTE_INDEXED;
// }
//
// // create the image with an explicit indexed color model.
// bi = new BufferedImage(getWidth(), getHeight(), type, icm);
//
// // set the data explicitly as well
// bi.setData(raster);
// } else {
// bi = new BufferedImage(cm, raster, true, null);
// }
//
// // hack to avoid *very* slow conversion
// ColorSpace cs = cm.getColorSpace();
// ColorSpace rgbCS = ColorSpace.getInstance(ColorSpace.CS_sRGB);
//
// // add in the alpha data supplied by the SMask, if any
// PDFImage sMaskImage = getSMask();
// if (sMaskImage != null) {
// BufferedImage si = sMaskImage.getImage();
//
// BufferedImage outImage = new BufferedImage(getWidth(),
// getHeight(), BufferedImage.TYPE_INT_ARGB);
//
// int[] srcArray = new int[width];
// int[] maskArray = new int[width];
//
// for (int i = 0; i < height; i++) {
// bi.getRGB(0, i, width, 1, srcArray, 0, width);
// si.getRGB(0, i, width, 1, maskArray, 0, width);
//
// for (int j = 0; j < width; j++) {
// int ac = 0xff000000;
//
// maskArray[j] = ((maskArray[j] & 0xff) << 24) | (srcArray[j] & ~ac);
// }
//
// outImage.setRGB(0, i, width, 1, maskArray, 0, width);
// }
//
// bi = outImage;
// }
//
// return (bi);
// }
/**
* Get the image's width
*/
public int getWidth() {
return width;
}
/**
* Set the image's width
*/
protected void setWidth(int width) {
this.width = width;
}
/**
* Get the image's height
*/
public int getHeight() {
return height;
}
/**
* Set the image's height
*/
protected void setHeight(int height) {
this.height = height;
}
/**
* set the color key mask. It is an array of start/end entries
* to indicate ranges of color indicies that should be masked out.
*
* @param maskArrayObject
*/
private void setColorKeyMask(PDFObject maskArrayObject) throws IOException {
PDFObject[] maskObjects = maskArrayObject.getArray();
colorKeyMask = null;
int[] masks = new int[maskObjects.length];
for (int i = 0; i < masks.length; i++) {
masks[i] = maskObjects[i].getIntValue();
}
colorKeyMask = masks;
}
/**
* Get the colorspace associated with this image, or null if there
* isn't one
*/
protected PDFColorSpace getColorSpace() {
return colorSpace;
}
/**
* Set the colorspace associated with this image
*/
protected void setColorSpace(PDFColorSpace colorSpace) {
this.colorSpace = colorSpace;
}
/**
* Get the number of bits per component sample
*/
protected int getBitsPerComponent() {
return bpc;
}
/**
* Set the number of bits per component sample
*/
protected void setBitsPerComponent(int bpc) {
this.bpc = bpc;
}
/**
* Return whether or not this is an image mask
*/
public boolean isImageMask() {
return imageMask;
}
/**
* Set whether or not this is an image mask
*/
public void setImageMask(boolean imageMask) {
this.imageMask = imageMask;
}
/**
* Return the soft mask associated with this image
*/
public PDFImage getSMask() {
return sMask;
}
/**
* Set the soft mask image
*/
protected void setSMask(PDFImage sMask) {
this.sMask = sMask;
}
/**
* Get the decode array
*/
protected float[] getDecode() {
return decode;
}
/**
* Set the decode array
*/
protected void setDecode(float[] decode) {
this.decode = decode;
}
// /**
// * get a Java ColorModel consistent with the current color space,
// * number of bits per component and decode array
// *
// * @param bpc the number of bits per component
// */
// private ColorModel getColorModel() {
// PDFColorSpace cs = getColorSpace();
//
// if (cs instanceof IndexedColor) {
// IndexedColor ics = (IndexedColor) cs;
//
// byte[] components = ics.getColorComponents();
// int num = ics.getCount();
//
// // process the decode array
// if (decode != null) {
// byte[] normComps = new byte[components.length];
//
// // move the components array around
// for (int i = 0; i < num; i++) {
// byte[] orig = new byte[1];
// orig[0] = (byte) i;
//
// float[] res = normalize(orig, null, 0);
// int idx = (int) res[0];
//
// normComps[i * 3] = components[idx * 3];
// normComps[(i * 3) + 1] = components[(idx * 3) + 1];
// normComps[(i * 3) + 2] = components[(idx * 3) + 2];
// }
//
// components = normComps;
// }
//
// // make sure the size of the components array is 2 ^ numBits
// // since if it's not, Java will complain
// int correctCount = 1 << getBitsPerComponent();
// if (correctCount < num) {
// byte[] fewerComps = new byte[correctCount * 3];
//
// System.arraycopy(components, 0, fewerComps, 0, correctCount * 3);
//
// components = fewerComps;
// num = correctCount;
// }
// if (colorKeyMask == null || colorKeyMask.length == 0) {
// return new IndexColorModel(getBitsPerComponent(), num, components,
// 0, false);
// } else {
// byte[] aComps = new byte[num * 4];
// int idx = 0;
// for (int i = 0; i < num; i++) {
// aComps[idx++] = components[(i * 3)];
// aComps[idx++] = components[(i * 3) + 1];
// aComps[idx++] = components[(i * 3) + 2];
// aComps[idx++] = (byte) 0xFF;
// }
// for (int i = 0; i < colorKeyMask.length; i += 2) {
// for (int j = colorKeyMask[i]; j <= colorKeyMask[i + 1]; j++) {
// aComps[(j * 4) + 3] = 0; // make transparent
// }
// }
// return new IndexColorModel(getBitsPerComponent(), num, aComps,
// 0, true);
// }
// } else {
// int[] bits = new int[cs.getNumComponents()];
// for (int i = 0; i < bits.length; i++) {
// bits[i] = getBitsPerComponent();
// }
//
// return new DecodeComponentColorModel(cs.getColorSpace(), bits);
// }
// }
/**
* Normalize an array of values to match the decode array
*/
private float[] normalize(byte[] pixels, float[] normComponents,
int normOffset) {
if (normComponents == null) {
normComponents = new float[normOffset + pixels.length];
}
float[] decodeArray = getDecode();
for (int i = 0; i < pixels.length; i++) {
int val = pixels[i] & 0xff;
int pow = ((int) Math.pow(2, getBitsPerComponent())) - 1;
float ymin = decodeArray[i * 2];
float ymax = decodeArray[(i * 2) + 1];
normComponents[normOffset + i] =
FunctionType0.interpolate(val, 0, pow, ymin, ymax);
}
return normComponents;
}
// /**
// * A wrapper for ComponentColorSpace which normalizes based on the
// * decode array.
// */
// class DecodeComponentColorModel extends ComponentColorModel {
//
// public DecodeComponentColorModel(ColorSpace cs, int[] bpc) {
// super(cs, bpc, false, false, Transparency.OPAQUE,
// DataBuffer.TYPE_BYTE);
//
// if (bpc != null) {
// pixel_bits = bpc.length * bpc[0];
// }
// }
//
// @Override
// public SampleModel createCompatibleSampleModel(int width, int height) {
// // workaround -- create a MultiPixelPackedSample models for
// // single-sample, less than 8bpp color models
// if (getNumComponents() == 1 && getPixelSize() < 8) {
// return new MultiPixelPackedSampleModel(getTransferType(),
// width,
// height,
// getPixelSize());
// }
//
// return super.createCompatibleSampleModel(width, height);
// }
//
// @Override
// public boolean isCompatibleRaster(Raster raster) {
// if (getNumComponents() == 1 && getPixelSize() < 8) {
// SampleModel sm = raster.getSampleModel();
//
// if (sm instanceof MultiPixelPackedSampleModel) {
// return (sm.getSampleSize(0) == getPixelSize());
// } else {
// return false;
// }
// }
//
// return super.isCompatibleRaster(raster);
// }
//
// @Override
// public float[] getNormalizedComponents(Object pixel,
// float[] normComponents, int normOffset) {
// if (getDecode() == null) {
// return super.getNormalizedComponents(pixel, normComponents,
// normOffset);
// }
//
// return normalize((byte[]) pixel, normComponents, normOffset);
// }
// }
}

View File

@ -0,0 +1,200 @@
/*
* $Id: PDFShapeCmd.java,v 1.3 2009/01/16 16:26:15 tomoke Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview;
import android.graphics.Matrix;
import android.graphics.Path;
import android.graphics.RectF;
import android.graphics.Paint.FontMetrics;
import android.util.Log;
/**
* Encapsulates a path. Also contains extra fields and logic to check
* for consecutive abutting anti-aliased regions. We stroke the shared
* line between these regions again with a 1-pixel wide line so that
* the background doesn't show through between them.
*
* @author Mike Wessler
*/
public class PDFNativeTextCmd extends PDFCmd {
private static final String TAG = "ANDPDF.natTXT";
/** stroke the outline of the path with the stroke paint */
public static final int STROKE = 1;
/** fill the path with the fill paint */
public static final int FILL = 2;
/** perform both stroke and fill */
public static final int BOTH = 3;
/** set the clip region to the path */
public static final int CLIP = 4;
/** the style */
private int style;
/** the bounding box of the path */
private RectF bounds;
private Matrix mat;
private float x;
private float y;
private float w;
private float h;
private String text;
/**
* create a new PDFNativeTextCmd
*/
public PDFNativeTextCmd(String text, Matrix mat) {
// Log.i(TAG, "NATIVETEXTCMD['"+text+"',"+mat+"]");
this.text = text;
this.mat = mat;
float[] values = new float[9];
mat.getValues(values);
this.x = values[2];
this.y = values[5];
this.w = values[0];
this.h = values[4];
bounds = new RectF(x, y, x+w, y+h);
}
/**
* perform the stroke and record the dirty region
*/
public RectF execute(PDFRenderer state) {
RectF rect = state.drawNativeText(text, bounds);
return rect;
}
// /**
// * Check for overlap with the previous shape to make anti-aliased shapes
// * that are near each other look good
// */
// private Path checkOverlap(PDFRenderer state) {
// if (style == FILL && gp != null && state.getLastShape() != null) {
// float mypoints[] = new float[16];
// float prevpoints[] = new float[16];
//
// int mycount = getPoints(gp, mypoints);
// int prevcount = getPoints(state.getLastShape(), prevpoints);
//
// // now check mypoints against prevpoints for opposite pairs:
// if (mypoints != null && prevpoints != null) {
// for (int i = 0; i < prevcount; i += 4) {
// for (int j = 0; j < mycount; j += 4) {
// if ((Math.abs(mypoints[j + 2] - prevpoints[i]) < 0.01 &&
// Math.abs(mypoints[j + 3] - prevpoints[i + 1]) < 0.01 &&
// Math.abs(mypoints[j] - prevpoints[i + 2]) < 0.01 &&
// Math.abs(mypoints[j + 1] - prevpoints[i + 3]) < 0.01)) {
// GeneralPath strokeagain = new GeneralPath();
// strokeagain.moveTo(mypoints[j], mypoints[j + 1]);
// strokeagain.lineTo(mypoints[j + 2], mypoints[j + 3]);
// return strokeagain;
// }
// }
// }
// }
// }
//
// // no issues
// return null;
// }
//
// /**
// * Get an array of 16 points from a path
// * @return the number of points we actually got
// */
// private int getPoints(GeneralPath path, float[] mypoints) {
// int count = 0;
// float x = 0;
// float y = 0;
// float startx = 0;
// float starty = 0;
// float[] coords = new float[6];
//
// PathIterator pi = path.getPathIterator(new AffineTransform());
// while (!pi.isDone()) {
// if (count >= mypoints.length) {
// mypoints = null;
// break;
// }
//
// int pathtype = pi.currentSegment(coords);
// switch (pathtype) {
// case PathIterator.SEG_MOVETO:
// startx = x = coords[0];
// starty = y = coords[1];
// break;
// case PathIterator.SEG_LINETO:
// mypoints[count++] = x;
// mypoints[count++] = y;
// x = mypoints[count++] = coords[0];
// y = mypoints[count++] = coords[1];
// break;
// case PathIterator.SEG_QUADTO:
// x = coords[2];
// y = coords[3];
// break;
// case PathIterator.SEG_CUBICTO:
// x = mypoints[4];
// y = mypoints[5];
// break;
// case PathIterator.SEG_CLOSE:
// mypoints[count++] = x;
// mypoints[count++] = y;
// x = mypoints[count++] = startx;
// y = mypoints[count++] = starty;
// break;
// }
//
// pi.next();
// }
//
// return count;
// }
/** Get detailed information about this shape
*/
@Override
public String getDetails() {
StringBuffer sb = new StringBuffer();
sb.append("NativeTextCommand Text: '" + text + "'\n");
sb.append("matrix: " + mat + "\n");
sb.append("Mode: ");
if ((style & FILL) != 0) {
sb.append("FILL ");
}
if ((style & STROKE) != 0) {
sb.append("STROKE ");
}
if ((style & CLIP) != 0) {
sb.append("CLIP");
}
return sb.toString();
}
public float getWidth() {
return 6.0f*text.length();
// return 0.5f*text.length()*w;
}
}

View File

@ -0,0 +1,761 @@
/*
* $Id: PDFObject.java,v 1.8 2009/03/15 20:47:38 tomoke Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview;
import java.io.IOException;
import net.sf.andpdf.refs.SoftReference;
import java.util.*;
import net.sf.andpdf.nio.ByteBuffer;
import com.sun.pdfview.decode.PDFDecoder;
import com.sun.pdfview.decrypt.PDFDecrypter;
import com.sun.pdfview.decrypt.IdentityDecrypter;
/**
* a class encapsulating all the possibilities of content for
* an object in a PDF file.
* <p>
* A PDF object can be a simple type, like a Boolean, a Number,
* a String, or the Null value. It can also be a NAME, which
* looks like a string, but is a special type in PDF files, like
* "/Name".
* <p>
* A PDF object can also be complex types, including Array;
* Dictionary; Stream, which is a Dictionary plus an array of
* bytes; or Indirect, which is a reference to some other
* PDF object. Indirect references will always be dereferenced
* by the time any data is returned from one of the methods
* in this class.
*
* @author Mike Wessler
*/
public class PDFObject {
/** an indirect reference*/
public static final int INDIRECT = 0; // PDFXref
/** a Boolean */
public static final int BOOLEAN = 1; // Boolean
/** a Number, represented as a double */
public static final int NUMBER = 2; // Double
/** a String */
public static final int STRING = 3; // String
/** a special string, seen in PDF files as /Name */
public static final int NAME = 4; // String
/** an array of PDFObjects */
public static final int ARRAY = 5; // Array of PDFObject
/** a Hashmap that maps String names to PDFObjects */
public static final int DICTIONARY = 6; // HashMap(String->PDFObject)
/** a Stream: a Hashmap with a byte array */
public static final int STREAM = 7; // HashMap + byte[]
/** the NULL object (there is only one) */
public static final int NULL = 8; // null
/** a special PDF bare word, like R, obj, true, false, etc */
public static final int KEYWORD = 9; // String
/**
* When a value of {@link #getObjGen objNum} or {@link #getObjGen objGen},
* indicates that the object is not top-level, and is embedded in another
* object
*/
public static final int OBJ_NUM_EMBEDDED = -2;
/**
* When a value of {@link #getObjGen objNum} or {@link #getObjGen objGen},
* indicates that the object is not top-level, and is embedded directly
* in the trailer.
*/
public static final int OBJ_NUM_TRAILER = -1;
/** the NULL PDFObject */
public static final PDFObject nullObj = new PDFObject(null, NULL, null);
/** the type of this object */
private int type;
/** the value of this object. It can be a wide number of things, defined by type */
private Object value;
/** the encoded stream, if this is a STREAM object */
private ByteBuffer stream;
/** a cached version of the decoded stream */
private SoftReference decodedStream;
/**
* the PDFFile from which this object came, used for
* dereferences
*/
private PDFFile owner;
/**
* a cache of translated data. This data can be
* garbage collected at any time, after which it will
* have to be rebuilt.
*/
private SoftReference cache;
/** @see #getObjNum() */
private int objNum = OBJ_NUM_EMBEDDED;
/** @see #getObjGen() */
private int objGen = OBJ_NUM_EMBEDDED;
/**
* create a new simple PDFObject with a type and a value
* @param owner the PDFFile in which this object resides, used
* for dereferencing. This may be null.
* @param type the type of object
* @param value the value. For DICTIONARY, this is a HashMap.
* for ARRAY it's an ArrayList. For NUMBER, it's a Double.
* for BOOLEAN, it's Boolean.TRUE or Boolean.FALSE. For
* everything else, it's a String.
*/
public PDFObject(PDFFile owner, int type, Object value) {
this.type = type;
if (type == NAME) {
value = ((String) value).intern();
} else if (type == KEYWORD && value.equals("true")) {
this.type = BOOLEAN;
value = Boolean.TRUE;
} else if (type == KEYWORD && value.equals("false")) {
this.type = BOOLEAN;
value = Boolean.FALSE;
}
this.value = value;
this.owner = owner;
}
/**
* create a new PDFObject that is the closest match to a
* given Java object. Possibilities include Double, String,
* PDFObject[], HashMap, Boolean, or PDFParser.Tok,
* which should be "true" or "false" to turn into a BOOLEAN.
*
* @param obj the sample Java object to convert to a PDFObject.
* @throws PDFParseException if the object isn't one of the
* above examples, and can't be turned into a PDFObject.
*/
public PDFObject(Object obj) throws PDFParseException {
this.owner = null;
this.value = obj;
if ((obj instanceof Double) || (obj instanceof Integer)) {
this.type = NUMBER;
} else if (obj instanceof String) {
this.type = NAME;
} else if (obj instanceof PDFObject[]) {
this.type = ARRAY;
} else if (obj instanceof Object[]) {
Object[] srcary = (Object[]) obj;
PDFObject[] dstary = new PDFObject[srcary.length];
for (int i = 0; i < srcary.length; i++) {
dstary[i] = new PDFObject(srcary[i]);
}
value = dstary;
this.type = ARRAY;
} else if (obj instanceof HashMap) {
this.type = DICTIONARY;
} else if (obj instanceof Boolean) {
this.type = BOOLEAN;
} else if (obj instanceof PDFParser.Tok) {
PDFParser.Tok tok = (PDFParser.Tok) obj;
if (tok.name.equals("true")) {
this.value = Boolean.TRUE;
this.type = BOOLEAN;
} else if (tok.name.equals("false")) {
this.value = Boolean.FALSE;
this.type = BOOLEAN;
} else {
this.value = tok.name;
this.type = NAME;
}
} else {
throw new PDFParseException("Bad type for raw PDFObject: " + obj);
}
}
/**
* create a new PDFObject based on a PDFXref
* @param owner the PDFFile from which the PDFXref was drawn
* @param xref the PDFXref to turn into a PDFObject
*/
public PDFObject(PDFFile owner, PDFXref xref) {
this.type = INDIRECT;
this.value = xref;
this.owner = owner;
}
/**
* get the type of this object. The object will be
* dereferenced, so INDIRECT will never be returned.
* @return the type of the object
*/
public int getType() throws IOException {
if (type == INDIRECT) {
return dereference().getType();
}
return type;
}
/**
* set the stream of this object. It should have been
* a DICTIONARY before the call.
* @param data the data, as a ByteBuffer.
*/
public void setStream(ByteBuffer data) {
this.type = STREAM;
this.stream = data;
}
/**
* get the value in the cache. May become null at any time.
* @return the cached value, or null if the value has been
* garbage collected.
*/
public Object getCache() throws IOException {
if (type == INDIRECT) {
return dereference().getCache();
} else if (cache != null) {
return cache.get();
} else {
return null;
}
}
/**
* set the cached value. The object may be garbage collected
* if no other reference exists to it.
* @param obj the object to be cached
*/
public void setCache(Object obj) throws IOException {
if (type == INDIRECT) {
dereference().setCache(obj);
return;
} else {
cache = new SoftReference<Object>(obj);
}
}
/**
* get the stream from this object. Will return null if this
* object isn't a STREAM.
* @return the stream, or null, if this isn't a STREAM.
*/
public byte[] getStream() throws IOException {
if (type == INDIRECT) {
return dereference().getStream();
} else if (type == STREAM && stream != null) {
byte[] data = null;
synchronized (stream) {
// decode
ByteBuffer streamBuf = decodeStream();
// ByteBuffer streamBuf = stream;
// First try to use the array with no copying. This can only
// be done if the buffer has a backing array, and is not a slice
if (streamBuf.hasArray() && streamBuf.arrayOffset() == 0) {
byte[] ary = streamBuf.array();
// make sure there is no extra data in the buffer
if (ary.length == streamBuf.remaining()) {
return ary;
}
}
// Can't use the direct buffer, so copy the data (bad)
data = new byte[streamBuf.remaining()];
streamBuf.get(data);
// return the stream to its starting position
streamBuf.flip();
}
return data;
} else if (type == STRING) {
return PDFStringUtil.asBytes(getStringValue());
}
// wrong type
return null;
}
/**
* get the stream from this object as a byte buffer. Will return null if
* this object isn't a STREAM.
* @return the buffer, or null, if this isn't a STREAM.
*/
public ByteBuffer getStreamBuffer() throws IOException {
if (type == INDIRECT) {
return dereference().getStreamBuffer();
} else if (type == STREAM && stream != null) {
synchronized (stream) {
ByteBuffer streamBuf = decodeStream();
// ByteBuffer streamBuf = stream;
return streamBuf.duplicate();
}
} else if (type == STRING) {
String src = getStringValue();
return ByteBuffer.wrap(src.getBytes());
}
// wrong type
return null;
}
/**
* Get the decoded stream value
*/
private ByteBuffer decodeStream() throws IOException {
ByteBuffer outStream = null;
// first try the cache
if (decodedStream != null) {
outStream = (ByteBuffer) decodedStream.get();
}
// no luck in the cache, do the actual decoding
if (outStream == null) {
stream.rewind();
outStream = PDFDecoder.decodeStream(this, stream);
decodedStream = new SoftReference(outStream);
}
return outStream;
}
/**
* get the value as an int. Will return 0 if this object
* isn't a NUMBER.
*/
public int getIntValue() throws IOException {
if (type == INDIRECT) {
return dereference().getIntValue();
} else if (type == NUMBER) {
return ((Double) value).intValue();
}
// wrong type
return 0;
}
/**
* get the value as a float. Will return 0 if this object
* isn't a NUMBER
*/
public float getFloatValue() throws IOException {
if (type == INDIRECT) {
return dereference().getFloatValue();
} else if (type == NUMBER) {
return ((Double) value).floatValue();
}
// wrong type
return 0;
}
/**
* get the value as a double. Will return 0 if this object
* isn't a NUMBER.
*/
public double getDoubleValue() throws IOException {
if (type == INDIRECT) {
return dereference().getDoubleValue();
} else if (type == NUMBER) {
return ((Double) value).doubleValue();
}
// wrong type
return 0;
}
/**
* get the value as a String. Will return null if the object
* isn't a STRING, NAME, or KEYWORD. This method will <b>NOT</b>
* convert a NUMBER to a String. If the string is actually
* a text string (i.e., may be encoded in UTF16-BE or PdfDocEncoding),
* then one should use {@link #getTextStringValue()} or use one
* of the {@link PDFStringUtil} methods on the result from this
* method. The string value represents exactly the sequence of 8 bit
* characters present in the file, decrypted and decoded as appropriate,
* into a string containing only 8 bit character values - that is, each
* char will be between 0 and 255.
*/
public String getStringValue() throws IOException {
if (type == INDIRECT) {
return dereference().getStringValue();
} else if (type == STRING || type == NAME || type == KEYWORD) {
return (String) value;
}
// wrong type
return null;
}
/**
* Get the value as a text string; i.e., a string encoded in UTF-16BE
* or PDFDocEncoding. Simple latin alpha-numeric characters are preserved in
* both these encodings.
* @return the text string value
* @throws IOException
*/
public String getTextStringValue() throws IOException {
return PDFStringUtil.asTextString(getStringValue());
}
/**
* get the value as a PDFObject[]. If this object is an ARRAY,
* will return the array. Otherwise, will return an array
* of one element with this object as the element.
*/
public PDFObject[] getArray() throws IOException {
if (type == INDIRECT) {
return dereference().getArray();
} else if (type == ARRAY) {
PDFObject[] ary = (PDFObject[]) value;
return ary;
} else {
PDFObject[] ary = new PDFObject[1];
ary[0] = this;
return ary;
}
}
/**
* get the value as a boolean. Will return false if this
* object is not a BOOLEAN
*/
public boolean getBooleanValue() throws IOException {
if (type == INDIRECT) {
return dereference().getBooleanValue();
} else if (type == BOOLEAN) {
return value == Boolean.TRUE;
}
// wrong type
return false;
}
/**
* if this object is an ARRAY, get the PDFObject at some
* position in the array. If this is not an ARRAY, returns
* null.
*/
public PDFObject getAt(int idx) throws IOException {
if (type == INDIRECT) {
return dereference().getAt(idx);
} else if (type == ARRAY) {
PDFObject[] ary = (PDFObject[]) value;
return ary[idx];
}
// wrong type
return null;
}
/**
* get an Iterator over all the keys in the dictionary. If
* this object is not a DICTIONARY or a STREAM, returns an
* Iterator over the empty list.
*/
public Iterator getDictKeys() throws IOException {
if (type == INDIRECT) {
return dereference().getDictKeys();
} else if (type == DICTIONARY || type == STREAM) {
return ((HashMap) value).keySet().iterator();
}
// wrong type
return new ArrayList().iterator();
}
/**
* get the dictionary as a HashMap. If this isn't a DICTIONARY
* or a STREAM, returns null
*/
public HashMap<String,PDFObject> getDictionary() throws IOException {
if (type == INDIRECT) {
return dereference().getDictionary();
} else if (type == DICTIONARY || type == STREAM) {
return (HashMap<String,PDFObject>) value;
}
// wrong type
return new HashMap<String,PDFObject>();
}
/**
* get the value associated with a particular key in the
* dictionary. If this isn't a DICTIONARY or a STREAM,
* or there is no such key, returns null.
*/
public PDFObject getDictRef(String key) throws IOException {
if (type == INDIRECT) {
return dereference().getDictRef(key);
} else if (type == DICTIONARY || type == STREAM) {
key = key.intern();
HashMap h = (HashMap) value;
PDFObject obj = (PDFObject) h.get(key.intern());
return obj;
}
// wrong type
return null;
}
/**
* returns true only if this object is a DICTIONARY or a
* STREAM, and the "Type" entry in the dictionary matches a
* given value.
* @param match the expected value for the "Type" key in the
* dictionary
* @return whether the dictionary is of the expected type
*/
public boolean isDictType(String match) throws IOException {
if (type == INDIRECT) {
return dereference().isDictType(match);
} else if (type != DICTIONARY && type != STREAM) {
return false;
}
PDFObject obj = getDictRef("Type");
return obj != null && obj.getStringValue().equals(match);
}
public PDFDecrypter getDecrypter() {
// PDFObjects without owners are always created as part of
// content instructions. Such objects will never have encryption
// applied to them, as the stream they're contained by is the
// unit of encryption. So, if someone asks for the decrypter for
// one of these in-stream objects, no decryption should
// ever be applied. This can be seen with inline images.
return owner != null ?
owner.getDefaultDecrypter() :
IdentityDecrypter.getInstance();
}
/**
* Set the object identifiers
* @param objNum the object number
* @param objGen the object generation number
*/
public void setObjectId(int objNum, int objGen) {
assert objNum >= OBJ_NUM_TRAILER;
assert objGen >= OBJ_NUM_TRAILER;
this.objNum = objNum;
this.objGen = objGen;
}
/**
* Get the object number of this object; a negative value indicates that
* the object is not numbered, as it's not a top-level object: if the value
* is {@link #OBJ_NUM_EMBEDDED}, it is because it's embedded within
* another object. If the value is {@link #OBJ_NUM_TRAILER}, it's because
* it's an object from the trailer.
* @return the object number, if positive
*/
public int getObjNum() {
return objNum;
}
/**
* Get the object generation number of this object; a negative value
* indicates that the object is not numbered, as it's not a top-level
* object: if the value is {@link #OBJ_NUM_EMBEDDED}, it is because it's
* embedded within another object. If the value is {@link
* #OBJ_NUM_TRAILER}, it's because it's an object from the trailer.
* @return the object generation number, if positive
*/
public int getObjGen() {
return objGen;
}
/**
* return a representation of this PDFObject as a String.
* Does NOT dereference anything: this is the only method
* that allows you to distinguish an INDIRECT PDFObject.
*/
@Override
public String toString() {
try {
if (type == INDIRECT) {
StringBuffer str = new StringBuffer ();
str.append("Indirect to #" + ((PDFXref) value).getID()+(((PDFXref) value).getCompressed()?" comp":""));
try {
// TODO [FHe] uncomment
// str.append("\n" + dereference().toString());
PDFObject obj = cachedDereference();
str.append("\n" + (obj==null?"<ref>":obj.toString()));
} catch (Throwable t) {
str.append(t.toString());
}
return str.toString();
} else if (type == BOOLEAN) {
return "Boolean: " + (getBooleanValue() ? "true" : "false");
} else if (type == NUMBER) {
return "Number: " + getDoubleValue();
} else if (type == STRING) {
return "String: " + getStringValue();
} else if (type == NAME) {
return "Name: /" + getStringValue();
} else if (type == ARRAY) {
return "Array, length=" + ((PDFObject[]) value).length;
} else if (type == DICTIONARY) {
StringBuffer sb = new StringBuffer();
PDFObject obj = getDictRef("Type");
if (obj != null) {
sb.append(obj.getStringValue());
obj = getDictRef("Subtype");
if (obj == null) {
obj = getDictRef("S");
}
if (obj != null) {
sb.append("/" + obj.getStringValue());
}
} else {
sb.append("Untyped");
}
sb.append(" dictionary. Keys:");
HashMap hm = (HashMap) value;
Iterator it = hm.entrySet().iterator();
Map.Entry entry;
while (it.hasNext()) {
entry = (Map.Entry) it.next();
sb.append("\n " + entry.getKey() + " " + entry.getValue());
}
return sb.toString();
} else if (type == STREAM) {
byte[] st = getStream();
if (st == null) {
return "Broken stream";
}
return "Stream: [[" + new String(st, 0, st.length > 30 ? 30 : st.length) + "]]";
} else if (type == NULL) {
return "Null";
} else if (type == KEYWORD) {
return "Keyword: " + getStringValue();
/* } else if (type==IMAGE) {
StringBuffer sb= new StringBuffer();
java.awt.Image im= (java.awt.Image)stream;
sb.append("Image ("+im.getWidth(null)+"x"+im.getHeight(null)+", with keys:");
HashMap hm= (HashMap)value;
Iterator it= hm.keySet().iterator();
while(it.hasNext()) {
sb.append(" "+(String)it.next());
}
return sb.toString();*/
} else {
return "Whoops! big error! Unknown type";
}
} catch (IOException ioe) {
return "Caught an error: " + ioe;
}
}
/**
* Make sure that this object is dereferenced. Use the cache of
* an indirect object to cache the dereferenced value, if possible.
*/
public PDFObject dereference() throws IOException {
if (type == INDIRECT) {
PDFObject obj = null;
if (cache != null) {
obj = (PDFObject) cache.get();
}
if (obj == null || obj.value == null) {
if (owner == null) {
System.out.println("Bad seed (owner==null)! Object=" + this);
}
obj = owner.dereference((PDFXref)value, getDecrypter());
cache = new SoftReference<PDFObject>(obj);
}
return obj;
} else {
// not indirect, no need to dereference
return this;
}
}
/**
* Make sure that this object is dereferenced. Use the cache of
* an indirect object to cache the dereferenced value, if possible.
*/
public PDFObject cachedDereference() throws IOException {
if (type == INDIRECT) {
PDFObject obj = null;
if (cache != null) {
obj = (PDFObject) cache.get();
}
if (obj == null || obj.value == null) {
if (owner == null) {
System.out.println("Bad seed (owner==null)! Object=" + this);
}
// obj = owner.dereference((PDFXref)value, getDecrypter());
//
// cache = new SoftReference<PDFObject>(obj);
}
return obj;
} else {
// not indirect, no need to dereference
return this;
}
}
/**
* Identify whether the object is currently an indirect/cross-reference
* @return whether currently indirect
*/
public boolean isIndirect() {
return (type == INDIRECT);
}
/**
* Test whether two PDFObject are equal. Objects are equal IFF they
* are the same reference OR they are both indirect objects with the
* same id and generation number in their xref
*/
@Override
public boolean equals(Object o) {
if (super.equals(o)) {
// they are the same object
return true;
} else if (type == INDIRECT && o instanceof PDFObject) {
// they are both PDFObjects. Check type and xref.
PDFObject obj = (PDFObject) o;
if (obj.type == INDIRECT) {
PDFXref lXref = (PDFXref) value;
PDFXref rXref = (PDFXref) obj.value;
return ((lXref.getID() == rXref.getID()) &&
(lXref.getGeneration() == rXref.getGeneration()) &&
(lXref.getCompressed() == rXref.getCompressed()));
}
}
return false;
}
}

View File

@ -0,0 +1,850 @@
/*
* $Id: PDFPage.java,v 1.5 2009/02/12 13:53:56 tomoke Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview;
import net.sf.andpdf.refs.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import net.sf.andpdf.utils.Utils;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.RectF;
import android.graphics.Bitmap.Config;
import android.graphics.Paint.Cap;
import android.graphics.Paint.Join;
/**
* A PDFPage encapsulates the parsed commands required to render a
* single page from a PDFFile. The PDFPage is not itself drawable;
* instead, create a PDFImage to display something on the screen.
* <p>
* This file also contains all of the PDFCmd commands that might
* be a part of the command stream in a PDFPage. They probably
* should be inner classes of PDFPage instead of separate non-public
* classes.
*
* @author Mike Wessler
* @author Ferenc Hechler (ferenc@hechler.de)
* @author Joerg Jahnke (joergjahnke@users.sourceforge.net)
*/
public class PDFPage {
/** the array of commands. The length of this array will always
* be greater than or equal to the actual number of commands. */
private final List<PDFCmd> commands = Collections.synchronizedList(new ArrayList<PDFCmd>(250));
/** whether this page has been finished. If true, there will be no
* more commands added to the cmds list. */
private boolean finished = false;
/** the page number used to find this page */
private int pageNumber;
/** the bounding box of the page, in page coordinates */
private RectF bbox;
/** the rotation of this page, in degrees */
private int rotation;
/** a map from image info (width, height, clip) to a soft reference to the
rendered image */
private Cache cache;
/** a map from image info to weak references to parsers that are active */
private final Map<ImageInfo, WeakReference> renderers = Collections.synchronizedMap(new HashMap<ImageInfo, WeakReference>());
// TODO [FHe]: just a quick hack
private static int parsedCommands;
public static int getParsedCommands() {
return parsedCommands;
}
private static int lastRenderedCommand;
public static int getLastRenderedCommand() {
return lastRenderedCommand;
}
/**
* create a PDFPage with dimensions in bbox and rotation.
*/
public PDFPage(RectF bbox, int rotation) {
this(-1, bbox, rotation, null);
}
/**
* create a PDFPage with dimensions in bbox and rotation.
*/
public PDFPage(int pageNumber, RectF bbox, int rotation,
Cache cache) {
this.pageNumber = pageNumber;
this.cache = cache;
if (bbox == null) {
bbox = new RectF(0, 0, 1, 1);
}
if (rotation < 0) {
rotation += 360;
}
this.rotation = rotation;
if (rotation == 90 || rotation == 270) {
bbox = new RectF(bbox.left, bbox.top,
bbox.left+bbox.height(), bbox.top+bbox.width());
}
this.bbox = bbox;
}
/**
* Get the width and height of this image in the correct aspect ratio.
* The image returned will have at least one of the width and
* height values identical to those requested. The other
* dimension may be smaller, so as to keep the aspect ratio
* the same as in the original page.
*
* @param width the maximum width of the image
* @param height the maximum height of the image
* @param clip the region in <b>page space</b> of the page to
* display. It may be null, in which the page's defined crop box
* will be used.
*/
public PointF getUnstretchedSize(int width, int height,
RectF clip) {
if (clip == null) {
clip = bbox;
} else {
if (getRotation() == 90 ||
getRotation() == 270) {
clip = new RectF(clip.left, clip.top,
clip.height(), clip.width());
}
}
float ratio = clip.height() / clip.width();
float askratio = height / width;
if (askratio > ratio) {
// asked for something too high
height = (int) (width * ratio + 0.5);
} else {
// asked for something too wide
width = (int) (height / ratio + 0.5);
}
return new PointF(width, height);
}
/**
* Get an image producer which can be used to draw the image
* represented by this PDFPage. The ImageProducer is guaranteed to
* stay in sync with the PDFPage as commands are added to it.
*
* The image will contain the section of the page specified by the clip,
* scaled to fit in the area given by width and height.
*
* @param width the width of the image to be produced
* @param height the height of the image to be produced
* @param clip the region in <b>page space</b> of the entire page to
* display
* @param observer an image observer who will be notified when the
* image changes, or null
* @return an Image that contains the PDF data
*/
public Bitmap getImage(int width, int height, RectF clip) {
return getImage(width, height, clip, true, false);
}
/**
* Get an image producer which can be used to draw the image
* represented by this PDFPage. The ImageProducer is guaranteed to
* stay in sync with the PDFPage as commands are added to it.
*
* The image will contain the section of the page specified by the clip,
* scaled to fit in the area given by width and height.
*
* @param width the width of the image to be produced
* @param height the height of the image to be produced
* @param clip the region in <b>page space</b> of the entire page to
* display
* @param observer an image observer who will be notified when the
* image changes, or null
* @param drawbg if true, put a white background on the image. If not,
* draw no color (alpha 0) for the background.
* @param wait if true, do not return until this image is fully rendered.
* @return an Image that contains the PDF data
*/
public Bitmap getImage(int width, int height, RectF clip,
boolean drawbg, boolean wait) {
// see if we already have this image
Bitmap image = null;
PDFRenderer renderer = null;
ImageInfo info = new ImageInfo(width, height, clip, Color.WHITE);
// if (cache != null) {
// image = cache.getImage(this, info);
// renderer = cache.getImageRenderer(this, info);
// }
//
// not in the cache, so create it
if (image == null) {
if (drawbg) {
info.bgColor = Color.WHITE;
}
image = Bitmap.createBitmap(width, height, Config.RGB_565);
renderer = new PDFRenderer(this, info, image);
// if (cache != null) {
// cache.addImage(this, info, image, renderer);
// }
renderers.put(info, new WeakReference<PDFRenderer>(renderer));
}
// the renderer may be null if we are getting this image from the
// cache and rendering has completed.
if (renderer != null) {
// if (observer != null) {
// renderer.addObserver(observer);
// }
if (!renderer.isFinished()) {
renderer.go(wait);
}
}
// return the image
return image;
}
/**
* get the page number used to lookup this page
* @return the page number
*/
public int getPageNumber() {
return pageNumber;
}
/**
* get the aspect ratio of the correctly oriented page.
* @return the width/height aspect ratio of the page
*/
public float getAspectRatio() {
return getWidth() / getHeight();
}
/**
* get the bounding box of the page, before any rotation.
*/
public RectF getBBox() {
return bbox;
}
/**
* get the width of this page, after rotation
*/
public float getWidth() {
return (float) bbox.width();
}
/**
* get the height of this page, after rotation
*/
public float getHeight() {
return (float) bbox.height();
}
/**
* get the rotation of this image
*/
public int getRotation() {
return rotation;
}
/**
* Get the initial transform to map from a specified clip rectangle in
* pdf coordinates to an image of the specfied width and
* height in device coordinates
*
* @param width the width of the image
* @param height the height of the image
* @param clip the desired clip rectangle (in PDF space) or null to use
* the page's bounding box
*/
public Matrix getInitialTransform(int width, int height, RectF clip) {
Matrix mat = new Matrix();
switch (getRotation()) {
case 0:
Utils.setMatValues(mat, 1, 0, 0, -1, 0, height);
break;
case 90:
Utils.setMatValues(mat, 0, 1, 1, 0, 0, 0);
break;
case 180:
Utils.setMatValues(mat, -1, 0, 0, 1, width, 0);
break;
case 270:
Utils.setMatValues(mat, 0, -1, -1, 0, width, height);
break;
}
if (clip == null) {
clip = getBBox();
} else if (getRotation() == 90 || getRotation() == 270) {
int tmp = width;
width = height;
height = tmp;
}
// now scale the image to be the size of the clip
float scaleX = width / clip.width();
float scaleY = height / clip.height();
mat.preScale(scaleX, scaleY);
// create a transform that moves the top left corner of the clip region
// (minX, minY) to (0,0) in the image
// mat.setTranslate(-clip.top, -clip.left);
mat.preTranslate(-clip.left, -clip.top);
return mat;
}
/**
* get the current number of commands for this page
*/
public int getCommandCount() {
return commands.size();
}
/**
* get the command at a given index
*/
public PDFCmd getCommand(int index) {
lastRenderedCommand = index;
return commands.get(index);
}
/**
* get all the commands in the current page
*/
public List<PDFCmd> getCommands() {
return commands;
}
/**
* get all the commands in the current page starting at the given index
*/
public List getCommands(int startIndex) {
return getCommands(startIndex, getCommandCount());
}
/*
* get the commands in the page within the given start and end indices
*/
public List getCommands(int startIndex, int endIndex) {
return commands.subList(startIndex, endIndex);
}
/**
* Add a single command to the page list.
*/
public void addCommand(PDFCmd cmd) {
synchronized (commands) {
commands.add(cmd);
}
// notify any outstanding images
updateImages();
}
/**
* add a collection of commands to the page list. This is probably
* invoked as the result of an XObject 'do' command, or through a
* type 3 font.
*/
public void addCommands(PDFPage page) {
addCommands(page, null);
}
/**
* add a collection of commands to the page list. This is probably
* invoked as the result of an XObject 'do' command, or through a
* type 3 font.
* @param page the source of other commands. It MUST be finished.
* @param extra a transform to perform before adding the commands.
* If null, no extra transform will be added.
*/
public void addCommands(PDFPage page, Matrix extra) {
synchronized (commands) {
addPush();
if (extra != null) {
addXform(extra);
}
//addXform(page.getTransform());
commands.addAll(page.getCommands());
addPop();
}
// notify any outstanding images
updateImages();
}
/**
* Clear all commands off the current page
*/
public void clearCommands() {
synchronized (commands) {
commands.clear();
}
// notify any outstanding images
updateImages();
}
/**
* get whether parsing for this PDFPage has been completed and all
* commands are in place.
*/
public boolean isFinished() {
return finished;
}
/**
* wait for finish
*/
public synchronized void waitForFinish() throws InterruptedException {
if (!finished) {
wait();
}
}
/**
* Stop the rendering of a particular image on this page
*/
public void stop(int width, int height, RectF clip) {
ImageInfo info = new ImageInfo(width, height, clip);
synchronized (renderers) {
// find our renderer
WeakReference rendererRef = renderers.get(info);
if (rendererRef != null) {
PDFRenderer renderer = (PDFRenderer) rendererRef.get();
if (renderer != null) {
// stop it
renderer.stop();
}
}
}
}
/**
* The entire page is done. This must only be invoked once. All
* observers will be notified.
*/
public synchronized void finish() {
// System.out.println("Page finished!");
finished = true;
notifyAll();
// notify any outstanding images
updateImages();
}
/** push the graphics state */
public void addPush() {
addCommand(new PDFPushCmd());
}
/** pop the graphics state */
public void addPop() {
addCommand(new PDFPopCmd());
}
/** concatenate a transform to the graphics state */
public void addXform(Matrix mat) {
// PDFXformCmd xc= lastXformCmd();
// xc.at.concatenate(at);
addCommand(new PDFXformCmd(new Matrix(mat)));
}
/**
* set the stroke width
* @param w the width of the stroke
*/
public void addStrokeWidth(float w) {
PDFChangeStrokeCmd sc = new PDFChangeStrokeCmd();
// if (w == 0) {
// w = 0.1f;
// }
sc.setWidth(w);
addCommand(sc);
}
/**
* set the end cap style
* @param capstyle the cap style: 0 = BUTT, 1 = ROUND, 2 = SQUARE
*/
public void addEndCap(int capstyle) {
PDFChangeStrokeCmd sc = new PDFChangeStrokeCmd();
Cap cap = Paint.Cap.BUTT;
switch (capstyle) {
case 0:
cap = Paint.Cap.BUTT;
break;
case 1:
cap = Paint.Cap.ROUND;
break;
case 2:
cap = Paint.Cap.SQUARE;
break;
}
sc.setEndCap(cap);
addCommand(sc);
}
/**
* set the line join style
* @param joinstyle the join style: 0 = MITER, 1 = ROUND, 2 = BEVEL
*/
public void addLineJoin(int joinstyle) {
PDFChangeStrokeCmd sc = new PDFChangeStrokeCmd();
Join join = Paint.Join.MITER;
switch (joinstyle) {
case 0:
join = Paint.Join.MITER;
break;
case 1:
join = Paint.Join.ROUND;
break;
case 2:
join = Paint.Join.BEVEL;
break;
}
sc.setLineJoin(join);
addCommand(sc);
}
/**
* set the miter limit
*/
public void addMiterLimit(float limit) {
PDFChangeStrokeCmd sc = new PDFChangeStrokeCmd();
sc.setMiterLimit(limit);
addCommand(sc);
}
/**
* set the dash style
* @param dashary the array of on-off lengths
* @param phase offset of the array at the start of the line drawing
*/
public void addDash(float[] dashary, float phase) {
PDFChangeStrokeCmd sc = new PDFChangeStrokeCmd();
sc.setDash(dashary, phase);
addCommand(sc);
}
/**
* set the current path
* @param path the path
* @param style the style: PDFShapeCmd.STROKE, PDFShapeCmd.FILL,
* PDFShapeCmd.BOTH, PDFShapeCmd.CLIP, or some combination.
*/
public void addPath(Path path, int style) {
addCommand(new PDFShapeCmd(path, style));
}
/**
* set the fill paint
*/
public void addFillPaint(PDFPaint p) {
addCommand(new PDFFillPaintCmd(p));
}
/** set the stroke paint */
public void addStrokePaint(PDFPaint p) {
addCommand(new PDFStrokePaintCmd(p));
}
/**
* set the fill alpha
*/
public void addFillAlpha(float a) {
addCommand(new PDFFillAlphaCmd(a));
}
/** set the stroke alpha */
public void addStrokeAlpha(float a) {
addCommand(new PDFStrokeAlphaCmd(a));
}
/**
* draw an image
* @param image the image to draw
*/
public void addImage(PDFImage image) {
addCommand(new PDFImageCmd(image));
}
/**
* Notify all images we know about that a command has been added
*/
public void updateImages() {
parsedCommands = commands.size();
for (Iterator i = renderers.values().iterator(); i.hasNext();) {
WeakReference ref = (WeakReference) i.next();
PDFRenderer renderer = (PDFRenderer) ref.get();
if (renderer != null) {
if (renderer.getStatus() == Watchable.NEEDS_DATA) {
// there are watchers. Set the state to paused and
// let the watcher decide when to start.
renderer.setStatus(Watchable.PAUSED);
}
}
}
}
}
/**
* draw an image
*/
class PDFImageCmd extends PDFCmd {
PDFImage image;
public PDFImageCmd(PDFImage image) {
this.image = image;
}
public RectF execute(PDFRenderer state) {
return state.drawImage(image);
}
}
/**
* set the fill paint
*/
class PDFFillPaintCmd extends PDFCmd {
PDFPaint p;
public PDFFillPaintCmd(PDFPaint p) {
this.p = p;
}
public RectF execute(PDFRenderer state) {
state.setFillPaint(p);
return null;
}
}
/**
* set the stroke paint
*/
class PDFStrokePaintCmd extends PDFCmd {
PDFPaint p;
public PDFStrokePaintCmd(PDFPaint p) {
this.p = p;
}
public RectF execute(PDFRenderer state) {
state.setStrokePaint(p);
return null;
}
}
/**
* set the fill paint
*/
class PDFFillAlphaCmd extends PDFCmd {
float a;
public PDFFillAlphaCmd(float a) {
this.a = a;
}
public RectF execute(PDFRenderer state) {
// TODO [FHe]: fill alpha
// state.setFillAlpha(a);
return null;
}
}
/**
* set the stroke paint
*/
class PDFStrokeAlphaCmd extends PDFCmd {
float a;
public PDFStrokeAlphaCmd(float a) {
this.a = a;
}
public RectF execute(PDFRenderer state) {
// TODO [FHe]: stroke alpha
// state.setStrokeAlpha(a);
return null;
}
}
/**
* push the graphics state
*/
class PDFPushCmd extends PDFCmd {
public RectF execute(PDFRenderer state) {
state.push();
return null;
}
}
/**
* pop the graphics state
*/
class PDFPopCmd extends PDFCmd {
public RectF execute(PDFRenderer state) {
state.pop();
return null;
}
}
/**
* concatenate a transform to the graphics state
*/
class PDFXformCmd extends PDFCmd {
Matrix mat;
public PDFXformCmd(Matrix mat) {
if (mat == null) {
throw new RuntimeException("Null transform in PDFXformCmd");
}
this.mat = mat;
}
public RectF execute(PDFRenderer state) {
state.transform(mat);
return null;
}
public String toString(PDFRenderer state) {
return "PDFXformCmd: " + mat;
}
@Override
public String getDetails() {
StringBuffer buf = new StringBuffer();
buf.append("PDFXformCommand: \n");
buf.append(mat.toString());
return buf.toString();
}
}
/**
* change the stroke style
*/
class PDFChangeStrokeCmd extends PDFCmd {
float w, limit, phase;
Cap cap;
Join join;
float[] ary;
public PDFChangeStrokeCmd() {
this.w = PDFRenderer.NOWIDTH;
this.cap = PDFRenderer.NOCAP;
this.join = PDFRenderer.NOJOIN;
this.limit = PDFRenderer.NOLIMIT;
this.ary = PDFRenderer.NODASH;
this.phase = PDFRenderer.NOPHASE;
}
/**
* set the width of the stroke. Rendering needs to account for a minimum
* stroke width in creating the output.
*
* @param w float
*/
public void setWidth(float w) {
this.w = w;
}
public void setEndCap(Cap cap) {
this.cap = cap;
}
public void setLineJoin(Join join) {
this.join = join;
}
public void setMiterLimit(float limit) {
this.limit = limit;
}
public void setDash(float[] ary, float phase) {
if (ary != null) {
// make sure no pairs start with 0, since having no opaque
// region doesn't make any sense.
for (int i = 0; i < ary.length - 1; i += 2) {
if (ary[i] == 0) {
/* Give a very small value, since 0 messes java up */
ary[i] = 0.00001f;
break;
}
}
}
this.ary = ary;
this.phase = phase;
}
public RectF execute(PDFRenderer state) {
state.setStrokeParts(w, cap, join, limit, ary, phase);
return null;
}
public String toString(PDFRenderer state) {
return "STROKE: w=" + w + " cap=" + cap + " join=" + join + " limit=" + limit +
" ary=" + ary + " phase=" + phase;
}
}

View File

@ -0,0 +1,94 @@
/*
* $Id: PDFPaint.java,v 1.4 2009/01/16 16:26:09 tomoke Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.graphics.Paint.Style;
/**
* PDFPaint is some kind of shader that knows how to fill a path.
* At the moment, only a solid color is implemented, but gradients
* and textures should be possible, too.
*
* @author Mike Wessler
* @author Ferenc Hechler (ferenc@hechler.de)
* @author Joerg Jahnke (joergjahnke@users.sourceforge.net)
*/
public class PDFPaint {
private Paint mainPaint;
public static boolean s_doAntiAlias = false;
/**
* create a new PDFPaint based on a solid color
*/
protected PDFPaint(int p) {
this.mainPaint = new Paint();
mainPaint.setColor(p);
mainPaint.setAntiAlias(s_doAntiAlias);
}
/**
* get the PDFPaint representing a solid color
*/
public static PDFPaint getColorPaint(int c) {
PDFPaint result = new PDFPaint(c);
// result.getPaint().setStyle(Style.FILL);
result.getPaint().setStyle(Style.STROKE);
return result;
}
/**
* get the PDFPaint representing a generic paint
*/
public static PDFPaint getPaint(int p) {
PDFPaint result = new PDFPaint(p);
// result.getPaint().setStyle(Style.STROKE);
result.getPaint().setStyle(Style.FILL);
return result;
}
/**
* fill a path with the paint, and record the dirty area.
* @param state the current graphics state
* @param g the graphics into which to draw
* @param s the path to fill
*/
public RectF fill(final PDFRenderer state, final Canvas g, final Path s) {
g.drawPath(s, mainPaint);
final RectF bounds = new RectF();
final RectF result = new RectF();
s.computeBounds(bounds, false);
g.getMatrix().mapRect(result, bounds);
return bounds;
}
/**
* get the primary color associated with this PDFPaint.
*/
public Paint getPaint() {
return mainPaint;
}
}

View File

@ -0,0 +1,41 @@
/*
* $Id: PDFParseException.java,v 1.4 2009/03/12 12:25:25 tomoke Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview;
import java.io.IOException;
/**
* an exception class for recording parse errors in the PDF file
* @author Mike Wessler
*/
public class PDFParseException extends IOException {
public PDFParseException(String msg) {
super(msg);
}
public PDFParseException(String msg, Throwable cause) {
this(msg);
initCause(cause);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,900 @@
/*
* $Id: PDFRenderer.java,v 1.8 2009/02/12 13:53:56 tomoke Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview;
import net.sf.andpdf.refs.WeakReference;
import java.util.Stack;
import net.sf.andpdf.utils.BiCa;
import net.sf.andpdf.utils.Utils;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Bitmap.Config;
import android.graphics.Paint.Cap;
import android.graphics.Paint.Join;
import android.graphics.Region.Op;
import android.util.Log;
/**
* This class turns a set of PDF Commands from a PDF page into an image. It
* encapsulates the state of drawing in terms of stroke, fill, transform,
* etc., as well as pushing and popping these states.
*
* When the run method is called, this class goes through all remaining commands
* in the PDF Page and draws them to its buffered image. It then updates any
* ImageConsumers with the drawn data.
*
* @author ?
* @author Ferenc Hechler (ferenc@hechler.de)
* @author Joerg Jahnke (joergjahnke@users.sourceforge.net)
*/
public class PDFRenderer extends BaseWatchable implements Runnable {
private static final String TAG = "APV.PDFRenderer";
private int cmdCnt;
/** the page we were generate from */
private PDFPage page;
/** where we are in the page's command list */
private int currentCommand;
/** a weak reference to the image we render into. For the image
* to remain available, some other code must retain a strong reference to it.
*/
private WeakReference<BiCa> imageRef;
/** the graphics object for use within an iteration. Note this must be
* set to null at the end of each iteration, or the image will not be
* collected
*/
private Canvas g;
/** the current graphics state */
private GraphicsState state;
/** the stack of push()ed graphics states */
private Stack<GraphicsState> stack;
/** the total region of this image that has been written to */
private RectF globalDirtyRegion;
/** the last shape we drew (to check for overlaps) */
private Path lastShape;
/** the info about the image, if we need to recreate it */
private ImageInfo imageinfo;
/** the next time the image should be notified about updates */
private long then = 0;
/** the sum of all the individual dirty regions since the last update */
private RectF unupdatedRegion;
/** how long (in milliseconds) to wait between image updates */
public static final long UPDATE_DURATION = 200;
public static final float NOPHASE = -1000;
public static final float NOWIDTH = -1000;
public static final float NOLIMIT = -1000;
public static final Cap NOCAP = null;
public static final float[] NODASH = null;
public static final Join NOJOIN = null;
/**
* create a new PDFGraphics state
* @param page the current page
* @param imageinfo the paramters of the image to render
*/
public PDFRenderer(PDFPage page, ImageInfo imageinfo, Bitmap bi) {
super();
this.page = page;
this.imageinfo = imageinfo;
this.imageRef = new WeakReference<BiCa>(new BiCa(bi, g));
// // initialize the list of observers
// observers = new ArrayList<ImageObserver>();
this.cmdCnt = 0;
}
/**
* create a new PDFGraphics state, given a Graphics2D. This version
* will <b>not</b> create an image, and you will get a NullPointerException
* if you attempt to call getImage().
* @param page the current page
* @param g the Graphics2D object to use for drawing
* @param imgbounds the bounds of the image into which to fit the page
* @param clip the portion of the page to draw, in page space, or null
* if the whole page should be drawn
* @param bgColor the color to draw the background of the image, or
* null for no color (0 alpha value)
*/
public PDFRenderer(PDFPage page, Canvas g, RectF imgbounds,
RectF clip, int bgColor) {
super();
this.page = page;
this.g = g;
this.imageinfo = new ImageInfo((int) imgbounds.width(), (int) imgbounds.height(),
clip, bgColor);
g.translate(imgbounds.left, imgbounds.top);
// System.out.println("Translating by "+imgbounds.x+","+imgbounds.y);
// // initialize the list of observers
// observers = new ArrayList<ImageObserver>();
this.cmdCnt = 0;
}
/**
* Set up the graphics transform to match the clip region
* to the image size.
*/
private void setupRendering(Canvas g) {
// g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
// RenderingHints.VALUE_ANTIALIAS_ON);
// g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
// RenderingHints.VALUE_INTERPOLATION_BICUBIC);
// g.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION,
// RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
Paint g_p = new Paint();
g_p.setColor(imageinfo.bgColor);
g.drawRect(0, 0, imageinfo.width, imageinfo.height, g_p);
g_p.setColor(Color.BLACK);
// set the initial clip and transform on the graphics
Matrix mat = getInitialTransform();
g.setMatrix(mat);
// set up the initial graphics state
state = new GraphicsState();
state.cliprgn = null;
state.strokePaint = PDFPaint.getColorPaint(Color.BLACK);
state.fillPaint = PDFPaint.getPaint(Color.BLACK);
state.xform = g.getMatrix();
// initialize the stack
stack = new Stack<GraphicsState>();
// initialize the current command
currentCommand = 0;
}
/**
* push the current graphics state onto the stack. Continue working
* with the current object; calling pop() restores the state of this
* object to its state when push() was called.
*/
public void push() {
state.cliprgn = g.getClipBounds();
stack.push(state);
state = (GraphicsState) state.clone();
}
/**
* restore the state of this object to what it was when the previous
* push() was called.
*/
public void pop() {
state = (GraphicsState) stack.pop();
setTransform(state.xform);
setClip(state.cliprgn);
}
/**
* draw an outline using the current stroke and draw paint
* @param s the path to stroke
* @return a Rectangle2D to which the current region being
* drawn will be added. May also be null, in which case no dirty
* region will be recorded.
*/
public RectF stroke(Path s) {
// g.setComposite(state.strokeAlpha);
// s = new GeneralPath(autoAdjustStrokeWidth(g, state.stroke).createStrokedShape(s));
return state.strokePaint.fill(this, g, s);
}
// /**
// * auto adjust the stroke width, according to 6.5.4, which presumes that
// * the device characteristics (an image) require a single pixel wide
// * line, even if the width is set to less. We determine the scaling to
// * see if we would produce a line that was too small, and if so, scale
// * it up to produce a graphics line of 1 pixel, or so. This matches our
// * output with Adobe Reader.
// *
// * @param g
// * @param bs
// * @return
// */
// private BasicStroke autoAdjustStrokeWidth(Graphics2D g, BasicStroke bs) {
// AffineTransform bt = new AffineTransform(g.getTransform());
// float width = bs.getLineWidth() * (float) bt.getScaleX();
// BasicStroke stroke = bs;
// if (width < 1f) {
// if (bt.getScaleX() > 0.01) {
// width = 1.0f / (float) bt.getScaleX();
// stroke = new BasicStroke(width,
// bs.getEndCap(),
// bs.getLineJoin(),
// bs.getMiterLimit(),
// bs.getDashArray(),
// bs.getDashPhase());
// } else {
// // prevent division by a really small number
// width = 1.0f;
// }
// }
// return stroke;
// }
/**
* draw an outline.
* @param p the path to draw
* @param bs the stroke with which to draw the path
*/
public void draw(Path p) {
g.drawPath(p, state.fillPaint.getPaint());
g.drawPath(p, state.strokePaint.getPaint());
}
/**
* fill an outline using the current fill paint
* @param s the path to fill
*/
public RectF fill(Path s) {
// g.setComposite(state.fillAlpha);
return state.fillPaint.fill(this, g, s);
}
/**
* draw native text
* @param text
* @param mat
* @return
*/
public RectF drawNativeText(String text, RectF bounds) {
Paint paint = state.fillPaint.getPaint();
g.save();
Matrix m;
Matrix mOrig = g.getMatrix();
// Log.i(TAG, "orig");
// m = new Matrix(mOrig);
// showTrans(m, bounds);
// g.setMatrix(m);
// g.drawText(text, bounds.left, bounds.top, paint);
//
// Log.i(TAG, "m.postScale(1.0f, -1.0f);");
// m = new Matrix(mOrig);
// m.postScale(1.0f, -1.0f);
// showTrans(m, bounds);
// g.setMatrix(m);
// g.drawText(text, bounds.left, bounds.top, paint);
//
// Log.i(TAG, "m.postScale(1.0f, -1.0f, 10.0f, 10.0f);");
// m = new Matrix(mOrig);
// m.postScale(1.0f, -1.0f, 10.0f, 10.0f);
// showTrans(m, bounds);
// g.setMatrix(m);
// g.drawText(text, bounds.left, bounds.top, paint);
//
// Log.i(TAG, "m.postScale(1.0f, -1.0f, bounds.left, bounds.top);");
// m = new Matrix(mOrig);
// m.postScale(1.0f, -1.0f, bounds.left, bounds.top);
// showTrans(m, bounds);
// g.setMatrix(m);
// g.drawText(text, bounds.left, bounds.top, paint);
//
// Log.i(TAG, "m.postScale(1.0f, -1.0f, bounds.left, -bounds.top);");
// m = new Matrix(mOrig);
// m.postScale(1.0f, -1.0f, bounds.left, -bounds.top);
// showTrans(m, bounds);
// g.setMatrix(m);
// g.drawText(text, bounds.left, bounds.top, paint);
//
// Log.i(TAG, "m.preScale(1.0f, -1.0f);");
// m = new Matrix(mOrig);
// m.preScale(1.0f, -1.0f);
// showTrans(m, bounds);
// g.setMatrix(m);
// g.drawText(text, bounds.left, bounds.top, paint);
//
// Log.i(TAG, "m.preScale(1.0f, -1.0f, 10.0f, 10.0f);");
// m = new Matrix(mOrig);
// m.preScale(1.0f, -1.0f, 10.0f, 10.0f);
// showTrans(m, bounds);
// g.setMatrix(m);
// g.drawText(text, bounds.left, bounds.top, paint);
//
// Log.i(TAG, "m.preScale(1.0f, -1.0f, bounds.left, bounds.top);");
m = new Matrix(mOrig);
m.preScale(1.0f, -1.0f, bounds.left, bounds.top);
// showTrans(m, bounds);
g.setMatrix(m);
g.drawText(text, bounds.left, bounds.top, paint);
// Log.i(TAG, "m.preScale(1.0f, -1.0f, bounds.left, -bounds.top);");
// m = new Matrix(mOrig);
// m.preScale(1.0f, -1.0f, bounds.left, -bounds.top);
// showTrans(m, bounds);
// g.setMatrix(m);
// g.drawText(text, bounds.left, bounds.top, paint);
//
g.restore();
return bounds;
}
private void showTrans(Matrix m, RectF src) {
RectF dst = new RectF();
m.mapRect(dst, src);
Log.i(TAG, "M="+m);
Log.i(TAG, "src="+src);
Log.i(TAG, "dst="+dst);
}
/**
* draw native text
* @param text
* @param mat
* @return
*/
public RectF drawNativeText(String text, float x, float y) {
Paint paint = state.fillPaint.getPaint();
g.drawText(text, x, y, paint);
return new RectF();
}
/**
* draw an image.
* @param image the image to draw
*/
public RectF drawImage(PDFImage image) {
Matrix mat = new Matrix();
Utils.setMatValues(mat,
1f / image.getWidth(), 0,
0, -1f / image.getHeight(),
0, 1);
Bitmap bi = image.getImage();
if (image.isImageMask()) {
bi = getMaskedImage(bi);
}
/*
javax.swing.JFrame frame = new javax.swing.JFrame("Original Image");
frame.getContentPane().add(new javax.swing.JLabel(new javax.swing.ImageIcon(bi)));
frame.pack();
frame.show();
*/
g.drawBitmap(bi, mat, null);
// get the total transform that was executed
Matrix matB = new Matrix(g.getMatrix());
matB.preConcat(mat);
float minx = 0;
float miny = 0;
float[] points = new float[]{
minx, miny, minx + bi.getWidth(), miny + bi.getHeight()
};
matB.mapPoints(points, 0, points, 0, 2);
return new RectF(points[0], points[1],
points[2] - points[0],
points[3] - points[1]);
}
/**
* add the path to the current clip. The new clip will be the intersection
* of the old clip and given path.
*/
public void clip(Path s) {
g.clipPath(s);
}
/**
* set the clip to be the given shape. The current clip is not taken
* into account.
*/
private void setClip(Rect s) {
state.cliprgn = s;
g.clipRect(s, Op.REPLACE);
}
/**
* get the current affinetransform
*/
public Matrix getTransform() {
return state.xform;
}
/**
* concatenate the given transform with the current transform
*/
public void transform(Matrix mat) {
state.xform.preConcat(mat);
g.setMatrix(state.xform);
}
/**
* replace the current transform with the given one.
*/
public void setTransform(Matrix mat) {
state.xform = mat;
g.setMatrix(state.xform);
}
/**
* get the initial transform from page space to Java space
*/
public Matrix getInitialTransform() {
return page.getInitialTransform(imageinfo.width,
imageinfo.height,
imageinfo.clip);
}
/**
* Set some or all aspects of the current stroke.
* @param w the width of the stroke, or NOWIDTH to leave it unchanged
* @param cap the end cap style, or NOCAP to leave it unchanged
* @param join the join style, or NOJOIN to leave it unchanged
* @param limit the miter limit, or NOLIMIT to leave it unchanged
* @param phase the phase of the dash array, or NOPHASE to leave it
* unchanged
* @param ary the dash array, or null to leave it unchanged. phase
* and ary must both be valid, or phase must be NOPHASE while ary is null.
*/
public void setStrokeParts(float w, Cap cap, Join join, float limit, float[] ary, float phase) {
if (w == NOWIDTH) {
w = state.lineWidth;
}
if (cap == NOCAP) {
cap = state.cap;
}
if (join == NOJOIN) {
join = state.join;
}
if (limit == NOLIMIT) {
limit = state.miterLimit;
}
if (phase == NOPHASE) {
// ary = state.stroke.getDashArray();
// phase = state.stroke.getDashPhase();
}
if (ary != null && ary.length == 0) {
ary = null;
}
// if (phase == NOPHASE) {
// state.stroke = new BasicStroke(w, cap, join, limit);
// } else {
// state.stroke = new BasicStroke(w, cap, join, limit, ary, phase);
// }
state.lineWidth = w;
state.cap = cap;
state.join = join;
state.miterLimit = limit;
}
// /**
// * get the current stroke as a BasicStroke
// */
// public BasicStroke getStroke() {
// return state.stroke;
// }
//
// /**
// * set the current stroke as a BasicStroke
// */
// public void setStroke(BasicStroke bs) {
// state.stroke = bs;
// }
/**
* set the stroke color
*/
public void setStrokePaint(PDFPaint paint) {
state.strokePaint = paint;
}
/**
* set the fill color
*/
public void setFillPaint(PDFPaint paint) {
state.fillPaint = paint;
}
// /**
// * set the stroke alpha
// */
// public void setStrokeAlpha(float alpha) {
// state.strokeAlpha = AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
// alpha);
// }
//
// /**
// * set the stroke alpha
// */
// public void setFillAlpha(float alpha) {
// state.fillAlpha = AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
// alpha);
// }
// /**
// * Add an image observer
// */
// public void addObserver(ImageObserver observer) {
// if (observer == null) {
// return;
// }
//
// // update the new observer to the current state
// Image i = (Image) imageRef.get();
// if (rendererFinished()) {
// // if we're finished, just send a finished notification, don't
// // add to the list of observers
// // System.out.println("Late notify");
// observer.imageUpdate(i, ImageObserver.ALLBITS, 0, 0,
// imageinfo.width, imageinfo.height);
// return;
// } else {
// // if we're not yet finished, add to the list of observers and
// // notify of the current dirty region
// synchronized (observers) {
// observers.add(observer);
// }
//
// if (globalDirtyRegion != null) {
// observer.imageUpdate(i, ImageObserver.SOMEBITS,
// (int) globalDirtyRegion.getMinX(),
// (int) globalDirtyRegion.getMinY(),
// (int) globalDirtyRegion.getWidth(),
// (int) globalDirtyRegion.getHeight());
// }
// }
// }
//
// /**
// * Remove an image observer
// */
// public void removeObserver(ImageObserver observer) {
// synchronized (observers) {
// observers.remove(observer);
// }
// }
/**
* Set the last shape drawn
*/
public void setLastShape(Path shape) {
this.lastShape = shape;
}
/**
* Get the last shape drawn
*/
public Path getLastShape() {
return lastShape;
}
/**
* Setup rendering. Called before iteration begins
*/
@Override
public void setup() {
Canvas graphics = null;
if (imageRef != null) {
BiCa bica = (BiCa) imageRef.get();
if (bica != null) {
graphics = bica.createCa();
}
} else {
graphics = g;
}
if (graphics != null) {
setupRendering(graphics);
}
}
/**
* Draws the next command in the PDFPage to the buffered image.
* The image will be notified about changes no less than every
* UPDATE_DURATION milliseconds.
*
* @return <ul><li>Watchable.RUNNING when there are commands to be processed
* <li>Watchable.NEEDS_DATA when there are no commands to be
* processed, but the page is not yet complete
* <li>Watchable.COMPLETED when the page is done and all
* the commands have been processed
* <li>Watchable.STOPPED if the image we are rendering into
* has gone away
* </ul>
*/
public int iterate() throws Exception {
// make sure we have a page to render
if (page == null) {
return Watchable.COMPLETED;
}
// check if this renderer is based on a weak reference to a graphics
// object. If it is, and the graphics is no longer valid, then just quit
if (imageRef != null) {
final BiCa bica = imageRef.get();
if (bica == null) {
System.out.println("Image went away. Stopping");
return Watchable.STOPPED;
}
g = bica.createCa();
}
// check if there are any commands to parse. If there aren't,
// just return, but check if we'return really finished or not
if (currentCommand >= page.getCommandCount()) {
if (page.isFinished()) {
return Watchable.COMPLETED;
} else {
return Watchable.NEEDS_DATA;
}
}
// TODO [FHe]: display currentCommand / page.commandCount
// find the current command
final PDFCmd cmd = page.getCommand(currentCommand++);
if (cmd == null) {
// uh oh. Synchronization problem!
throw new PDFParseException("Command not found!");
}
if (!PDFParser.RELEASE) {
cmdCnt += 1;
Log.i(TAG, "CMD[" + cmdCnt + "]: " + cmd.toString() + ": " + cmd.getDetails());
}
RectF dirtyRegion = null;
// execute the command
try {
dirtyRegion = cmd.execute(this);
} catch (Exception e) {
// TODO [FHe] remove if all commands are supported, now catch image not yet supp. excp.
Log.e(TAG, e.getMessage(), e);
// throw new PDFParseException(e.getMessage());
}
// append to the global dirty region
globalDirtyRegion = addDirtyRegion(dirtyRegion, globalDirtyRegion);
unupdatedRegion = addDirtyRegion(dirtyRegion, unupdatedRegion);
final long now = System.currentTimeMillis();
if (now > then || rendererFinished()) {
// now tell any observers, so they can repaint
// notifyObservers(bi, unupdatedRegion);
unupdatedRegion = null;
then = now + UPDATE_DURATION;
}
// if we are based on a reference to a graphics, don't hold on to it
// since that will prevent the image from being collected.
if (imageRef != null) {
g = null;
}
// if we need to stop, it will be caught at the start of the next
// iteration.
return Watchable.RUNNING;
}
/**
* Called when iteration has stopped
*/
@Override
public void cleanup() {
page = null;
state = null;
stack = null;
globalDirtyRegion = null;
lastShape = null;
// observers.clear();
// keep around the image ref and image info for use in
// late addObserver() call
}
/**
* Append a rectangle to the total dirty region of this shape
*/
private RectF addDirtyRegion(RectF region, RectF glob) {
if (region == null) {
return glob;
} else if (glob == null) {
return region;
} else {
glob.union(region);
return glob;
}
}
/**
* Determine if we are finished
*/
private boolean rendererFinished() {
if (page == null) {
return true;
}
return (page.isFinished() && currentCommand == page.getCommandCount());
}
// /**
// * Notify the observer that a region of the image has changed
// */
// private void notifyObservers(BufferedImage bi, Rectangle2D region) {
// if (bi == null) {
// return;
// }
//
// int startx, starty, width, height;
// int flags = 0;
//
// // don't do anything if nothing is there or no one is listening
// if ((region == null && !rendererFinished()) || observers == null ||
// observers.size() == 0) {
// return;
// }
//
// if (region != null) {
// // get the image data for the total dirty region
// startx = (int) Math.floor(region.getMinX());
// starty = (int) Math.floor(region.getMinY());
// width = (int) Math.ceil(region.getWidth());
// height = (int) Math.ceil(region.getHeight());
//
// // sometimes width or height is negative. Grrr...
// if (width < 0) {
// startx += width;
// width = -width;
// }
// if (height < 0) {
// starty += height;
// height = -height;
// }
//
// flags = 0;
// } else {
// startx = 0;
// starty = 0;
// width = imageinfo.width;
// height = imageinfo.height;
// }
// if (rendererFinished()) {
// flags |= ImageObserver.ALLBITS;
// // forget about the Graphics -- allows the image to be
// // garbage collected.
// g = null;
// } else {
// flags |= ImageObserver.SOMEBITS;
// }
//
// synchronized (observers) {
// for (Iterator i = observers.iterator(); i.hasNext();) {
// ImageObserver observer = (ImageObserver) i.next();
//
// boolean result = observer.imageUpdate(bi, flags,
// startx, starty,
// width, height);
//
// // if result is false, the observer no longer wants to
// // be notified of changes
// if (!result) {
// i.remove();
// }
// }
// }
// }
/**
* Convert an image mask into an image by painting over any pixels
* that have a value in the image with the current paint
*/
private Bitmap getMaskedImage(Bitmap bi) {
// get the color of the current paint
int col = state.fillPaint.getPaint().getColor();
// format as 8 bits each of ARGB
int paintColor = Color.alpha(col) << 24;
paintColor |= Color.red(col) << 16;
paintColor |= Color.green(col) << 8;
paintColor |= Color.blue(col);
// transparent (alpha = 1)
int noColor = 0;
// get the coordinates of the source image
int startX = 0;
int startY = 0;
int width = bi.getWidth();
int height = bi.getHeight();
// create a destion image of the same size
Bitmap dstImage = Bitmap.createBitmap(width, height, Config.ARGB_8888);
// copy the pixels row by row
for (int i = 0; i < height; i++) {
int[] srcPixels = new int[width];
int[] dstPixels = new int[srcPixels.length];
// read a row of pixels from the source
bi.getPixels(srcPixels, 0, 0, startX, startY + i, width, 1);
// bi.getRGB(startX, startY + i, width, 1, srcPixels, 0, height);
// figure out which ones should get painted
for (int j = 0; j < srcPixels.length; j++) {
if (srcPixels[j] == 0xff000000) {
dstPixels[j] = paintColor;
} else {
dstPixels[j] = noColor;
}
}
// write the destination image
// dstImage.setRGB(startX, startY + i, width, 1, dstPixels, 0, height);
dstImage.setPixels(dstPixels, 0, 0, startX, startY + i, width, 1);
}
return dstImage;
}
class GraphicsState implements Cloneable {
/** the clip region */
Rect cliprgn;
/** the current stroke */
Cap cap;
Join join;
float lineWidth;
float miterLimit;
/** the current paint for drawing strokes */
PDFPaint strokePaint;
/** the current paint for filling shapes */
PDFPaint fillPaint;
/** the current transform */
Matrix xform;
/** Clone this Graphics state.
*
* Note that cliprgn is not cloned. It must be set manually from
* the current graphics object's clip
*/
@Override
public Object clone() {
GraphicsState cState = new GraphicsState();
cState.cliprgn = null;
cState.cap = cap;
cState.join = join;
cState.strokePaint = strokePaint;
cState.fillPaint = fillPaint;
cState.xform = new Matrix(xform);
cState.lineWidth = lineWidth;
cState.miterLimit = miterLimit;
return cState;
}
}
}

View File

@ -0,0 +1,215 @@
/*
* $Id: PDFShapeCmd.java,v 1.3 2009/01/16 16:26:15 tomoke Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview;
import android.graphics.Path;
import android.graphics.RectF;
/**
* Encapsulates a path. Also contains extra fields and logic to check
* for consecutive abutting anti-aliased regions. We stroke the shared
* line between these regions again with a 1-pixel wide line so that
* the background doesn't show through between them.
*
* @author Mike Wessler
*/
public class PDFShapeCmd extends PDFCmd {
/** stroke the outline of the path with the stroke paint */
public static final int STROKE = 1;
/** fill the path with the fill paint */
public static final int FILL = 2;
/** perform both stroke and fill */
public static final int BOTH = 3;
/** set the clip region to the path */
public static final int CLIP = 4;
/** base path */
private Path gp;
/** the style */
private int style;
/** the bounding box of the path */
private RectF bounds;
/** the stroke style for the anti-antialias stroke */
// TODO [FHe]: againstroke
// BasicStroke againstroke =
// new BasicStroke(2, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL);
/**
* create a new PDFShapeCmd and check it against the previous one
* to find any shared edges.
* @param gp the path
* @param style the style: an OR of STROKE, FILL, or CLIP. As a
* convenience, BOTH = STROKE | FILL.
*/
public PDFShapeCmd(Path gp, int style) {
this.gp = new Path(gp);
this.style = style;
bounds = new RectF();
gp.computeBounds(bounds, false);
}
/**
* perform the stroke and record the dirty region
*/
public RectF execute(PDFRenderer state) {
RectF rect = null;
if ((style & FILL) != 0) {
rect = state.fill(gp);
// TODO [FHe]: againstroke
// Path strokeagain = checkOverlap(state);
// if (strokeagain != null) {
// state.draw(strokeagain, againstroke);
// }
if (gp != null) {
state.setLastShape(gp);
}
}
if ((style & STROKE) != 0) {
RectF strokeRect = state.stroke(gp);
if (rect == null) {
rect = strokeRect;
} else {
rect.union(strokeRect);
}
}
if ((style & CLIP) != 0) {
state.clip(gp);
}
return rect;
}
// /**
// * Check for overlap with the previous shape to make anti-aliased shapes
// * that are near each other look good
// */
// private Path checkOverlap(PDFRenderer state) {
// if (style == FILL && gp != null && state.getLastShape() != null) {
// float mypoints[] = new float[16];
// float prevpoints[] = new float[16];
//
// int mycount = getPoints(gp, mypoints);
// int prevcount = getPoints(state.getLastShape(), prevpoints);
//
// // now check mypoints against prevpoints for opposite pairs:
// if (mypoints != null && prevpoints != null) {
// for (int i = 0; i < prevcount; i += 4) {
// for (int j = 0; j < mycount; j += 4) {
// if ((Math.abs(mypoints[j + 2] - prevpoints[i]) < 0.01 &&
// Math.abs(mypoints[j + 3] - prevpoints[i + 1]) < 0.01 &&
// Math.abs(mypoints[j] - prevpoints[i + 2]) < 0.01 &&
// Math.abs(mypoints[j + 1] - prevpoints[i + 3]) < 0.01)) {
// GeneralPath strokeagain = new GeneralPath();
// strokeagain.moveTo(mypoints[j], mypoints[j + 1]);
// strokeagain.lineTo(mypoints[j + 2], mypoints[j + 3]);
// return strokeagain;
// }
// }
// }
// }
// }
//
// // no issues
// return null;
// }
//
// /**
// * Get an array of 16 points from a path
// * @return the number of points we actually got
// */
// private int getPoints(GeneralPath path, float[] mypoints) {
// int count = 0;
// float x = 0;
// float y = 0;
// float startx = 0;
// float starty = 0;
// float[] coords = new float[6];
//
// PathIterator pi = path.getPathIterator(new AffineTransform());
// while (!pi.isDone()) {
// if (count >= mypoints.length) {
// mypoints = null;
// break;
// }
//
// int pathtype = pi.currentSegment(coords);
// switch (pathtype) {
// case PathIterator.SEG_MOVETO:
// startx = x = coords[0];
// starty = y = coords[1];
// break;
// case PathIterator.SEG_LINETO:
// mypoints[count++] = x;
// mypoints[count++] = y;
// x = mypoints[count++] = coords[0];
// y = mypoints[count++] = coords[1];
// break;
// case PathIterator.SEG_QUADTO:
// x = coords[2];
// y = coords[3];
// break;
// case PathIterator.SEG_CUBICTO:
// x = mypoints[4];
// y = mypoints[5];
// break;
// case PathIterator.SEG_CLOSE:
// mypoints[count++] = x;
// mypoints[count++] = y;
// x = mypoints[count++] = startx;
// y = mypoints[count++] = starty;
// break;
// }
//
// pi.next();
// }
//
// return count;
// }
/** Get detailed information about this shape
*/
@Override
public String getDetails() {
StringBuffer sb = new StringBuffer();
RectF b = new RectF();
gp.computeBounds(b, false);
sb.append("ShapeCommand at: " + b.left + ", " + b.top + "\n");
sb.append("Size: " + b.width() + " x " + b.height() + "\n");
sb.append("Mode: ");
if ((style & FILL) != 0) {
sb.append("FILL ");
}
if ((style & STROKE) != 0) {
sb.append("STROKE ");
}
if ((style & CLIP) != 0) {
sb.append("CLIP");
}
return sb.toString();
}
}

View File

@ -0,0 +1,233 @@
/*
* Copyright 2008 Pirion Systems Pty Ltd, 139 Warry St,
* Fortitude Valley, Queensland, Australia
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview;
import java.io.*;
import java.util.Map;
import java.util.HashMap;
import java.nio.CharBuffer;
import java.nio.ByteBuffer;
import java.nio.charset.CharacterCodingException;
/**
* <p> Utility methods for dealing with PDF Strings, such as:
* <ul>
* <li>{@link #asTextString(String) converting to text strings}
* <li>{@link #asPDFDocEncoded(String) converting to PDFDocEncoded strings}
* <li>{@link #asUTF16BEEncoded converting to UTF-16BE strings}
* <li>converting basic strings between {@link #asBytes(String) byte} and
* {@link #asBasicString(byte[], int, int) string} representations
* </ul></p>
*
* <p>We refer to basic strings as those corresponding to the PDF 'string' type.
* PDFRenderer represents these as {@link String}s, though this is somewhat
* deceiving, as they are, effectively, just sequences of bytes, although byte
* values &lt;= 127 do correspond to the ASCII character set. Outside of this,
* the 'string' type, as repesented by basic strings do not possess any
* character set or encoding, and byte values &gt;= 128 are entirely acceptable.
* For a basic string as represented by a String, each character has a value
* less than 256 and is represented in the String as if the bytes represented as
* it were in ISO-8859-1 encoding. This, however, is merely for convenience. For
* strings that are user visible, and that don't merely represent some
* identifying token, the PDF standard employs a 'text string' type that offers
* the basic string as an encoding of in either UTF-16BE (with a byte order
* marking) or a specific 8-byte encoding, PDFDocEncoding. Using a basic string
* without conversion when the actual type is a 'text string' is erroneous
* (though without consequence if the string consists only of ASCII
* alphanumeric values). Care must be taken to either convert basic strings to
* text strings (also expressed as a String) when appropriate, using either the
* methods in this class, or {@link PDFObject#getTextStringValue()}}. For
* strings that are 'byte strings', {@link #asBytes(String)} or {@link
* PDFObject#getStream()} should be used. </p>.
*
* @author Luke Kirby
*/
public class PDFStringUtil {
/**
* <p>Take a basic PDF string and determine if it is in UTF-16BE encoding
* by looking at the lead characters for a byte order marking (BOM). If it
* appears to be UTF-16BE, we return the string representation of the
* UTF-16BE encoding of those bytes. If the BOM is not present, the bytes
* from the input string are decoded using the PDFDocEncoding charset.</p>
*
* <p>From the PDF Reference 1.7, p158:
*
* <blockquote>The text string type is used for character strings that are
* encoded in either PDFDocEncoding or the UTF-16BE Unicode character
* encoding scheme. PDFDocEncoding can encode all of the ISO Latin 1
* character set and is documented in Appendix D. UTF-16BE can encode all
* Unicode characters. UTF-16BE and Unicode character encoding are
* described in the Unicode Standard by the Unicode Consortium (see the
* Bibliography). Note that PDFDocEncoding does not support all Unicode
* characters whereas UTF-16BE does.</blockquote>
* </p>
*
* @param basicString the basic PDF string, as offered by {@link
* PDFObject#getStringValue()}
* @return either the original input, or the input decoded as UTF-16
*/
public static String asTextString(String basicString) {
if (basicString == null) {
return null;
}
if (basicString.length() >= 2) {
if ((basicString.charAt(0) == (char) 0xFE
&& basicString.charAt(1) == (char) 0xFF)) {
// found the BOM!
return asUTF16BEEncoded(basicString);
}
}
// it's not UTF16-BE encoded, so it must be
return asPDFDocEncoded(basicString);
}
/**
* Take a basic PDF string and produce a string of its bytes as encoded in
* PDFDocEncoding. The PDFDocEncoding is described in the PDF Reference.
*
* @param basicString the basic PDF string, as offered by {@link
* PDFObject#getStringValue()}
* @return the decoding of the string's bytes in PDFDocEncoding
*/
public static String asPDFDocEncoded(String basicString) {
final StringBuilder buf = new StringBuilder(basicString.length());
for (int i = 0; i < basicString.length(); ++i) {
final char c = PDF_DOC_ENCODING_MAP[basicString.charAt(i) & 0xFF];
buf.append(c);
}
return buf.toString();
}
public byte[] toPDFDocEncoded(String string)
throws CharacterCodingException {
// we can just grab array since we know that if charset completes
// without error then there's the output buffer will be exactly
// correct in size, since there's only ever 1 byte for one char.
return new PDFDocCharsetEncoder().encode(CharBuffer.wrap(string)).
array();
}
/**
* Take a basic PDF string and produce a string from its bytes as an
* UTF16-BE encoding. The first 2 bytes are presumed to be the big-endian
* byte markers, 0xFE and 0xFF; that is not checked by this method.
*
* @param basicString the basic PDF string, as offered by {@link
* PDFObject#getStringValue()}
* @return the decoding of the string's bytes in UTF16-BE
*/
public static String asUTF16BEEncoded(String basicString) {
try {
return new String(asBytes(basicString),
2, basicString.length() - 2, "UTF-16BE");
} catch (UnsupportedEncodingException e) {
// UTF-16BE should always be available
throw new RuntimeException("No UTF-16BE charset!");
}
}
/**
* Get the corresponding byte array for a basic string. This is effectively
* the char[] array cast to bytes[], as chars in basic strings only use the
* least significant byte.
*
* @param basicString the basic PDF string, as offered by {@link
* PDFObject#getStringValue()}
* @return the bytes corresponding to its characters
*/
public static byte[] asBytes(String basicString) {
final byte[] b = new byte[basicString.length()];
for (int i = 0; i < b.length; ++i) {
b[i] = (byte) basicString.charAt(i);
}
return b;
}
/**
* Create a basic string from bytes. This is effectively the byte array
* cast to a char array and turned into a String.
* @param bytes the source of the bytes for the basic string
* @param offset the offset into butes where the string starts
* @param length the number of bytes to turn into a string
* @return the corresponding string
*/
public static String asBasicString(
byte[] bytes, int offset, int length) {
final char[] c = new char[length];
for (int i = 0; i < c.length; ++i) {
c[i] = (char) bytes[i + offset];
}
return new String(c);
}
/**
* Create a basic string from bytes. This is effectively the byte array
* cast to a char array and turned into a String.
* @param bytes the bytes, all of which are used
* @return the corresponding string
*/
public static String asBasicString(byte[] bytes) {
return asBasicString(bytes, 0, bytes.length);
}
/**
* Maps from PDFDocEncoding bytes to unicode characters. Table generated
* by PDFDocEncodingMapGenerator.
*/
final static char[] PDF_DOC_ENCODING_MAP = new char[] {
0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, //00-07
0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, //08-0F
0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, //10-17
0x02D8, 0x02C7, 0x02C6, 0x02D9, 0x02DD, 0x02DB, 0x02DA, 0x02DC, //18-1F
0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, //20-27
0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, //28-2F
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, //30-37
0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, //38-3F
0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, //40-47
0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, //48-4F
0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, //50-57
0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, //58-5F
0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, //60-67
0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, //68-6F
0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, //70-77
0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0xFFFD, //78-7F
0x2022, 0x2020, 0x2021, 0x2026, 0x2014, 0x2013, 0x0192, 0x2044, //80-87
0x2039, 0x203A, 0x2212, 0x2030, 0x201E, 0x201C, 0x201D, 0x2018, //88-8F
0x2019, 0x201A, 0x2122, 0xFB01, 0xFB02, 0x0141, 0x0152, 0x0160, //90-97
0x0178, 0x017D, 0x0131, 0x0142, 0x0153, 0x0161, 0x017E, 0xFFFD, //98-9F
0x20AC, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, //A0-A7
0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0xFFFD, 0x00AE, 0x00AF, //A8-AF
0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, //B0-B7
0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, //B8-BF
0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, //C0-C7
0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, //C8-CF
0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, //D0-D7
0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, //D8-DF
0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, //E0-E7
0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, //E8-EF
0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, //F0-F7
0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF, //F8-FF
};
}

View File

@ -0,0 +1,396 @@
/*
* $Id: PDFTextFormat.java,v 1.3 2009/01/16 16:26:11 tomoke Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview;
import java.util.List;
import net.sf.andpdf.utils.Utils;
import android.graphics.Matrix;
import android.graphics.PointF;
import com.sun.pdfview.font.OutlineFont;
import com.sun.pdfview.font.PDFFont;
import com.sun.pdfview.font.PDFGlyph;
/**
* a class encapsulating the text state
*
* @author Mike Wessler
* @author Ferenc Hechler (ferenc@hechler.de)
* @author Joerg Jahnke (joergjahnke@users.sourceforge.net)
*/
public class PDFTextFormat implements Cloneable {
/** character spacing */
private float tc = 0;
/** word spacing */
private float tw = 0;
/** horizontal scaling */
private float th = 1;
/** leading */
private float tl = 0;
/** rise amount */
private float tr = 0;
/** text mode */
private int tm = PDFShapeCmd.FILL;
/** text knockout */
private float tk = 0;
/** current matrix transform */
private final Matrix cur;
/** matrix transform at start of line */
private Matrix line;
/** font */
private PDFFont font;
/** font size */
private float fsize = 1;
/** are we between BT and ET? */
private boolean inuse = false;
// private Object array[]= new Object[1];
/** build text rep of word */
private StringBuffer word = new StringBuffer();
// this is where we build and keep the word list for this page.
/** start location of the hunk of text */
private PointF wordStart;
/** location of the end of the previous hunk of text */
private final PointF prevEnd;
/**
* create a new PDFTextFormat, with initial values
*/
public PDFTextFormat() {
cur = new Matrix();
line = new Matrix();
wordStart = new PointF(-100, -100);
prevEnd = new PointF(-100, -100);
tc = tw = tr = tk = 0;
tm = PDFShapeCmd.FILL;
th = 1;
}
/**
* reset the PDFTextFormat for a new run
*/
public void reset() {
cur.reset();
line.reset();
inuse = true;
word.setLength(0);
}
/**
* end a span of text
*/
public void end() {
inuse = false;
}
/** get the char spacing */
public float getCharSpacing() {
return tc;
}
/** set the character spacing */
public void setCharSpacing(float spc) {
this.tc = spc;
}
/** get the word spacing */
public float getWordSpacing() {
return tw;
}
/** set the word spacing */
public void setWordSpacing(float spc) {
this.tw = spc;
}
/**
* Get the horizontal scale
* @return the horizontal scale, in percent
*/
public float getHorizontalScale() {
return th * 100;
}
/**
* set the horizontal scale.
* @param scl the horizontal scale, in percent (100=normal)
*/
public void setHorizontalScale(float scl) {
this.th = scl / 100;
}
/** get the leading */
public float getLeading() {
return tl;
}
/** set the leading */
public void setLeading(float spc) {
this.tl = spc;
}
/** get the font */
public PDFFont getFont() {
return font;
}
/** get the font size */
public float getFontSize() {
return fsize;
}
/** set the font and size */
public void setFont(PDFFont f, float size) {
this.font = f;
this.fsize = size;
}
/**
* Get the mode of the text
*/
public int getMode() {
return tm;
}
/**
* set the mode of the text. The correspondence of m to mode is
* show in the following table. m is a value from 0-7 in binary:
*
* 000 Fill
* 001 Stroke
* 010 Fill + Stroke
* 011 Nothing
* 100 Fill + Clip
* 101 Stroke + Clip
* 110 Fill + Stroke + Clip
* 111 Clip
*
* Therefore: Fill corresponds to the low bit being 0; Clip
* corresponds to the hight bit being 1; and Stroke corresponds
* to the middle xor low bit being 1.
*/
public void setMode(int m) {
int mode = 0;
if ((m & 0x1) == 0) {
mode |= PDFShapeCmd.FILL;
}
if ((m & 0x4) != 0) {
mode |= PDFShapeCmd.CLIP;
}
if (((m & 0x1) ^ ((m & 0x2) >> 1)) != 0) {
mode |= PDFShapeCmd.STROKE;
}
this.tm = mode;
}
/**
* Set the mode from another text format mode
*
* @param mode the text render mode using the
* codes from PDFShapeCmd and not the wacky PDF codes
*/
public void setTextFormatMode(int mode) {
this.tm = mode;
}
/**
* Get the rise
*/
public float getRise() {
return tr;
}
/**
* set the rise
*/
public void setRise(float spc) {
this.tr = spc;
}
/**
* perform a carriage return
*/
public void carriageReturn() {
carriageReturn(0, -tl);
}
/**
* perform a carriage return by translating by x and y. The next
* carriage return will be relative to the new location.
*/
public void carriageReturn(float x, float y) {
Matrix trans = new Matrix();
trans.setTranslate(x, y);
line.preConcat(trans);
cur.set(line);
}
/**
* Get the current transform
*/
public Matrix getTransform() {
return cur;
}
/**
* set the transform matrix directly
*/
public void setMatrix(float[] matrix) {
line = new Matrix();
Utils.setMatValues(line, matrix);
cur.set(line);
}
public void doText(final PDFPage cmds, final String text) {
if (PDFFont.sUseFontSubstitution)
doTextFontSubst(cmds, text);
else
doTextNormal(cmds, text);
}
/**
* add some text to the page.
* @param cmds the PDFPage to add the commands to
* @param text the text to add
*/
private void doTextFontSubst(final PDFPage cmds, final String text) {
final PointF zero = new PointF();
final Matrix scale = new Matrix();
Utils.setMatValues(scale, fsize, 0, 0, fsize * th, 0, tr);
final Matrix at = new Matrix();
at.set(cur);
at.preConcat(scale);
PDFNativeTextCmd ntx = new PDFNativeTextCmd(text, at);
cmds.addCommand(ntx);
// calc widths
for (int i=0; i<text.length(); i++) {
char c = text.charAt(0);
float width = 0.6f;
if (font instanceof OutlineFont)
width = ((OutlineFont)font).getWidth(c, null);
float advanceX = (width * fsize) + tc;
if (c == ' ') {
advanceX += tw;
}
advanceX *= th;
cur.preTranslate(advanceX, 0);
}
final float[] src = {zero.x, zero.y};
final float[] dst = new float[src.length];
cur.mapPoints(dst, src);
prevEnd.set(dst[0], dst[1]);
}
/**
* add some text to the page.
* @param cmds the PDFPage to add the commands to
* @param text the text to add
*/
private void doTextNormal(final PDFPage cmds, final String text) {
final PointF zero = new PointF();
final Matrix scale = new Matrix();
Utils.setMatValues(scale, fsize, 0, 0, fsize * th, 0, tr);
final Matrix at = new Matrix();
final List<PDFGlyph> l = (List<PDFGlyph>) font.getGlyphs(text);
for (final PDFGlyph glyph : l) {
at.set(cur);
at.preConcat(scale);
final PointF advance = glyph.addCommands(cmds, at, tm);
float advanceX = (advance.x * fsize) + tc;
if (glyph.getChar() == ' ') {
advanceX += tw;
}
advanceX *= th;
cur.preTranslate(advanceX, advance.y);
}
final float[] src = {zero.x, zero.y};
final float[] dst = new float[src.length];
cur.mapPoints(dst, src);
prevEnd.set(dst[0], dst[1]);
}
/**
* add some text to the page.
* @param cmds the PDFPage to add the commands to
* @param ary an array of Strings and Doubles, where the Strings
* represent text to be added, and the Doubles represent kerning
* amounts.
*/
public void doText(final PDFPage cmds, final Object ary[]) throws PDFParseException {
for (int i = 0, to = ary.length; i < to; ++i) {
if (ary[i] instanceof String) {
doText(cmds, (String) ary[i]);
} else if (ary[i] instanceof Double) {
float val = ((Double) ary[i]).floatValue() / 1000f;
cur.preTranslate(-val * fsize * th, 0);
} else {
throw new PDFParseException("Bad element in TJ array");
}
}
}
/**
* finish any unfinished words. TODO: write this!
*/
public void flush() {
// TODO: finish any unfinished words
}
/**
* Clone the text format
*/
@Override
public Object clone() {
PDFTextFormat newFormat = new PDFTextFormat();
// copy values
newFormat.setCharSpacing(getCharSpacing());
newFormat.setWordSpacing(getWordSpacing());
newFormat.setHorizontalScale(getHorizontalScale());
newFormat.setLeading(getLeading());
newFormat.setTextFormatMode(getMode());
newFormat.setRise(getRise());
// copy immutable fields
newFormat.setFont(getFont(), getFontSize());
// clone transform (mutable)
// newFormat.getTransform().setTransform(getTransform());
return newFormat;
}
}

View File

@ -0,0 +1,143 @@
/*
* $Id: PDFXref.java,v 1.4 2009/02/12 13:53:56 tomoke Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview;
import net.sf.andpdf.refs.SoftReference;
/**
* a cross reference representing a line in the PDF cross referencing
* table.
* <p>
* There are two forms of the PDFXref, destinguished by absolutely nothing.
* The first type of PDFXref is used as indirect references in a PDFObject.
* In this type, the id is an index number into the object cross reference
* table. The id will range from 0 to the size of the cross reference
* table.
* <p>
* The second form is used in the Java representation of the cross reference
* table. In this form, the id is the file position of the start of the
* object in the PDF file. See the use of both of these in the
* PDFFile.dereference() method, which takes a PDFXref of the first form,
* and uses (internally) a PDFXref of the second form.
* <p>
* This is an unhappy state of affairs, and should be fixed. Fortunatly,
* the two uses have already been factored out as two different methods.
*
* @author Mike Wessler
*/
public class PDFXref {
private int id;
private int generation;
private boolean compressed;
// this field is only used in PDFFile.objIdx
private SoftReference<PDFObject> reference = null;
/**
* create a new PDFXref, given a parsed id and generation.
*/
public PDFXref(int id, int gen) {
this.id = id;
this.generation = gen;
this.compressed = false;
}
/**
* create a new PDFXref, given a parsed id, compressedObjId and index
*/
public PDFXref(int id, int gen, boolean compressed) {
this.id = id;
this.generation = gen;
this.compressed = compressed;
}
/**
* create a new PDFXref, given a sequence of bytes representing the
* fixed-width cross reference table line
*/
public PDFXref(byte[] line) {
if (line == null) {
id = -1;
generation = -1;
} else {
id = Integer.parseInt(new String(line, 0, 10));
generation = Integer.parseInt(new String(line, 11, 5));
}
compressed = false;
}
/**
* get the character index into the file of the start of this object
*/
public int getFilePos() {
return id;
}
/**
* get the generation of this object
*/
public int getGeneration() {
return generation;
}
/**
* get the generation of this object
*/
public int getIndex() {
return generation;
}
/**
* get the object number of this object
*/
public int getID() {
return id;
}
/**
* get compressed flag of this object
*/
public boolean getCompressed() {
return compressed;
}
/**
* Get the object this reference refers to, or null if it hasn't been
* set.
* @return the object if it exists, or null if not
*/
public PDFObject getObject() {
if (reference != null) {
return (PDFObject) reference.get();
}
return null;
}
/**
* Set the object this reference refers to.
*/
public void setObject(PDFObject obj) {
this.reference = new SoftReference<PDFObject>(obj);
}
}

View File

@ -0,0 +1,57 @@
/*
* $Id: RefImage.java,v 1.3 2009/01/16 16:26:10 tomoke Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Bitmap.Config;
/**
* A BufferedImage subclass that holds a strong reference to its graphics
* object. This means that the graphics will never go away as long as
* someone holds a reference to this image, and createGraphics() and
* getGraphics() can be called multiple times safely, and will always return
* the same graphics object.
*/
public class RefImage {
/** a strong reference to the graphics object */
private Bitmap bi;
private Canvas g;
/** Creates a new instance of RefImage */
public RefImage(int width, int height, Config config) {
bi = Bitmap.createBitmap(width, height, config);
}
/**
* Create a graphics object only if it is currently null, otherwise
* return the existing graphics object.
*/
public Canvas createGraphics() {
if (g == null) {
g = new Canvas(bi);
}
return g;
}
}

View File

@ -0,0 +1,73 @@
/*
* $Id: Watchable.java,v 1.3 2009/01/16 16:26:15 tomoke Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview;
/**
* An interface for rendering or parsing, which can be stopped and started.
*/
public interface Watchable {
/** the possible statuses */
public static final int UNKNOWN = 0;
public static final int NOT_STARTED = 1;
public static final int PAUSED = 2;
public static final int NEEDS_DATA = 3;
public static final int RUNNING = 4;
public static final int STOPPED = 5;
public static final int COMPLETED = 6;
public static final int ERROR = 7;
/**
* Get the status of this watchable
*
* @return one of the well-known statuses
*/
public int getStatus();
/**
* Stop this watchable. Stop will cause all processing to cease,
* and the watchable to be destroyed.
*/
public void stop();
/**
* Start this watchable and run until it is finished or stopped.
* Note the watchable may be stopped if go() with a
* different time is called during execution.
*/
public void go();
/**
* Start this watchable and run for the given number of steps or until
* finished or stopped.
*
* @param steps the number of steps to run for
*/
public void go(int steps);
/**
* Start this watchable and run for the given amount of time, or until
* finished or stopped.
*
* @param millis the number of milliseconds to run for
*/
public void go(long millis);
}

View File

@ -0,0 +1,70 @@
/*
* $Id: GoToAction.java,v 1.2 2007/12/20 18:33:34 rbair Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview.action;
import java.io.IOException;
import com.sun.pdfview.PDFObject;
import com.sun.pdfview.PDFDestination;
import com.sun.pdfview.PDFParseException;
/**
* An action which specifies going to a particular destination
*/
public class GoToAction extends PDFAction {
/** the destination to go to */
private PDFDestination dest;
/**
* Creates a new instance of GoToAction from an object
*
* @param obj the PDFObject with the action information
*/
public GoToAction(PDFObject obj, PDFObject root) throws IOException {
super("GoTo");
// find the destination
PDFObject destObj = obj.getDictRef("D");
if (destObj == null) {
throw new PDFParseException("No destination in GoTo action " + obj);
}
// parse it
dest = PDFDestination.getDestination(destObj, root);
}
/**
* Create a new GoToAction from a destination
*/
public GoToAction(PDFDestination dest) {
super("GoTo");
this.dest = dest;
}
/**
* Get the destination this action refers to
*/
public PDFDestination getDestination() {
return dest;
}
}

View File

@ -0,0 +1,100 @@
/*
* $Id: PDFAction.java,v 1.2 2007/12/20 18:33:34 rbair Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview.action;
import java.io.IOException;
import com.sun.pdfview.PDFObject;
import com.sun.pdfview.PDFParseException;
/**
* The common super-class of all PDF actions.
*/
public class PDFAction {
/** the type of this action */
private String type;
/** the next action or array of actions */
private PDFObject next;
/** Creates a new instance of PDFAction */
public PDFAction(String type) {
this.type = type;
}
/**
* Get an action of the appropriate type from a PDFObject
*
* @param obj the PDF object containing the action to parse
* @param root the root of the PDF object tree
*/
public static PDFAction getAction(PDFObject obj, PDFObject root)
throws IOException
{
// figure out the action type
PDFObject typeObj = obj.getDictRef("S");
if (typeObj == null) {
throw new PDFParseException("No action type in object: " + obj);
}
// create the action based on the type
PDFAction action = null;
String type = typeObj.getStringValue();
if (type.equals("GoTo")) {
action = new GoToAction(obj, root);
} else {
/** [JK FIXME: Implement other action types! ] */
throw new PDFParseException("Unknown Action type: " + type);
}
// figure out if there is a next action
PDFObject nextObj = obj.getDictRef("Next");
if (nextObj != null) {
action.setNext(nextObj);
}
// return the action
return action;
}
/**
* Get the type of this action
*/
public String getType() {
return type;
}
/**
* Get the next action or array of actions
*/
public PDFObject getNext() {
return next;
}
/**
* Set the next action or array of actions
*/
public void setNext(PDFObject next) {
this.next = next;
}
}

View File

@ -0,0 +1,88 @@
/*
* $Id: CalRGBColor.java,v 1.2 2007/12/20 18:33:34 rbair Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview.colorspace;
import com.sun.pdfview.function.PDFFunction;
import android.graphics.Color;
public class AlternateColorSpace extends PDFColorSpace {
/** The alternate color space */
private PDFColorSpace alternate;
/** The function */
private PDFFunction function;
/** Creates a new instance of AlternateColorSpace */
public AlternateColorSpace(PDFColorSpace alternate, PDFFunction function) {
this.alternate = alternate;
this.function = function;
}
/**
* get the number of components expected in the getPaint command
*/
@Override public int getNumComponents() {
if (function != null) {
return function.getNumInputs();
} else {
return alternate.getNumComponents();
}
}
@Override public int toColor(float[] fcomp) {
if (function != null) {
// translate values using function
fcomp = function.calculate(fcomp);
}
float k = fcomp[3];
float w = 255*(1-k);
float r = w*(1-fcomp[0]);
float g = w*(1-fcomp[1]);
float b = w*(1-fcomp[2]);
return Color.rgb((int)r,(int)g,(int)b);
}
@Override public int toColor(int[] icomp) {
float[] fcomp = new float[icomp.length];
for (int i = 0; i < fcomp.length; i++)
fcomp[i] = icomp[i]/255;
return toColor(fcomp);
}
/**
* get the type of this color space (TYPE_CMYK)
*/
@Override public int getType() {
return COLORSPACE_ALTERNATE;
}
@Override
public String getName() {
return "ALTERNATE";
}
}

View File

@ -0,0 +1,71 @@
/*
* $Id: CalRGBColor.java,v 1.2 2007/12/20 18:33:34 rbair Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview.colorspace;
import android.graphics.Color;
public class CMYKColorSpace extends PDFColorSpace {
public CMYKColorSpace() {
}
/**
* get the number of components (4)
*/
@Override public int getNumComponents() {
return 4;
}
@Override public int toColor(float[] fcomp) {
float k = fcomp[3];
float w = 255*(1-k);
float r = w*(1-fcomp[0]);
float g = w*(1-fcomp[1]);
float b = w*(1-fcomp[2]);
return Color.rgb((int)r,(int)g,(int)b);
}
@Override public int toColor(int[] icomp) {
int k = icomp[3];
int w = 255-k;
int r = w*(255-icomp[0])/255;
int g = w*(255-icomp[1])/255;
int b = w*(255-icomp[2])/255;
return Color.rgb(r,g,b);
}
/**
* get the type of this color space (TYPE_CMYK)
*/
@Override public int getType() {
return COLORSPACE_CMYK;
}
@Override
public String getName() {
return "CMYK";
}
}

View File

@ -0,0 +1,60 @@
/*
* $Id: CalRGBColor.java,v 1.2 2007/12/20 18:33:34 rbair Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview.colorspace;
import android.graphics.Color;
public class GrayColorSpace extends PDFColorSpace {
public GrayColorSpace() {
}
/**
* get the number of components (3)
*/
@Override public int getNumComponents() {
return 1;
}
@Override public int toColor(float[] fcomp) {
return Color.rgb((int)(fcomp[0]*255),(int)(fcomp[0]*255),(int)(fcomp[0]*255));
}
@Override public int toColor(int[] icomp) {
return Color.rgb(icomp[0],icomp[0],icomp[0]);
}
/**
* get the type of this color space (TYPE_RGB)
*/
@Override public int getType() {
return COLORSPACE_GRAY;
}
@Override
public String getName() {
return "G";
}
}

View File

@ -0,0 +1,125 @@
/*
* $Id: IndexedColor.java,v 1.4 2009/01/26 05:40:42 tomoke Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview.colorspace;
import java.io.IOException;
import com.sun.pdfview.PDFObject;
import com.sun.pdfview.PDFPaint;
/**
* A PDFColorSpace for an IndexedColor model
*
* @author Mike Wessler
*/
public class IndexedColor extends PDFColorSpace {
/** the color table */
int table[];
/** size of the color table */
int count;
/**
* create a new IndexColor PDFColorSpace based on another PDFColorSpace,
* a count of colors, and a stream of values. Every consecutive n bytes
* of the stream is interpreted as a color in the base ColorSpace, where
* n is the number of components in that color space.
*
* @param base the color space in which the data is interpreted
* @param count the number of colors in the table
* @param stream a stream of bytes. The number of bytes must be count*n,
* where n is the number of components in the base colorspace.
*/
public IndexedColor(PDFColorSpace base, int count, PDFObject stream) throws IOException {
count++;
this.count = count;
byte[] data = stream.getStream();
int nchannels = base.getNumComponents();
boolean offSized = (data.length / nchannels) < count;
table = new int[count];
float comps[] = new float[nchannels];
int loc = 0;
int finalloc = 0;
for (int i = 0; i < count; i++) {
for (int j = 0; j < comps.length; j++) {
if (loc < data.length) {
comps[j] = (((int) data[loc++]) & 0xff) / 255f;
} else {
comps[j] = 1.0f;
}
}
table[i] = base.toColor(comps);
}
}
/**
* create a new IndexColor PDFColorSpace based on a table of colors.
*
* @param table an array of colors
*/
public IndexedColor(int[] table) throws IOException {
this.count = table.length;
this.table = table;
}
/**
* Get the number of indices
*/
public int getCount() {
return count;
}
/**
* Get the table of color components
*/
public int[] getColorTable() {
return table;
}
/**
* get the number of components of this colorspace (1)
*/
@Override
public int getNumComponents() {
return 1;
}
@Override
public String getName() {
return "I";
}
@Override
public int getType() {
return COLORSPACE_INDEXED;
}
@Override
public int toColor(float[] comp) {
return table[(int)(255*comp[0])];
}
@Override
public int toColor(int[] comp) {
return table[comp[0]];
}
}

View File

@ -0,0 +1,214 @@
/*
* $Id: PDFColorSpace.java,v 1.5 2009/03/08 20:46:16 tomoke Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview.colorspace;
import java.io.IOException;
import java.util.Map;
import com.sun.pdfview.PDFObject;
import com.sun.pdfview.PDFPaint;
import com.sun.pdfview.PDFParseException;
import com.sun.pdfview.function.PDFFunction;
/**
* A color space that can convert a set of color components into
* PDFPaint.
* @author Mike Wessler
*/
public abstract class PDFColorSpace {
/** the name of the device-dependent gray color space */
public static final int COLORSPACE_GRAY = 0;
/** the name of the device-dependent RGB color space */
public static final int COLORSPACE_RGB = 1;
/** the name of the device-dependent CMYK color space */
public static final int COLORSPACE_CMYK = 2;
/** the name of the pattern color space */
public static final int COLORSPACE_PATTERN = 3;
public static final int COLORSPACE_INDEXED = 4;
public static final int COLORSPACE_ALTERNATE = 5;
/** the device-dependent color spaces */
// private static PDFColorSpace graySpace =
// new PDFColorSpace(ColorSpace.getInstance(ColorSpace.CS_GRAY));
private static PDFColorSpace rgbSpace = new RGBColorSpace();
private static PDFColorSpace cmykSpace = new CMYKColorSpace();
/** the pattern space */
private static PDFColorSpace patternSpace = new RGBColorSpace(); // TODO [FHe]
/** graySpace and the gamma correction for it. */
private static PDFColorSpace graySpace = new GrayColorSpace();
/**
* create a PDFColorSpace based on a Java ColorSpace
* @param cs the Java ColorSpace
*/
protected PDFColorSpace() {
}
/**
* Get a color space by name
*
* @param name the name of one of the device-dependent color spaces
*/
public static PDFColorSpace getColorSpace(int name) {
switch (name) {
case COLORSPACE_GRAY:
return graySpace;
case COLORSPACE_RGB:
return rgbSpace;
case COLORSPACE_CMYK:
return cmykSpace;
case COLORSPACE_PATTERN:
return patternSpace;
default:
throw new IllegalArgumentException("Unknown Color Space name: " +
name);
}
}
/**
* Get a color space specified in a PDFObject
*
* @param csobj the PDFObject with the colorspace information
*/
public static PDFColorSpace getColorSpace(PDFObject csobj, Map resources)
throws IOException {
String name;
PDFObject colorSpaces = null;
if (resources != null) {
colorSpaces = (PDFObject) resources.get("ColorSpace");
}
if (csobj.getType() == PDFObject.NAME) {
name = csobj.getStringValue();
if (name.equals("DeviceGray") || name.equals("G")) {
return getColorSpace(COLORSPACE_GRAY);
} else if (name.equals("DeviceRGB") || name.equals("RGB")) {
return getColorSpace(COLORSPACE_RGB);
} else if (name.equals("DeviceCMYK") || name.equals("CMYK")) {
return getColorSpace(COLORSPACE_CMYK);
} else if (name.equals("Pattern")) {
return getColorSpace(COLORSPACE_PATTERN);
} else if (colorSpaces != null) {
csobj = (PDFObject) colorSpaces.getDictRef(name);
}
}
if (csobj == null) {
return null;
} else if (csobj.getCache() != null) {
return (PDFColorSpace) csobj.getCache();
}
PDFColorSpace value = null;
// csobj is [/name <<dict>>]
PDFObject[] ary = csobj.getArray();
name = ary[0].getStringValue();
if (name.equals("CalGray")) {
value = graySpace; // TODO [FHe]
} else if (name.equals("CalRGB")) {
value = rgbSpace; // TODO [FHe]
} else if (name.equals("Lab")) {
value = rgbSpace; // TODO [FHe]
} else if (name.equals("ICCBased")) {
value = rgbSpace; // TODO [FHe]
} else if (name.equals("Separation") || name.equals("DeviceN")) {
PDFColorSpace alternate = getColorSpace(ary[2], resources);
PDFFunction function = PDFFunction.getFunction(ary[3]);
value = new AlternateColorSpace(alternate, function);
} else if (name.equals("Indexed") || name.equals("I")) {
/**
* 4.5.5 [/Indexed baseColor hival lookup]
*/
PDFColorSpace refspace = getColorSpace(ary[1], resources);
// number of indices= ary[2], data is in ary[3];
int count = ary[2].getIntValue();
value = new IndexedColor(refspace, count, ary[3]);
} else if (name.equals("Pattern")) {
return rgbSpace; // TODO [FHe]
} else {
throw new PDFParseException("Unknown color space: " + name +
" with " + ary[1]);
}
csobj.setCache(value);
return value;
}
/**
* get the number of components expected in the getPaint command
*/
public abstract int getNumComponents();
/**
* get the PDFPaint representing the color described by the
* given color components
* @param components the color components corresponding to the given
* colorspace
* @return a PDFPaint object representing the closest Color to the
* given components.
*/
public PDFPaint getPaint(float[] components) {
return PDFPaint.getColorPaint(toColor(components));
}
public PDFPaint getFillPaint(float[] components) {
return PDFPaint.getPaint(toColor(components));
}
/**
* get the type of this color space
*/
public abstract int getType();
/**
* get the name of this color space
*/
public abstract String getName();
public abstract int toColor(float[] fcomp);
public abstract int toColor(int[] icomp);
@Override
public String toString() {
return "ColorSpace["+getName()+"]";
}
}

View File

@ -0,0 +1,61 @@
/*
* $Id: CalRGBColor.java,v 1.2 2007/12/20 18:33:34 rbair Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview.colorspace;
import android.graphics.Color;
public class RGBColorSpace extends PDFColorSpace {
public RGBColorSpace() {
}
/**
* get the number of components (3)
*/
@Override public int getNumComponents() {
return 3;
}
@Override public int toColor(float[] fcomp) {
return Color.rgb((int)(fcomp[0]*255),(int)(fcomp[1]*255),(int)(fcomp[2]*255));
}
@Override public int toColor(int[] icomp) {
return Color.rgb(icomp[0],icomp[1],icomp[2]);
}
/**
* get the type of this color space (TYPE_RGB)
*/
@Override public int getType() {
return COLORSPACE_RGB;
}
@Override
public String getName() {
return "RGB";
}
}

View File

@ -0,0 +1,154 @@
/*
* $Id: ASCII85Decode.java,v 1.3 2009/02/22 00:52:35 tomoke Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview.decode;
import java.io.ByteArrayOutputStream;
import net.sf.andpdf.nio.ByteBuffer;
import com.sun.pdfview.PDFFile;
import com.sun.pdfview.PDFObject;
import com.sun.pdfview.PDFParseException;
/**
* decode ASCII85 text into a byte array.
*
* @author Mike Wessler
*/
public class ASCII85Decode {
private ByteBuffer buf;
/**
* initialize the decoder with byte buffer in ASCII85 format
*/
private ASCII85Decode(ByteBuffer buf) {
this.buf = buf;
}
/**
* get the next character from the input.
* @return the next character, or -1 if at end of stream
*/
private int nextChar() {
// skip whitespace
// returns next character, or -1 if end of stream
while (buf.remaining() > 0) {
char c = (char) buf.get();
if (!PDFFile.isWhiteSpace(c)) {
return c;
}
}
// EOF reached
return -1;
}
/**
* decode the next five ASCII85 characters into up to four decoded
* bytes. Return false when finished, or true otherwise.
*
* @param baos the ByteArrayOutputStream to write output to, set to the
* correct position
* @return false when finished, or true otherwise.
*/
private boolean decode5(ByteArrayOutputStream baos)
throws PDFParseException {
// stream ends in ~>
int[] five = new int[5];
int i;
for (i = 0; i < 5; i++) {
five[i] = nextChar();
if (five[i] == '~') {
if (nextChar() == '>') {
break;
} else {
throw new PDFParseException("Bad character in ASCII85Decode: not ~>");
}
} else if (five[i] >= '!' && five[i] <= 'u') {
five[i] -= '!';
} else if (five[i] == 'z') {
if (i == 0) {
five[i] = 0;
i = 4;
} else {
throw new PDFParseException("Inappropriate 'z' in ASCII85Decode");
}
} else {
throw new PDFParseException("Bad character in ASCII85Decode: " + five[i] + " (" + (char) five[i] + ")");
}
}
if (i > 0) {
i -= 1;
}
int value =
five[0] * 85 * 85 * 85 * 85 +
five[1] * 85 * 85 * 85 +
five[2] * 85 * 85 +
five[3] * 85 +
five[4];
for (int j = 0; j < i; j++) {
int shift = 8 * (3 - j);
baos.write((byte) ((value >> shift) & 0xff));
}
return (i == 4);
}
/**
* decode the bytes
* @return the decoded bytes
*/
private ByteBuffer decode() throws PDFParseException {
// start from the beginning of the data
buf.rewind();
// allocate the output buffer
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// decode the bytes
while (decode5(baos)) {
}
return ByteBuffer.wrap(baos.toByteArray());
}
/**
* decode an array of bytes in ASCII85 format.
* <p>
* In ASCII85 format, every 5 characters represents 4 decoded
* bytes in base 85. The entire stream can contain whitespace,
* and ends in the characters '~&gt;'.
*
* @param buf the encoded ASCII85 characters in a byte buffer
* @param params parameters to the decoder (ignored)
* @return the decoded bytes
*/
public static ByteBuffer decode(ByteBuffer buf, PDFObject params)
throws PDFParseException {
ASCII85Decode me = new ASCII85Decode(buf);
return me.decode();
}
}

View File

@ -0,0 +1,128 @@
/*
* $Id: ASCIIHexDecode.java,v 1.2 2007/12/20 18:33:32 rbair Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview.decode;
import java.io.ByteArrayOutputStream;
import net.sf.andpdf.nio.ByteBuffer;
import com.sun.pdfview.PDFObject;
import com.sun.pdfview.PDFParseException;
import com.sun.pdfview.PDFFile;
/**
* decode an array of hex nybbles into a byte array
*
* @author Mike Wessler
*/
public class ASCIIHexDecode {
private ByteBuffer buf;
/**
* initialize the decoder with an array of bytes in ASCIIHex format
*/
private ASCIIHexDecode(ByteBuffer buf) {
this.buf = buf;
}
/**
* get the next character from the input
* @return a number from 0-15, or -1 for the end character
*/
private int readHexDigit() throws PDFParseException {
// read until we hit a non-whitespace character or the
// end of the stream
while (buf.remaining() > 0) {
int c = (int) buf.get();
// see if we found a useful character
if (!PDFFile.isWhiteSpace((char) c)) {
if (c >= '0' && c <= '9') {
c -= '0';
} else if (c >= 'a' && c <= 'f') {
c -= 'a' - 10;
} else if (c >= 'A' && c <= 'F') {
c -= 'A' - 10;
} else if (c == '>') {
c = -1;
} else {
// unknown character
throw new PDFParseException("Bad character " + c +
"in ASCIIHex decode");
}
// return the useful character
return c;
}
}
// end of stream reached
throw new PDFParseException("Short stream in ASCIIHex decode");
}
/**
* decode the array
* @return the decoded bytes
*/
private ByteBuffer decode() throws PDFParseException {
// start at the beginning of the buffer
buf.rewind();
// allocate the output buffer
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while (true) {
int first = readHexDigit();
int second = readHexDigit();
if (first == -1) {
break;
} else if (second == -1) {
baos.write((byte) (first << 4));
break;
} else {
baos.write((byte) ((first << 4) + second));
}
}
return ByteBuffer.wrap(baos.toByteArray());
}
/**
* decode an array of bytes in ASCIIHex format.
* <p>
* ASCIIHex format consists of a sequence of Hexidecimal
* digits, with possible whitespace, ending with the
* '&gt;' character.
*
* @param buf the encoded ASCII85 characters in a byte
* buffer
* @param params parameters to the decoder (ignored)
* @return the decoded bytes
*/
public static ByteBuffer decode(ByteBuffer buf, PDFObject params)
throws PDFParseException
{
ASCIIHexDecode me = new ASCIIHexDecode(buf);
return me.decode();
}
}

View File

@ -0,0 +1,236 @@
# $Id: CCITTCodes,v 1.2 2007/12/20 18:33:33 rbair Exp $
#
# Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
# Santa Clara, California 95054, U.S.A. All rights reserved.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
# WHITE CODES
00110101 0
000111 1
0111 2
1000 3
1011 4
1100 5
1110 6
1111 7
10011 8
10100 9
00111 10
01000 11
001000 12
000011 13
110100 14
110101 15
101010 16
101011 17
0100111 18
0001100 19
0001000 20
0010111 21
0000011 22
0000100 23
0101000 24
0101011 25
0010011 26
0100100 27
0011000 28
00000010 29
00000011 30
00011010 31
00011011 32
00010010 33
00010011 34
00010100 35
00010101 36
00010110 37
00010111 38
00101000 39
00101001 40
00101010 41
00101011 42
00101100 43
00101101 44
00000100 45
00000101 46
00001010 47
00001011 48
01010010 49
01010011 50
01010100 51
01010101 52
00100100 53
00100101 54
01011000 55
01011001 56
01011010 57
01011011 58
01001010 59
01001011 60
00110010 61
00110011 62
00110100 63
11011 64
10010 128
010111 192
0110111 256
00110110 320
00110111 384
01100100 448
01100101 512
01101000 576
01100111 640
011001100 704
011001101 768
011010010 832
011010011 896
011010100 960
011010101 1024
011010110 1088
011010111 1152
011011000 1216
011011001 1280
011011010 1344
011011011 1408
010011000 1472
010011001 1536
010011010 1600
011000 1664
010011011 1728
00000001000 1792
00000001100 1856
00000001101 1920
000000010010 1984
000000010011 2048
000000010100 2112
000000010101 2176
000000010110 2240
000000010111 2304
000000011100 2368
000000011101 2432
000000011110 2496
000000011111 2560
000000001111 -2
0000000000 -1
# BLACK CODES
0000110111 0
010 1
11 2
10 3
011 4
0011 5
0010 6
00011 7
000101 8
000100 9
0000100 10
0000101 11
0000111 12
00000100 13
00000111 14
000011000 15
0000010111 16
0000011000 17
0000001000 18
00001100111 19
00001101000 20
00001101100 21
00000110111 22
00000101000 23
00000010111 24
00000011000 25
000011001010 26
000011001011 27
000011001100 28
000011001101 29
000001101000 30
000001101001 31
000001101010 32
000001101011 33
000011010010 34
000011010011 35
000011010100 36
000011010101 37
000011010110 38
000011010111 39
000001101100 40
000001101101 41
000011011010 42
000011011011 43
000001010100 44
000001010101 45
000001010110 46
000001010111 47
000001100100 48
000001100101 49
000001010010 50
000001010011 51
000000100100 52
000000110111 53
000000111000 54
000000100111 55
000000101000 56
000001011000 57
000001011001 58
000000101011 59
000000101100 60
000001011010 61
000001100110 62
000001100111 63
0000001111 64
000011001000 128
000011001001 192
000001011011 256
000000110011 320
000000110100 384
000000110101 448
0000001101100 512
0000001101101 576
0000001001010 640
0000001001011 704
0000001001100 768
0000001001101 832
0000001110010 896
0000001110011 960
0000001110100 1024
0000001110101 1088
0000001110110 1152
0000001110111 1216
0000001010010 1280
0000001010011 1344
0000001010100 1408
0000001010101 1472
0000001011010 1536
0000001011011 1600
0000001100100 1664
0000001100101 1728
00000001000 1792
00000001100 1856
00000001101 1920
000000010010 1984
000000010011 2048
000000010100 2112
000000010101 2176
000000010110 2240
000000010111 2304
000000011100 2368
000000011101 2432
000000011110 2496
000000011111 2560
000000001111 -2
00000000000 -1

View File

@ -0,0 +1,96 @@
package com.sun.pdfview.decode;
import java.io.IOException;
import net.sf.andpdf.nio.ByteBuffer;
import com.sun.pdfview.PDFObject;
public class CCITTFaxDecode {
protected static ByteBuffer decode(PDFObject dict, ByteBuffer buf,
PDFObject params) throws IOException {
byte[] bytes = new byte[buf.remaining()];
buf.get(bytes, 0, bytes.length);
return ByteBuffer.wrap(decode(dict, bytes));
}
protected static byte[] decode(PDFObject dict, byte[] source) throws IOException {
int width = 1728;
PDFObject widthDef = dict.getDictRef("Width");
if (widthDef == null) {
widthDef = dict.getDictRef("W");
}
if (widthDef != null) {
width = widthDef.getIntValue();
}
int height = 0;
PDFObject heightDef = dict.getDictRef("Height");
if (heightDef == null) {
heightDef = dict.getDictRef("H");
}
if (heightDef != null) {
height = heightDef.getIntValue();
}
//
int columns = getOptionFieldInt(dict, "Columns", width);
int rows = getOptionFieldInt(dict, "Rows", height);
int k = getOptionFieldInt(dict, "K", 0);
int size = rows * ((columns + 7) >> 3);
byte[] destination = new byte[size];
boolean align = getOptionFieldBoolean(dict, "EncodedByteAlign", false);
CCITTFaxDecoder decoder = new CCITTFaxDecoder(1, columns, rows);
decoder.setAlign(align);
if (k == 0) {
decoder.decodeT41D(destination, source, 0, rows);
} else if (k > 0) {
decoder.decodeT42D(destination, source, 0, rows);
} else if (k < 0) {
decoder.decodeT6(destination, source, 0, rows);
}
if (!getOptionFieldBoolean(dict, "BlackIs1", false)) {
for (int i = 0; i < destination.length; i++) {
// bitwise not
destination[i] = (byte) ~destination[i];
}
}
return destination;
}
public static int getOptionFieldInt(PDFObject dict, String name, int defaultValue) throws IOException {
PDFObject dictParams = dict.getDictRef("DecodeParms");
if (dictParams == null) {
return defaultValue;
}
PDFObject value = dictParams.getDictRef(name);
if (value == null) {
return defaultValue;
}
return value.getIntValue();
}
public static boolean getOptionFieldBoolean(PDFObject dict, String name, boolean defaultValue) throws IOException {
PDFObject dictParams = dict.getDictRef("DecodeParms");
if (dictParams == null) {
return defaultValue;
}
PDFObject value = dictParams.getDictRef(name);
if (value == null) {
return defaultValue;
}
return value.getBooleanValue();
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,94 @@
/*
* $Id: DCTDecode.java,v 1.2 2007/12/20 18:33:33 rbair Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview.decode;
import java.io.IOException;
import java.nio.IntBuffer;
import net.sf.andpdf.nio.ByteBuffer;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Bitmap.Config;
import android.util.Log;
import com.sun.pdfview.PDFObject;
import com.sun.pdfview.PDFParseException;
import com.sun.pdfview.colorspace.PDFColorSpace;
/**
* decode a DCT encoded array into a byte array. This class uses Java's
* built-in JPEG image class to do the decoding.
*
* @author Mike Wessler
*/
public class DCTDecode {
/**
* decode an array of bytes in DCT format.
* <p>
* DCT is the format used by JPEG images, so this class simply
* loads the DCT-format bytes as an image, then reads the bytes out
* of the image to create the array. Unfortunately, their most
* likely use is to get turned BACK into an image, so this isn't
* terribly efficient... but is is general... don't hit, please.
* <p>
* The DCT-encoded stream may have 1, 3 or 4 samples per pixel, depending
* on the colorspace of the image. In decoding, we look for the colorspace
* in the stream object's dictionary to decide how to decode this image.
* If no colorspace is present, we guess 3 samples per pixel.
*
* @param dict the stream dictionary
* @param buf the DCT-encoded buffer
* @param params the parameters to the decoder (ignored)
* @return the decoded buffer
*/
protected static ByteBuffer decode(PDFObject dict, ByteBuffer buf,
PDFObject params) throws PDFParseException
{
// System.out.println("DCTDecode image info: "+params);
buf.rewind();
// copy the data into a byte array required by createimage
byte[] ary = new byte[buf.remaining()];
buf.get(ary);
Bitmap img = BitmapFactory.decodeByteArray(ary, 0, ary.length);
if (img == null)
throw new PDFParseException("could not decode image of compressed size "+ary.length);
Config conf = img.getConfig();
Log.e("ANDPDF.dctdecode", "decoded image type"+conf);
int size = 4*img.getWidth()*img.getHeight();
if (conf == Config.RGB_565)
size = 2*img.getWidth()*img.getHeight();
// TODO [FHe]: else ... what do we get for gray? Config.ALPHA_8?
java.nio.ByteBuffer byteBuf = java.nio.ByteBuffer.allocate(size);
img.copyPixelsToBuffer(byteBuf);
ByteBuffer result = ByteBuffer.fromNIO(byteBuf);
result.rewind();
return result;
}
}

View File

@ -0,0 +1,111 @@
/*
* $Id: FlateDecode.java,v 1.4 2009/01/03 17:23:30 tomoke Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview.decode;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
import net.sf.andpdf.nio.ByteBuffer;
import com.sun.pdfview.PDFObject;
import com.sun.pdfview.PDFParseException;
/**
* decode a deFlated byte array
*
* @author Mike Wessler
* @author Joerg Jahnke (joerg.jahnke@users.sourceforge.net)
*/
public class FlateDecode {
/**
* decode a byte buffer in Flate format.
* <p>
* Flate is a built-in Java algorithm. It's part of the java.util.zip
* package.
*
* @param buf the deflated input buffer
* @param params parameters to the decoder (unused)
* @return the decoded (inflated) bytes
*/
public static ByteBuffer decode(PDFObject dict, ByteBuffer buf,
PDFObject params) throws IOException {
Inflater inf = new Inflater(false);
int bufSize = buf.remaining();
// set the input for the inflater
byte[] data = null;
if (buf.hasArray()) {
data = buf.array();
inf.setInput(data, buf.arrayOffset() + buf.position(), bufSize);
buf.position(buf.position() + bufSize);
} else {
// copy the data, since the array() method is not supported
// on raf-based ByteBuffers
data = new byte[bufSize];
buf.get(data);
inf.setInput(data);
}
// output to a byte-array output stream, since we don't
// know how big the output will be
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] decomp = new byte[bufSize];
int read = 0;
try {
while (!inf.finished()) {
read = inf.inflate(decomp);
if (read <= 0) {
// System.out.println("Read = " + read + "! Params: " + params);
if (inf.needsDictionary()) {
throw new PDFParseException("Don't know how to ask for a dictionary in FlateDecode");
} else {
// System.out.println("Inflate data length=" + buf.remaining());
return ByteBuffer.allocate(0);
// throw new PDFParseException("Inflater wants more data... but it's already here!");
}
}
baos.write(decomp, 0, read);
}
} catch (DataFormatException dfe) {
throw new PDFParseException("Data format exception:" + dfe.getMessage());
}
// return the output as a byte buffer
ByteBuffer outBytes = ByteBuffer.wrap(baos.toByteArray());
// undo a predictor algorithm, if any was used
if (params != null && params.getDictionary().containsKey("Predictor")) {
Predictor predictor = Predictor.getPredictor(params);
if (predictor != null) {
outBytes = predictor.unpredict(outBytes);
}
}
return outBytes;
}
}

View File

@ -0,0 +1,204 @@
/*
* $Id: LZWDecode.java,v 1.4 2009/02/22 00:45:32 tomoke Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview.decode;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import net.sf.andpdf.nio.ByteBuffer;
import com.sun.pdfview.PDFObject;
import com.sun.pdfview.PDFParseException;
/**
* decode an LZW-encoded array of bytes. LZW is a patented algorithm.
*
* <p>Feb 21, 2009 Legal statement on Intellectual Property from Unisys</p><pre>
* <b><u>LZW Patent Information</u></b> (http://www.unisys.com/about__unisys/lzw)
* <u>License Information on GIF and Other LZW-based Technologies
* </u><p>
* <b><i>Unisys U.S. LZW Patent No. 4,558,302 expired on June 20, 2003,
* the counterpart patents in the United Kingdom, France, Germany and
* Italy expired on June 18, 2004, the Japanese counterpart patents
* expired on June 20, 2004 and the counterpart Canadian patent
* expired on July 7, 2004.
* </i></b><p>
* Unisys Corporation holds and has patents pending on a number of
* improvements on the inventions claimed in the above-expired patents.
* Information on these improvement patents and terms under which they
* may be licensed can be obtained by contacting the following:
*<p>
* Unisys Corporation
* Welch Patent Licensing Department
* Mail Stop E8-114
* Unisys Way
* Blue Bell, PA 19424
*<p>
* Via the Internet, send email to Robert.Marley@unisys.com.
*<p>
* Via facsimile, send inquiries to Welch Patent Licensing Department at
* 215-986-3090.
*<p>
* The above is presented for information purposes only, and is subject
* to change by Unisys. Additionally, this information should not be
* considered as legally obligating Unisys in any way with regard to license
* availability, or as to the terms and conditions offered for a license,
* or with regard to the interpretation of any license agreements.
* You should consult with your own legal counsel regarding your
* particular situation.
* </pre></p>
*
* @author Mike Wessler
*/
public class LZWDecode {
ByteBuffer buf;
int bytepos;
int bitpos;
byte[] dict[] = new byte[4096][];
int dictlen = 0;
int bitspercode = 9;
static int STOP = 257;
static int CLEARDICT = 256;
/**
* initialize this decoder with an array of encoded bytes
* @param buf the buffer of bytes
*/
private LZWDecode(ByteBuffer buf) throws PDFParseException {
for (int i = 0; i < 256; i++) {
dict[i] = new byte[1];
dict[i][0] = (byte) i;
}
dictlen = 258;
bitspercode = 9;
this.buf = buf;
bytepos = 0;
bitpos = 0;
}
/**
* reset the dictionary to the initial 258 entries
*/
private void resetDict() {
dictlen = 258;
bitspercode = 9;
}
/**
* get the next code from the input stream
*/
private int nextCode() {
int fillbits = bitspercode;
int value = 0;
if (bytepos >= buf.limit() - 1) {
return -1;
}
while (fillbits > 0) {
int nextbits = buf.get(bytepos); // bitsource
int bitsfromhere = 8 - bitpos; // how many bits can we take?
if (bitsfromhere > fillbits) { // don't take more than we need
bitsfromhere = fillbits;
}
value |= ((nextbits >> (8 - bitpos - bitsfromhere)) &
(0xff >> (8 - bitsfromhere))) << (fillbits - bitsfromhere);
fillbits -= bitsfromhere;
bitpos += bitsfromhere;
if (bitpos >= 8) {
bitpos = 0;
bytepos++;
}
}
return value;
}
/**
* decode the array.
* @return the uncompressed byte array
*/
private ByteBuffer decode() throws PDFParseException {
// algorithm derived from:
// http://www.rasip.fer.hr/research/compress/algorithms/fund/lz/lzw.html
// and the PDFReference
int cW = CLEARDICT;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while (true) {
int pW = cW;
cW = nextCode();
if (cW == -1) {
throw new PDFParseException("Missed the stop code in LZWDecode!");
}
if (cW == STOP) {
break;
} else if (cW == CLEARDICT) {
resetDict();
// pW= -1;
} else if (pW == CLEARDICT) {
baos.write(dict[cW], 0, dict[cW].length);
} else {
if (cW < dictlen) { // it's a code in the dictionary
baos.write(dict[cW], 0, dict[cW].length);
byte[] p = new byte[dict[pW].length + 1];
System.arraycopy(dict[pW], 0, p, 0, dict[pW].length);
p[dict[pW].length] = dict[cW][0];
dict[dictlen++] = p;
} else { // not in the dictionary (should==dictlen)
// if (cW!=dictlen) {
// System.out.println("Got a bouncy code: "+cW+" (dictlen="+dictlen+")");
// }
byte[] p = new byte[dict[pW].length + 1];
System.arraycopy(dict[pW], 0, p, 0, dict[pW].length);
p[dict[pW].length] = p[0];
baos.write(p, 0, p.length);
dict[dictlen++] = p;
}
if (dictlen >= (1 << bitspercode) - 1 && bitspercode < 12) {
bitspercode++;
}
}
}
return ByteBuffer.wrap(baos.toByteArray());
}
/**
* decode an array of LZW-encoded bytes to a byte array.
*
* @param buf the buffer of encoded bytes
* @param params parameters for the decoder (unused)
* @return the decoded uncompressed bytes
*/
public static ByteBuffer decode(ByteBuffer buf, PDFObject params)
throws IOException {
// decode the array
LZWDecode me = new LZWDecode(buf);
ByteBuffer outBytes = me.decode();
// undo a predictor algorithm, if any was used
if (params != null && params.getDictionary().containsKey("Predictor")) {
Predictor predictor = Predictor.getPredictor(params);
if (predictor != null) {
outBytes = predictor.unpredict(outBytes);
}
}
return outBytes;
}
}

View File

@ -0,0 +1,122 @@
/*
* $Id: PDFDecoder.java,v 1.5 2009/03/12 12:26:19 tomoke Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview.decode;
import java.io.IOException;
import net.sf.andpdf.nio.ByteBuffer;
import com.sun.pdfview.PDFObject;
import com.sun.pdfview.PDFParseException;
import com.sun.pdfview.decrypt.PDFDecrypterFactory;
/**
* A PDF Decoder encapsulates all the methods of decoding a stream of bytes
* based on all the various encoding methods.
* <p>
* You should use the decodeStream() method of this object rather than using
* any of the decoders directly.
*/
public class PDFDecoder {
/** Creates a new instance of PDFDecoder */
private PDFDecoder() {
}
/**
* decode a byte[] stream using the filters specified in the object's
* dictionary (passed as argument 1).
* @param dict the dictionary associated with the stream
* @param streamBuf the data in the stream, as a byte buffer
*/
public static ByteBuffer decodeStream(PDFObject dict, ByteBuffer streamBuf)
throws IOException {
PDFObject filter = dict.getDictRef("Filter");
if (filter == null) {
// just apply default decryption
return dict.getDecrypter().decryptBuffer(null, dict, streamBuf);
} else {
// apply filters
PDFObject ary[];
PDFObject params[];
if (filter.getType() == PDFObject.NAME) {
ary = new PDFObject[1];
ary[0] = filter;
params = new PDFObject[1];
params[0] = dict.getDictRef("DecodeParms");
} else {
ary = filter.getArray();
PDFObject parmsobj = dict.getDictRef("DecodeParms");
if (parmsobj != null) {
params = parmsobj.getArray();
} else {
params = new PDFObject[ary.length];
}
}
// determine whether default encryption applies or if there's a
// specific Crypt filter; it must be the first filter according to
// the errata for PDF1.7
boolean specificCryptFilter =
ary.length != 0 && ary[0].getStringValue().equals("Crypt");
if (!specificCryptFilter) {
// No Crypt filter, so should apply default decryption (if
// present!)
streamBuf = dict.getDecrypter().decryptBuffer(
null, dict, streamBuf);
}
for (int i = 0; i < ary.length; i++) {
String enctype = ary[i].getStringValue();
if (enctype == null) {
} else if (enctype.equals("FlateDecode") || enctype.equals("Fl")) {
streamBuf = FlateDecode.decode(dict, streamBuf, params[i]);
} else if (enctype.equals("LZWDecode") || enctype.equals("LZW")) {
streamBuf = LZWDecode.decode(streamBuf, params[i]);
} else if (enctype.equals("ASCII85Decode") || enctype.equals("A85")) {
streamBuf = ASCII85Decode.decode(streamBuf, params[i]);
} else if (enctype.equals("ASCIIHexDecode") || enctype.equals("AHx")) {
streamBuf = ASCIIHexDecode.decode(streamBuf, params[i]);
} else if (enctype.equals("RunLengthDecode") || enctype.equals("RL")) {
streamBuf = RunLengthDecode.decode(streamBuf, params[i]);
} else if (enctype.equals("DCTDecode") || enctype.equals("DCT")) {
streamBuf = DCTDecode.decode(dict, streamBuf, params[i]);
} else if (enctype.equals("CCITTFaxDecode") || enctype.equals("CCF")) {
streamBuf = CCITTFaxDecode.decode(dict, streamBuf, params[i]);
} else if (enctype.equals("Crypt")) {
String cfName = PDFDecrypterFactory.CF_IDENTITY;
if (params[i] != null) {
final PDFObject nameObj = params[i].getDictRef("Name");
if (nameObj != null && nameObj.getType() == PDFObject.NAME) {
cfName = nameObj.getStringValue();
}
}
streamBuf = dict.getDecrypter().decryptBuffer(cfName, null, streamBuf);
} else {
throw new PDFParseException("Unknown coding method:" + ary[i].getStringValue());
}
}
}
return streamBuf;
}
}

View File

@ -0,0 +1,213 @@
/*
* $Id: PNGPredictor.java,v 1.3 2009/02/12 13:53:58 tomoke Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview.decode;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import net.sf.andpdf.nio.ByteBuffer;
/**
* Undo prediction based on the PNG algorithm.
*/
public class PNGPredictor extends Predictor {
/** Creates a new instance of PNGPredictor */
public PNGPredictor() {
super (PNG);
}
/**
* Undo data based on the png algorithm
*/
public ByteBuffer unpredict(ByteBuffer imageData)
throws IOException
{
List<byte[]> rows = new ArrayList<byte[]>();
byte[] curLine = null;
byte[] prevLine = null;
// get the number of bytes per row
int rowSize = getColumns() * getColors() * getBitsPerComponent();
rowSize = (int) Math.ceil(rowSize / 8.0);
while(imageData.remaining() >= rowSize + 1) {
// the first byte determines the algorithm
int algorithm = (int) (imageData.get() & 0xff);
// read the rest of the line
curLine = new byte[rowSize];
imageData.get(curLine);
// use the algorithm, Luke
switch (algorithm) {
case 0:
// none
break;
case 1:
doSubLine(curLine);
break;
case 2:
doUpLine(curLine, prevLine);
break;
case 3:
doAverageLine(curLine, prevLine);
break;
case 4:
doPaethLine(curLine, prevLine);
break;
}
rows.add(curLine);
prevLine = curLine;
}
// turn into byte array
ByteBuffer outBuf = ByteBuffer.allocate(rows.size() * rowSize);
for (Iterator i = rows.iterator(); i.hasNext();) {
outBuf.put((byte[]) i.next());
}
// reset start pointer
outBuf.flip();
// return
return outBuf;
}
/**
* Return the value of the Sub algorithm on the line (compare bytes to
* the previous byte of the same color on this line).
*/
protected void doSubLine(byte[] curLine) {
// get the number of bytes per sample
int sub = (int) Math.ceil((getBitsPerComponent() * getColors()) / 8.0);
for (int i = 0; i < curLine.length; i++) {
int prevIdx = i - sub;
if (prevIdx >= 0) {
curLine[i] += curLine[prevIdx];
}
}
}
/**
* Return the value of the up algorithm on the line (compare bytes to
* the same byte in the previous line)
*/
protected void doUpLine(byte[] curLine, byte[] prevLine) {
if (prevLine == null) {
// do nothing if this is the first line
return;
}
for (int i = 0; i < curLine.length; i++) {
curLine[i] += prevLine[i];
}
}
/**
* Return the value of the average algorithm on the line (compare
* bytes to the average of the previous byte of the same color and
* the same byte on the previous line)
*/
protected void doAverageLine(byte[] curLine, byte[] prevLine) {
// get the number of bytes per sample
int sub = (int) Math.ceil((getBitsPerComponent() * getColors()) / 8.0);
for (int i = 0; i < curLine.length; i++) {
int raw = 0;
int prior = 0;
// get the last value of this color
int prevIdx = i - sub;
if (prevIdx >= 0) {
raw = curLine[prevIdx] & 0xff;
}
// get the value on the previous line
if (prevLine != null) {
prior = prevLine[i] & 0xff;
}
// add the average
curLine[i] += (byte) Math.floor((raw + prior) / 2);
}
}
/**
* Return the value of the average algorithm on the line (compare
* bytes to the average of the previous byte of the same color and
* the same byte on the previous line)
*/
protected void doPaethLine(byte[] curLine, byte[] prevLine) {
// get the number of bytes per sample
int sub = (int) Math.ceil((getBitsPerComponent() * getColors()) / 8.0);
for (int i = 0; i < curLine.length; i++) {
int left = 0;
int up = 0;
int upLeft = 0;
// get the last value of this color
int prevIdx = i - sub;
if (prevIdx >= 0) {
left = curLine[prevIdx] & 0xff;
}
// get the value on the previous line
if (prevLine != null) {
up = prevLine[i] & 0xff;
}
if (prevIdx > 0 && prevLine != null) {
upLeft = prevLine[prevIdx] & 0xff;
}
// add the average
curLine[i] += (byte) paeth(left, up, upLeft);
}
}
/**
* The paeth algorithm
*/
protected int paeth(int left, int up, int upLeft) {
int p = left + up - upLeft;
int pa = Math.abs(p - left);
int pb = Math.abs(p - up);
int pc = Math.abs(p - upLeft);
if ((pa <= pb) && (pa <= pc)) {
return left;
} else if (pb <= pc) {
return up;
} else {
return upLeft;
}
}
}

View File

@ -0,0 +1,175 @@
/*
* $Id: Predictor.java,v 1.2 2007/12/20 18:33:33 rbair Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview.decode;
import java.io.IOException;
import net.sf.andpdf.nio.ByteBuffer;
import com.sun.pdfview.PDFObject;
import com.sun.pdfview.PDFParseException;
/**
* The abstract superclass of various predictor objects that undo well-known
* prediction algorithms.
*/
public abstract class Predictor {
/** well known algorithms */
public static final int TIFF = 0;
public static final int PNG = 1;
/** the algorithm to use */
private int algorithm;
/** the number of colors per sample */
private int colors = 1;
/** the number of bits per color component */
private int bpc = 8;
/** the number of columns per row */
private int columns = 1;
/**
* Create an instance of a predictor. Use <code>getPredictor()</code>
* instead of this.
*/
protected Predictor(int algorithm) {
this.algorithm = algorithm;
}
/**
* Actually perform this algorithm on decoded image data.
* Subclasses must implement this method
*/
public abstract ByteBuffer unpredict(ByteBuffer imageData)
throws IOException;
/**
* Get an instance of a predictor
*
* @param params the filter parameters
*/
public static Predictor getPredictor(PDFObject params)
throws IOException
{
// get the algorithm (required)
PDFObject algorithmObj = params.getDictRef("Predictor");
if (algorithmObj == null) {
// no predictor
return null;
}
int algorithm = algorithmObj.getIntValue();
// create the predictor object
Predictor predictor = null;
switch (algorithm) {
case 1:
// no predictor
return null;
case 2:
throw new PDFParseException("Tiff Predictor not supported");
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
predictor = new PNGPredictor();
break;
default:
throw new PDFParseException("Unknown predictor: " + algorithm);
}
// read the colors (optional)
PDFObject colorsObj = params.getDictRef("Colors");
if (colorsObj != null) {
predictor.setColors(colorsObj.getIntValue());
}
// read the bits per component (optional)
PDFObject bpcObj = params.getDictRef("BitsPerComponent");
if (bpcObj != null) {
predictor.setBitsPerComponent(bpcObj.getIntValue());
}
// read the columns (optional)
PDFObject columnsObj = params.getDictRef("Columns");
if (columnsObj != null) {
predictor.setColumns(columnsObj.getIntValue());
}
// all set
return predictor;
}
/**
* Get the algorithm in use
*
* @return one of the known algorithm types
*/
public int getAlgorithm() {
return algorithm;
}
/**
* Get the number of colors per sample
*/
public int getColors() {
return colors;
}
/**
* Set the number of colors per sample
*/
protected void setColors(int colors) {
this.colors = colors;
}
/**
* Get the number of bits per color component
*/
public int getBitsPerComponent() {
return bpc;
}
/**
* Set the number of bits per color component
*/
public void setBitsPerComponent(int bpc) {
this.bpc = bpc;
}
/**
* Get the number of columns
*/
public int getColumns() {
return columns;
}
/**
* Set the number of columns
*/
public void setColumns(int columns) {
this.columns = columns;
}
}

View File

@ -0,0 +1,99 @@
/*
* $Id: RunLengthDecode.java,v 1.1 2009/02/21 20:04:52 tomoke Exp $
*
* Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview.decode;
import java.io.ByteArrayOutputStream;
import net.sf.andpdf.nio.ByteBuffer;
import com.sun.pdfview.PDFObject;
import com.sun.pdfview.PDFParseException;
/**
* decode an array of Run Length encoded bytes into a byte array
*
* @author Mike Wessler
*/
public class RunLengthDecode {
/** the end of data in the RunLength encoding. */
private static final int RUN_LENGTH_EOD = 128;
private ByteBuffer buf;
/**
* initialize the decoder with an array of bytes in RunLength format
*/
private RunLengthDecode(ByteBuffer buf) {
this.buf = buf;
}
/**
* decode the array
* @return the decoded bytes
*/
private ByteBuffer decode() throws PDFParseException {
// start at the beginning of the buffer
buf.rewind();
// allocate the output buffer
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte dupAmount = -1;
byte[] buffer = new byte[128];
while ((dupAmount = buf.get()) != -1 &&
dupAmount != RUN_LENGTH_EOD) {
if (dupAmount <= 127) {
int amountToCopy = dupAmount + 1;
while (amountToCopy > 0) {
buf.get(buffer, 0, amountToCopy);
baos.write(buffer, 0, amountToCopy);
}
} else {
byte dupByte = buf.get();
for (int i = 0; i < 257 - (int) (dupAmount & 0xFF); i++) {
baos.write(dupByte);
}
}
}
return ByteBuffer.wrap(baos.toByteArray());
}
/**
* decode an array of bytes in RunLength format.
* <p>
* RunLength format consists of a sequence of a byte-oriented format
* based on run length. There are a series of "runs", where
* a run is a length byte followed by 1 to 128 bytes of data.
* If the length is 0-127, the following length+1 (1 to 128) bytes are
* to be copied. If the length is 129 through 255, the following
* single byte is copied 257-length (2 to 128) times.
* A length value of 128 means and End of Data (EOD).
*
* @param buf the RUnLEngth encoded bytes in a byte buffer
*
* @param params parameters to the decoder (ignored)
* @return the decoded bytes
*/
public static ByteBuffer decode(ByteBuffer buf, PDFObject params)
throws PDFParseException {
RunLengthDecode me = new RunLengthDecode(buf);
return me.decode();
}
}

View File

@ -0,0 +1,129 @@
/*
* Copyright 2008 Pirion Systems Pty Ltd, 139 Warry St,
* Fortitude Valley, Queensland, Australia
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview.decrypt;
import com.sun.pdfview.PDFObject;
import com.sun.pdfview.PDFParseException;
import java.util.Map;
import net.sf.andpdf.nio.ByteBuffer;
/**
* Implements Version 4 standard decryption, whereby the Encrypt dictionary
* contains a list of named 'crypt filters', each of which is the equivalent
* of a {@link PDFDecrypter}. In addition to this list of crypt filters,
* the name of the filter to use for streams and the default filter to use
* for strings is specified. Requests to decode a stream with a named
* decrypter (typically Identity) instead of the default decrypter
* are honoured.
*
* @author Luke Kirby
*/
public class CryptFilterDecrypter implements PDFDecrypter {
/** Maps from crypt filter names to their corresponding decrypters */
private Map<String, PDFDecrypter> decrypters;
/** The default decrypter for stream content */
private PDFDecrypter defaultStreamDecrypter;
/** The default decrypter for string content */
private PDFDecrypter defaultStringDecrypter;
/**
* Class constructor
* @param decrypters a map of crypt filter names to their corresponding
* decrypters. Must already contain the Identity filter.
* @param defaultStreamCryptName the crypt filter name of the default
* stream decrypter
* @param defaultStringCryptName the crypt filter name of the default
* string decrypter
* @throws PDFParseException if one of the named defaults is not
* present in decrypters
*/
public CryptFilterDecrypter(
Map<String, PDFDecrypter> decrypters,
String defaultStreamCryptName,
String defaultStringCryptName)
throws PDFParseException {
this.decrypters = decrypters;
assert this.decrypters.containsKey("Identity") :
"Crypt Filter map does not contain required Identity filter";
defaultStreamDecrypter = this.decrypters.get(defaultStreamCryptName);
if (defaultStreamDecrypter == null) {
throw new PDFParseException(
"Unknown crypt filter specified as default for streams: " +
defaultStreamCryptName);
}
defaultStringDecrypter = this.decrypters.get(defaultStringCryptName);
if (defaultStringDecrypter == null) {
throw new PDFParseException(
"Unknown crypt filter specified as default for strings: " +
defaultStringCryptName);
}
}
public ByteBuffer decryptBuffer(
String cryptFilterName, PDFObject streamObj, ByteBuffer streamBuf)
throws PDFParseException {
final PDFDecrypter decrypter;
if (cryptFilterName == null) {
decrypter = defaultStreamDecrypter;
} else {
decrypter = decrypters.get(cryptFilterName);
if (decrypter == null) {
throw new PDFParseException("Unknown CryptFilter: " +
cryptFilterName);
}
}
return decrypter.decryptBuffer(
// elide the filter name to prevent V2 decrypters from
// complaining about a crypt filter name
null,
// if there's a specific crypt filter being used then objNum
// and objGen shouldn't contribute to the key, so we
// should make sure that no streamObj makes its way through
cryptFilterName != null ? null : streamObj,
streamBuf);
}
public String decryptString(int objNum, int objGen, String inputBasicString)
throws PDFParseException {
return defaultStringDecrypter.decryptString(objNum, objGen, inputBasicString);
}
public boolean isEncryptionPresent() {
for (final PDFDecrypter decrypter : decrypters.values()) {
if (decrypter.isEncryptionPresent()) {
return true;
}
}
return false;
}
public boolean isOwnerAuthorised() {
for (final PDFDecrypter decrypter : decrypters.values()) {
if (decrypter.isOwnerAuthorised()) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,40 @@
/*
* Copyright 2008 Pirion Systems Pty Ltd, 139 Warry St,
* Fortitude Valley, Queensland, Australia
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview.decrypt;
/**
* Identifies that the specified encryption mechanism, though supported by the
* product, is not supported by the platform that it is running on; i.e., that
* either the JCE does not support a required cipher or that its policy is
* such that a key of a given length can not be used.
*
* @author Luke Kirby
*/
public class EncryptionUnsupportedByPlatformException
extends UnsupportedEncryptionException {
public EncryptionUnsupportedByPlatformException(String message) {
super(message);
}
public EncryptionUnsupportedByPlatformException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -0,0 +1,36 @@
/*
* Copyright 2008 Pirion Systems Pty Ltd, 139 Warry St,
* Fortitude Valley, Queensland, Australia
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview.decrypt;
/**
* Identifies that the specified encryption mechanism is not
* supported by this product, that is, PDFRenderer, as opposed to
* a {@link EncryptionUnsupportedByPlatformException limitation in
* the platform}.
*
* @author Luke Kirby
*/
public class EncryptionUnsupportedByProductException
extends UnsupportedEncryptionException {
public EncryptionUnsupportedByProductException(String message) {
super(message);
}
}

View File

@ -0,0 +1,64 @@
/*
* Copyright 2008 Pirion Systems Pty Ltd, 139 Warry St,
* Fortitude Valley, Queensland, Australia
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview.decrypt;
import net.sf.andpdf.nio.ByteBuffer;
import com.sun.pdfview.PDFObject;
import com.sun.pdfview.PDFParseException;
/**
* Performs identity decryption; that is, inputs aren't encrypted and
* are returned right back.
*
* @Author Luke Kirby
*/
public class IdentityDecrypter implements PDFDecrypter {
private static IdentityDecrypter INSTANCE = new IdentityDecrypter();
public ByteBuffer decryptBuffer(String cryptFilterName,
PDFObject streamObj, ByteBuffer streamBuf)
throws PDFParseException {
if (cryptFilterName != null) {
throw new PDFParseException("This Encryption version does not support Crypt filters");
}
return streamBuf;
}
public String decryptString(int objNum, int objGen, String inputBasicString) {
return inputBasicString;
}
public static IdentityDecrypter getInstance() {
return INSTANCE;
}
public boolean isEncryptionPresent() {
return false;
}
public boolean isOwnerAuthorised() {
return false;
}
}

View File

@ -0,0 +1,35 @@
/*
* Copyright 2008 Pirion Systems Pty Ltd, 139 Warry St,
* Fortitude Valley, Queensland, Australia
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview.decrypt;
import com.sun.pdfview.PDFParseException;
/**
* Identifies that the supplied password was incorrect or non-existent
* and required.
* @author Luke Kirby
*/
// TODO - consider having this not extend PDFParseException so that
// it will be handled more explicitly?
public class PDFAuthenticationFailureException extends PDFParseException {
public PDFAuthenticationFailureException(String message) {
super(message);
}
}

View File

@ -0,0 +1,98 @@
/*
* Copyright 2008 Pirion Systems Pty Ltd, 139 Warry St,
* Fortitude Valley, Queensland, Australia
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview.decrypt;
import net.sf.andpdf.nio.ByteBuffer;
import com.sun.pdfview.PDFObject;
import com.sun.pdfview.PDFParseException;
import com.sun.pdfview.PDFStringUtil;
/**
* A decrypter decrypts streams and strings in a PDF document. {@link
* #decryptBuffer(String, PDFObject, ByteBuffer)} } should be used for decoding
* streams, and {@link #decryptString(int, int, String)} for string values in
* the PDF. It is possible for strings and streams to be encrypted with
* different mechanisms, so the appropriate method must alwayus be used.
*
* @see "PDFReference 1.7, Section 3.5 Encryption"
* @author Luke Kirby
*/
public interface PDFDecrypter {
/**
* Decrypt a buffer of data
* @param cryptFilterName the name of the crypt filter, if V4
* encryption is being used, where individual crypt filters may
* be specified for individual streams. If encryption is not using
* V4 encryption (indicated by V=4 in the Encrypt dictionary) then
* this must be null. Null may also be specified with V4 encryption
* to indicate that the default filter should be used.
* @param streamObj the object whose stream is being decrypted. The
* containing object's number and generation contribute to the key used for
* stream encrypted with the document's default encryption, so this is
* typically required. Should be null only if a cryptFilterName is
* specified, as objects with specific stream filters use the general
* document key, rather than a stream-specific key.
* @param streamBuf the buffer to decrypt
* @return a buffer containing the decrypted stream, positioned at its
* beginning; will only be the same buffer as streamBuf if the identity
* decrypter is being used
* @throws PDFParseException if the named crypt filter does not exist, or
* if a crypt filter is named when named crypt filters are not supported.
* Problems due to incorrect passwords are revealed prior to this point.
*/
public ByteBuffer decryptBuffer(
String cryptFilterName,
PDFObject streamObj,
ByteBuffer streamBuf)
throws PDFParseException;
/**
* Decrypt a {@link PDFStringUtil basic string}.
* @param objNum the object number of the containing object
* @param objGen the generation number of the containing object
* @param inputBasicString the string to be decrypted
* @return the decrypted string
* @throws PDFParseException if the named crypt filter does not exist, or
* if a crypt filter is named when named crypt filters are not supported.
* Problems due to incorrect passwords are revealed prior to this point.
*/
public String decryptString(int objNum, int objGen, String inputBasicString)
throws PDFParseException;
/**
* Determine whether the password known by the decrypter indicates that
* the user is the owner of the document. Can be used, in conjunction
* with {@link #isEncryptionPresent()} to determine whether any
* permissions apply.
* @return whether owner authentication is being used to decrypt the
* document
*/
public boolean isOwnerAuthorised();
/**
* Determine whether this actually applies a decryption other than
* identity decryption.
* @return whether encryption is present
*/
public boolean isEncryptionPresent();
}

View File

@ -0,0 +1,320 @@
/*
* Copyright 2008 Pirion Systems Pty Ltd, 139 Warry St,
* Fortitude Valley, Queensland, Australia
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview.decrypt;
import com.sun.pdfview.PDFObject;
import com.sun.pdfview.PDFParseException;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* Produces a {@link PDFDecrypter} for documents given a (possibly non-existent)
* Encrypt dictionary. Supports decryption of versions 1, 2 and 4 of the
* password-based encryption mechanisms as described in PDF Reference version
* 1.7. This means that it supports RC4 and AES encryption with keys of
* 40-128 bits; esentially, password-protected documents with compatibility
* up to Acrobat 8.
*
* @See "PDF Reference version 1.7, section 3.5: Encryption"
* @author Luke Kirby
*/
public class PDFDecrypterFactory {
/** The name of the standard Identity CryptFilter */
public static final String CF_IDENTITY = "Identity";
/** Default key length for versions where key length is optional */
private static final int DEFAULT_KEY_LENGTH = 40;
/**
* Create a decryptor for a given encryption dictionary. A check is
* immediately performed that the supplied password decrypts content
* described by the encryption specification.
*
* @param encryptDict the Encrypt dict as found in the document's trailer.
* May be null, in which case the {@link IdentityDecrypter} will
* be returned.
* @param documentId the object with key "ID" in the trailer's dictionary.
* Should always be present if Encrypt is.
* @param password the password to use; may be <code>null</code>
* @return The decryptor that should be used for all encrypted data in the
* PDF
* @throws IOException will typically be a {@link
* com.sun.pdfview.PDFParseException}, indicating an IO problem, an error
* in the structure of the document, or a failure to obtain various ciphers
* from the installed JCE providers
* @throws EncryptionUnsupportedByPlatformException if the encryption
* is not supported by the environment in which the code is executing
* @throws EncryptionUnsupportedByProductException if PDFRenderer does
* not currently support the specified encryption
* @throws PDFAuthenticationFailureException if the supplied password
* was not able to
*/
public static PDFDecrypter createDecryptor
(PDFObject encryptDict, PDFObject documentId, PDFPassword password)
throws
IOException,
EncryptionUnsupportedByPlatformException,
EncryptionUnsupportedByProductException,
PDFAuthenticationFailureException {
// none of the classes beyond us want to see a null PDFPassword
password = PDFPassword.nonNullPassword(password);
if (encryptDict == null) {
// No encryption specified
return IdentityDecrypter.getInstance();
} else {
PDFObject filter = encryptDict.getDictRef("Filter");
// this means that we'll fail if, for example, public key
// encryption is employed
if (filter != null && "Standard".equals(filter.getStringValue())) {
final PDFObject vObj = encryptDict.getDictRef("V");
int v = vObj != null ? vObj.getIntValue() : 0;
if (v == 1 || v == 2) {
final PDFObject lengthObj =
encryptDict.getDictRef("Length");
final Integer length =
lengthObj != null ? lengthObj.getIntValue() : null;
return createStandardDecrypter(
encryptDict, documentId, password, length, false,
StandardDecrypter.EncryptionAlgorithm.RC4);
} else if (v == 4) {
return createCryptFilterDecrypter(
encryptDict, documentId, password, v);
} else {
throw new EncryptionUnsupportedByPlatformException(
"Unsupported encryption version: " + v);
}
} else if (filter == null) {
throw new PDFParseException(
"No Filter specified in Encrypt dictionary");
} else {
throw new EncryptionUnsupportedByPlatformException(
"Unsupported encryption Filter: " + filter +
"; only Standard is supported.");
}
}
}
/**
* Create a decrypter working from a crypt filter dictionary, as in
* version 4 encryption
*
* @param encryptDict the Encrypt dictionary
* @param documentId the document ID
* @param password the provided password
* @param v the version of encryption being used; must be at least 4
* @return the decrypter corresponding to the scheme expressed in
* encryptDict
* @throws PDFAuthenticationFailureException if the provided password
* does not decrypt this document
* @throws IOException if there is a problem reading the PDF, an invalid
* document structure, or an inability to obtain the required ciphers
* from the platform's JCE
* @throws EncryptionUnsupportedByPlatformException if the encryption
* is not supported by the environment in which the code is executing
* @throws EncryptionUnsupportedByProductException if PDFRenderer does
* not currently support the specified encryption
*/
private static PDFDecrypter createCryptFilterDecrypter(
PDFObject encryptDict,
PDFObject documentId,
PDFPassword password,
int v)
throws
PDFAuthenticationFailureException,
IOException,
EncryptionUnsupportedByPlatformException,
EncryptionUnsupportedByProductException {
assert v >= 4 : "crypt filter decrypter not supported for " +
"standard encryption prior to version 4";
// encryptMetadata is true if not present. Note that we don't actually
// use this to change our reading of metadata streams (that's all done
// internally by the document specifying a Crypt filter of None if
// appropriate), but it does affect the encryption key.
boolean encryptMetadata = true;
final PDFObject encryptMetadataObj =
encryptDict.getDictRef("EncryptMetadata");
if (encryptMetadataObj != null
&& encryptMetadataObj.getType() == PDFObject.BOOLEAN) {
encryptMetadata = encryptMetadataObj.getBooleanValue();
}
// Assemble decrypters for each filter in the
// crypt filter (CF) dictionary
final Map<String, PDFDecrypter> cfDecrypters =
new HashMap<String, PDFDecrypter>();
final PDFObject cfDict = encryptDict.getDictRef("CF");
if (cfDict == null) {
throw new PDFParseException(
"No CF value present in Encrypt dict for V4 encryption");
}
final Iterator<String> cfNameIt = cfDict.getDictKeys();
while (cfNameIt.hasNext()) {
final String cfName = cfNameIt.next();
final PDFObject cryptFilter = cfDict.getDictRef(cfName);
final PDFObject lengthObj = cryptFilter.getDictRef("Length");
// The Errata for PDF 1.7 explains that the value of
// Length in CF dictionaries is in bytes
final Integer length = lengthObj != null ?
lengthObj.getIntValue() * 8 : null;
// CFM is the crypt filter method, describing whether RC4,
// AES, or None (i.e., identity) is the encryption mechanism
// used for the name crypt filter
final PDFObject cfmObj = cryptFilter.getDictRef("CFM");
final String cfm = cfmObj != null ?
cfmObj.getStringValue() : "None";
final PDFDecrypter cfDecrypter;
if ("None".equals(cfm)) {
cfDecrypter = IdentityDecrypter.getInstance();
} else if ("V2".equals(cfm)) {
cfDecrypter = createStandardDecrypter(
encryptDict, documentId, password, length,
encryptMetadata,
StandardDecrypter.EncryptionAlgorithm.RC4);
} else if ("AESV2".equals(cfm)) {
cfDecrypter = createStandardDecrypter(
encryptDict, documentId, password, length,
encryptMetadata,
StandardDecrypter.EncryptionAlgorithm.AESV2);
} else {
throw new UnsupportedOperationException(
"Unknown CryptFilter method: " + cfm);
}
cfDecrypters.put(cfName, cfDecrypter);
}
// always put Identity in last so that it will override any
// Identity filter sneakily declared in the CF entry
cfDecrypters.put(CF_IDENTITY, IdentityDecrypter.getInstance());
PDFObject stmFObj = encryptDict.getDictRef("StmF");
final String defaultStreamFilter =
stmFObj != null ? stmFObj.getStringValue() : CF_IDENTITY;
PDFObject strFObj = encryptDict.getDictRef("StrF");
final String defaultStringFilter =
strFObj != null ? strFObj.getStringValue() : CF_IDENTITY;
return new CryptFilterDecrypter(
cfDecrypters, defaultStreamFilter, defaultStringFilter);
}
/**
* Create a standard single-algorithm AES or RC4 decrypter. The Encrypt
* dictionary is used where possible, but where different encryption
* versions employ different mechanisms of specifying configuration or may
* be specified via a CF entry (e.g. key length), the value is specified as
* a parameter.
*
* @param encryptDict the Encrypt dictionary
* @param documentId the document ID
* @param password the password
* @param keyLength the key length, in bits; may be <code>null</code>
* to use a {@link #DEFAULT_KEY_LENGTH default}
* @param encryptMetadata whether metadata is being encrypted
* @param encryptionAlgorithm, the encryption algorithm
* @return the decrypter
* @throws PDFAuthenticationFailureException if the provided password
* is not the one expressed by the encryption dictionary
* @throws IOException if there is a problem reading the PDF content,
* if the content does not comply with the PDF specification
* @throws EncryptionUnsupportedByPlatformException if the encryption
* is not supported by the environment in which the code is executing
* @throws EncryptionUnsupportedByProductException if PDFRenderer does
* not currently support the specified encryption
*
*/
private static PDFDecrypter createStandardDecrypter(
PDFObject encryptDict,
PDFObject documentId,
PDFPassword password,
Integer keyLength,
boolean encryptMetadata,
StandardDecrypter.EncryptionAlgorithm encryptionAlgorithm)
throws
PDFAuthenticationFailureException,
IOException,
EncryptionUnsupportedByPlatformException,
EncryptionUnsupportedByProductException {
if (keyLength == null) {
keyLength = DEFAULT_KEY_LENGTH;
}
// R describes the revision of the security handler
final PDFObject rObj = encryptDict.getDictRef("R");
if (rObj == null) {
throw new PDFParseException(
"No R entry present in Encrypt dictionary");
}
final int revision = rObj.getIntValue();
if (revision < 2 || revision > 4) {
throw new EncryptionUnsupportedByPlatformException(
"Unsupported Standard security handler revision; R=" +
revision);
}
// O describes validation details for the owner key
final PDFObject oObj = encryptDict.getDictRef("O");
if (oObj == null) {
throw new PDFParseException(
"No O entry present in Encrypt dictionary");
}
final byte[] o = oObj.getStream();
if (o.length != 32) {
throw new PDFParseException("Expected owner key O " +
"value of 32 bytes; found " + o.length);
}
// U describes validation details for the user key
final PDFObject uObj = encryptDict.getDictRef("U");
if (uObj == null) {
throw new PDFParseException(
"No U entry present in Encrypt dictionary");
}
final byte[] u = uObj.getStream();
if (u.length != 32) {
throw new PDFParseException(
"Expected user key U value of 32 bytes; found " + o.length);
}
// P describes the permissions regarding document usage
final PDFObject pObj = encryptDict.getDictRef("P");
if (pObj == null) {
throw new PDFParseException(
"Required P entry in Encrypt dictionary not found");
}
return new StandardDecrypter(
encryptionAlgorithm, documentId, keyLength,
revision, o, u, pObj.getIntValue(), encryptMetadata, password);
}
}

View File

@ -0,0 +1,277 @@
/*
* Copyright 2008 Pirion Systems Pty Ltd, 139 Warry St,
* Fortitude Valley, Queensland, Australia
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview.decrypt;
import com.sun.pdfview.PDFDocCharsetEncoder;
import com.sun.pdfview.Identity8BitCharsetEncoder;
import com.sun.pdfview.PDFStringUtil;
import java.util.*;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.CharsetEncoder;
import java.nio.CharBuffer;
import java.nio.ByteBuffer;
/**
* <p>Identifies a PDF Password, expressible either as a string or a
* byte sequence.</p>
*
* <p>In revisions up to version 1.e Expansion 3, the mapping between a string
* and the bytes corresponding to the password was poorly specified, meaning
* that the safest manner in which to specify a password was via a byte array.
* With 1.7 expansion 3, a still slightly problematic mapping was given for the
* Standard encryption algorithms through to version 4, and a very well
* specified mapping for the new version 5 encryption.</p>
*
* <p>So, for passwords specified in versions up to and including 4, a byte[]
* representation is the most accurate, but not necessarily the most convenient
* manner to provide passwords. For version 5, allowing passwords to be
* specified as Strings will be the preferred mechanism. Rather than specify two
* interfaces whenever a password can be provided - one for byte[] and one for
* String - we express the password as a class. This class can also offer a best
* guess at a String representation for a password for encryption versions up to
* and including 4.</p>
*
* @author Luke Kirby
*/
public class PDFPassword {
/** The empty password */
public static final PDFPassword EMPTY_PASSWORD =
new PDFPassword(new byte[0]);
/**
* Ensure a non-null PDFPassword by substituting the empty password
* for a null password
* @param password the password, may be null
* @return a non-null password
*/
public static PDFPassword nonNullPassword(PDFPassword password) {
return password != null ? password : EMPTY_PASSWORD;
}
/** the password in bytes, if specified as such */
private byte[] passwordBytes = null;
/** the passwird as a string, if specified as such */
private String passwordString = null;
/**
* Construct a byte-based password
* @param passwordBytes the password bytes
*/
public PDFPassword(byte[] passwordBytes) {
this.passwordBytes =
passwordBytes != null ? passwordBytes : new byte[0];
}
/**
* Construct a string-based password
* @param passwordString the password
*/
public PDFPassword(String passwordString) {
this.passwordString = passwordString != null ? passwordString : "";
}
/**
* Get the password bytes.
*
* @param unicodeConversion whether the specific conversion from a unicode
* String, as present for version 5 encryption, should be used
* @return a list of possible password bytes
*/
List<byte[]> getPasswordBytes(boolean unicodeConversion) {
// TODO - handle unicodeConversion when we support version 5
if (this.passwordBytes != null || this.passwordString == null) {
return Collections.singletonList(this.passwordBytes);
} else {
if (isAlphaNum7BitString(this.passwordString)) {
// there's no reasonthat this string would get encoded
// in any other way
return Collections.singletonList(
PDFStringUtil.asBytes(passwordString));
} else {
return generatePossiblePasswordBytes(passwordString);
}
}
}
/**
* An array of password byte generators that attempts to enumerate the
* possible strategies that an encrypting application might take to convert
* a string to an array of bytes
*/
private final static PasswordByteGenerator[] PASSWORD_BYTE_GENERATORS =
new PasswordByteGenerator[]{
// The best option, and that recommended by the spec, is
// straight PDFDocEncoding of the string but its not
// mentioned what to do with undefined characters
// (presumably, an encryption generating app should not
// allow them, but there are no guarantees!). Plus, that
// hasn't always been the case. There's also a possiblity
// that we'll be presented with the byte encoding from
// whatever code page is default on the system that
// generated the password. I don't think we're going to try
// all different code pages, though. Here are
// a few ideas, anyway!
// skip undefined chars
new PDFDocEncodingByteGenerator(null),
// replace undefined chars with 0
new PDFDocEncodingByteGenerator(Byte.valueOf((byte) 0)),
// replace undefined chars with ?
new PDFDocEncodingByteGenerator(Byte.valueOf((byte) '?')),
// just strip the higher 8 bits!
new PasswordByteGenerator() {
public byte[] generateBytes(String password) {
return PDFStringUtil.asBytes(password);
}
},
// skip 2-byte chars
new IdentityEncodingByteGenerator(null),
// replace 2-byte chars with 0
new IdentityEncodingByteGenerator(Byte.valueOf((byte) 0)),
// replace 2-byte chars with ?
new IdentityEncodingByteGenerator(Byte.valueOf((byte) '?'))
};
/**
* Generate some possible byte representations of a string password
*
* @param passwordString the string password
* @return a list of unique possible byte representations
*/
private static List<byte[]> generatePossiblePasswordBytes(
String passwordString) {
final List<byte[]> possibilties = new ArrayList<byte[]>();
for (final PasswordByteGenerator generator : PASSWORD_BYTE_GENERATORS) {
byte[] generated = generator.generateBytes(passwordString);
// avoid duplicates
boolean alreadyGenerated = false;
for (int i = 0; !alreadyGenerated && i < possibilties.size(); ++i) {
if (Arrays.equals(possibilties.get(i), generated)) {
alreadyGenerated = true;
}
}
if (!alreadyGenerated) {
possibilties.add(generated);
}
}
return possibilties;
}
private boolean isAlphaNum7BitString(String string) {
for (int i = 0; i < string.length(); ++i) {
final char c = string.charAt(i);
if (c >= 127 || !Character.isLetterOrDigit(c)) {
return false;
}
}
return true;
}
/**
* Converts a string password to a byte[] representation
*/
private static interface PasswordByteGenerator {
byte[] generateBytes(String password);
}
/**
* Converts strings to byte by employing a {@link CharsetEncoder} and a
* configurable mechanism to replace or ignore characters that are
* unrepresentable according to the encoder.
*/
private static abstract class CharsetEncoderGenerator
implements PasswordByteGenerator {
private Byte replacementByte;
/**
* Class constructor
*
* @param replacementByte the byte to replace to use to represent any
* unrepresentable character, or null if unrepresentable characters
* should just be ignored
*/
protected CharsetEncoderGenerator(Byte replacementByte) {
this.replacementByte = replacementByte;
}
public byte[] generateBytes(String password) {
final CharsetEncoder encoder = createCharsetEncoder();
if (replacementByte != null) {
encoder.replaceWith(new byte[]{replacementByte});
encoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
} else {
encoder.onUnmappableCharacter(CodingErrorAction.IGNORE);
}
try {
final ByteBuffer b = encoder.encode(CharBuffer.wrap(password));
final byte[] bytes = new byte[b.remaining()];
b.get(bytes);
return bytes;
} catch (CharacterCodingException e) {
// shouldn't happen: unmappable characters should be the only
// problem, and we're not handling them with a report
return null;
}
}
protected abstract CharsetEncoder createCharsetEncoder();
}
/**
* Generate byte[] representations based on the PDFDocEncoding
*/
private static class PDFDocEncodingByteGenerator
extends CharsetEncoderGenerator {
private PDFDocEncodingByteGenerator(Byte replacementByte) {
super(replacementByte);
}
protected CharsetEncoder createCharsetEncoder() {
return new PDFDocCharsetEncoder();
}
}
/**
* Generate byte[] representations based on a Unicode code point identity
* encoding; characters over 255 in value are considered unrepresentable
*/
private static class IdentityEncodingByteGenerator
extends CharsetEncoderGenerator {
private IdentityEncodingByteGenerator(Byte replacementByte) {
super(replacementByte);
}
protected CharsetEncoder createCharsetEncoder() {
return new Identity8BitCharsetEncoder();
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,39 @@
/*
* Copyright 2008 Pirion Systems Pty Ltd, 139 Warry St,
* Fortitude Valley, Queensland, Australia
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview.decrypt;
/**
* Identifies that the specified encryption mechanism is not
* supported by this product or platform.
*
* @see EncryptionUnsupportedByPlatformException
* @see EncryptionUnsupportedByProductException
* @author Luke Kirby
*/
public abstract class UnsupportedEncryptionException extends Exception {
protected UnsupportedEncryptionException(String message) {
super(message);
}
protected UnsupportedEncryptionException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -0,0 +1,243 @@
/*
* $Id: BuiltinFont.java,v 1.4 2009/03/15 20:47:38 tomoke Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview.font;
import android.content.res.Resources;
import android.graphics.Typeface;
import com.sun.pdfview.PDFObject;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
* This class represents the 14 built-in fonts. It reads these fonts
* from files in the "res" directory, as specified in
* BaseNames.properties.
*
* @author Ferenc Hechler (ferenc@hechler.de)
* @author Joerg Jahnke (joergjahnke@users.sourceforge.net)
*/
public class BuiltinFont extends Type1Font {
/** the properties file */
private static Properties props;
/**
* Android resources
*/
private static Resources resources;
/**
* package of the resources
*/
private static String pkg;
/** the names of the 14 base fonts */
private static final String[] baseFonts = {
"Courier", "Courier-Bold", "Courier-BoldOblique", "Courier-Oblique",
"Helvetica", "Helvetica-Bold", "Helvetica-BoldOblique",
"Helvetica-Oblique", "Times-Roman", "Times-Bold", "Times-BoldItalic",
"Times-Italic", "Symbol", "ZapfDingbats"
};
/** fonts others (e.g. Acrobad PDFWriter 3.02 for Windows) assume
* are there, even though they're not in the spec. Grrr...
*
* the format is <Name_in_PDF> <Builtin_To_Use>
*/
private static final String[] mappedFonts = {
// map arial to helvetica
"Arial", "Helvetica",
"Arial,Bold", "Helvetica-Bold",
"Arial,BoldItalic", "Helvetica-BoldOblique",
"Arial,Italic", "Helvetica-Oblique",
// map TimesNewRoman to Times
"TimesNewRoman", "Times-Roman",
"TimesNewRoman,Bold", "Times-Bold",
"TimesNewRoman,BoldItalic", "Times-BoldItalic",
"TimesNewRoman,Italic", "Times-Italic",};
/**
* Create a new Builtin object based on the name of a built-in font
*
* This must be the name of one of the 14 built-in fonts!
*
* @param baseFont the name of the font, from the PDF file
* @param fontObj the object containing font information
*/
public BuiltinFont(String baseFont, PDFObject fontObj) throws IOException {
super(baseFont, fontObj, null);
parseFont(baseFont);
}
/**
* create a new BuiltingFont object based on a description of the
* font from the PDF file. Parse the description for key information
* and use that to generate an appropriate font.
*/
public BuiltinFont(String baseFont, PDFObject fontObj,
PDFFontDescriptor descriptor)
throws IOException {
super(baseFont, fontObj, descriptor);
String fontName = descriptor.getFontName();
// check if it's one of the 14 base fonts
for (int i = 0; i < baseFonts.length; i++) {
if (fontName.equalsIgnoreCase(baseFonts[i])) {
parseFont(fontName);
return;
}
}
// check if it's a mapped font
for (int i = 0; i < mappedFonts.length; i += 2) {
if (fontName.equalsIgnoreCase(mappedFonts[i])) {
parseFont(mappedFonts[i + 1]);
return;
}
}
int flags = descriptor.getFlags();
int style = ((flags & PDFFontDescriptor.FORCEBOLD) != 0) ? Typeface.BOLD : Typeface.NORMAL;
if (fontName.indexOf("Bold") > 0) {
style |= Typeface.BOLD;
}
if ((descriptor.getItalicAngle() != 0) ||
((flags & PDFFontDescriptor.NONSYMBOLIC) != 0)) {
style |= Typeface.ITALIC;
}
String name = null;
if ((flags & PDFFontDescriptor.FIXED_PITCH) != 0) { // fixed width
if (((style & Typeface.BOLD) > 0) && ((style & Typeface.ITALIC) > 0)) {
name = "Courier-BoldOblique";
} else if ((style & Typeface.BOLD) > 0) {
name = "Courier-Bold";
} else if ((style & Typeface.ITALIC) > 0) {
name = "Courier-Oblique";
} else {
name = "Courier";
}
} else if ((flags & PDFFontDescriptor.SERIF) != 0) { // serif font
if (((style & Typeface.BOLD) > 0) && ((style & Typeface.ITALIC) > 0)) {
name = "Times-BoldItalic";
} else if ((style & Typeface.BOLD) > 0) {
name = "Times-Bold";
} else if ((style & Typeface.ITALIC) > 0) {
name = "Times-Italic";
} else {
name = "Times-Roman";
}
} else {
if (((style & Typeface.BOLD) > 0) && ((style & Typeface.ITALIC) > 0)) {
name = "Helvetica-BoldOblique";
} else if ((style & Typeface.BOLD) > 0) {
name = "Helvetica-Bold";
} else if ((style & Typeface.ITALIC) > 0) {
name = "Helvetica-Oblique";
} else {
name = "Helvetica";
}
}
parseFont(name);
}
/**
* Set the resources instance which we can use to access further raw resources
*
* @param res context's resources instance
* @param p package where to find the resources
*/
public static void setResources(final Resources res, final String p) {
resources = res;
pkg = p;
}
/**
* Parse a font given only the name of a builtin font
*/
private void parseFont(String baseFont) throws IOException {
// load the base fonts properties files, if it isn't already loaded
if (props == null) {
props = new Properties();
try {
props.load(BuiltinFont.class.getResourceAsStream("res/BaseFonts.properties"));
} catch (NullPointerException e) {
props.load(resources.openRawResource(resources.getIdentifier("basefonts", "raw", pkg)));
}
}
// make sure we're a known font
if (!props.containsKey(baseFont + ".file")) {
throw new IllegalArgumentException("Unknown Base Font: " + baseFont);
}
// get the font information from the properties file
String file = props.getProperty(baseFont + ".file");
// the size of the file
int length = Integer.parseInt(props.getProperty(baseFont + ".length"));
// the size of the unencrypted portion
int length1 = 0;
// the size of the encrypted portion
int length2 = 0;
// read the data from the file
byte[] data = new byte[length];
InputStream fontStream = BuiltinFont.class.getResourceAsStream("res/" + file);
if (fontStream == null) {
fontStream = resources.openRawResource(resources.getIdentifier(file.substring(0, file.indexOf('.')), "raw", pkg));
}
int cur = 0;
while (cur < length) {
cur += fontStream.read(data, cur, length - cur);
}
fontStream.close();
// are we a pfb file?
if ((data[0] & 0xff) == 0x80) {
// read lengths from the file
length1 = (data[2] & 0xff);
length1 |= (data[3] & 0xff) << 8;
length1 |= (data[4] & 0xff) << 16;
length1 |= (data[5] & 0xff) << 24;
length1 += 6;
length2 = (data[length1 + 2] & 0xff);
length2 |= (data[length1 + 3] & 0xff) << 8;
length2 |= (data[length1 + 4] & 0xff) << 16;
length2 |= (data[length1 + 5] & 0xff) << 24;
length1 += 6;
} else {
// get the values from the properties file
length1 = Integer.parseInt(props.getProperty(baseFont + ".length1"));
if (props.containsKey(baseFont + ".length2")) {
length2 = Integer.parseInt(props.getProperty(baseFont + ".lenth2"));
} else {
length2 = length - length1;
}
}
parseFont(data, length1, length2);
}
}

View File

@ -0,0 +1,274 @@
/*
* $Id: CIDFontType2.java,v 1.5 2009/03/15 20:47:38 tomoke Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview.font;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import net.sf.andpdf.nio.ByteBuffer;
import android.graphics.Path;
import com.sun.pdfview.PDFObject;
/**
* a font object derived from a CID font.
*
* @author Jonathan Kaplan
*/
public class CIDFontType2 extends TTFFont {
/**
* The width of each glyph from the DW and W arrays
*/
private Map<Character, Float> widths = null;
/**
* The vertical width of each glyph from the DW2 and W2 arrays
*/
private Map<Character, Float> widthsVertical = null;
/*
* the default width
*/
private int defaultWidth = 1000;
/*
* the default vertical width
*/
private int defaultWidthVertical = 1000;
/** the CIDtoGID map, if any */
private ByteBuffer cidToGidMap;
/**
* create a new CIDFontType2 object based on the name of a built-in font
* and the font descriptor
* @param baseName the name of the font, from the PDF file
* @param fontObj a dictionary that contains the DW (defaultWidth) and
* W (width) parameters
* @param descriptor a descriptor for the font
*/
public CIDFontType2(String baseName, PDFObject fontObj,
PDFFontDescriptor descriptor) throws IOException {
super(baseName, fontObj, descriptor);
parseWidths(fontObj);
// read the CIDSystemInfo dictionary (required)
PDFObject systemInfoObj = fontObj.getDictRef("CIDSystemInfo");
// read the cid to gid map (optional)
PDFObject mapObj = fontObj.getDictRef("CIDToGIDMap");
// only read the map if it is a stream (if it is a name, it
// is "Identity" and can be ignored
if (mapObj != null && (mapObj.getType() == PDFObject.STREAM)) {
cidToGidMap = mapObj.getStreamBuffer();
}
}
/** Parse the Widths array and DW object */
private void parseWidths(PDFObject fontObj)
throws IOException {
// read the default width (otpional)
PDFObject defaultWidthObj = fontObj.getDictRef("DW");
if (defaultWidthObj != null) {
defaultWidth = defaultWidthObj.getIntValue();
}
int entryIdx = 0;
int first = 0;
int last = 0;
PDFObject[] widthArray;
// read the widths table
PDFObject widthObj = fontObj.getDictRef("W");
if (widthObj != null) {
// initialize the widths array
widths = new HashMap<Character, Float>();
// parse the width array
widthArray = widthObj.getArray();
/* an entry can be in one of two forms:
* <startIndex> <endIndex> <value> or
* <startIndex> [ array of values ]
* we use the entryIdx to differentitate between them
*/
for (int i = 0; i < widthArray.length; i++) {
if (entryIdx == 0) {
// first value in an entry. Just store it
first = widthArray[i].getIntValue();
} else if (entryIdx == 1) {
// second value -- is it an int or array?
if (widthArray[i].getType() == PDFObject.ARRAY) {
// add all the entries in the array to the width array
PDFObject[] entries = widthArray[i].getArray();
for (int c = 0; c < entries.length; c++) {
Character key = new Character((char) (c + first));
// value is width / default width
float value = entries[c].getIntValue();
widths.put(key, new Float(value));
}
// all done
entryIdx = -1;
} else {
last = widthArray[i].getIntValue();
}
} else {
// third value. Set a range
int value = widthArray[i].getIntValue();
// set the range
for (int c = first; c <= last; c++) {
widths.put(new Character((char) c), new Float(value));
}
// all done
entryIdx = -1;
}
entryIdx++;
}
}
// read the optional vertical default width
defaultWidthObj = fontObj.getDictRef("DW2");
if (defaultWidthObj != null) {
defaultWidthVertical = defaultWidthObj.getIntValue();
}
// read the vertical widths table
widthObj = fontObj.getDictRef("W2");
if (widthObj != null) {
// initialize the widths array
widthsVertical = new HashMap<Character, Float>();
// parse the width2 array
widthArray = widthObj.getArray();
/* an entry can be in one of two forms:
* <startIndex> <endIndex> <value> or
* <startIndex> [ array of values ]
* we use the entryIdx to differentitate between them
*/
entryIdx = 0;
first = 0;
last = 0;
for (int i = 0; i < widthArray.length; i++) {
if (entryIdx == 0) {
// first value in an entry. Just store it
first = widthArray[i].getIntValue();
} else if (entryIdx == 1) {
// second value -- is it an int or array?
if (widthArray[i].getType() == PDFObject.ARRAY) {
// add all the entries in the array to the width array
PDFObject[] entries = widthArray[i].getArray();
for (int c = 0; c < entries.length; c++) {
Character key = new Character((char) (c + first));
// value is width / default width
float value = entries[c].getIntValue();
widthsVertical.put(key, new Float(value));
}
// all done
entryIdx = -1;
} else {
last = widthArray[i].getIntValue();
}
} else {
// third value. Set a range
int value = widthArray[i].getIntValue();
// set the range
for (int c = first; c <= last; c++) {
widthsVertical.put(new Character((char) c), new Float(value));
}
// all done
entryIdx = -1;
}
entryIdx++;
}
}
}
/** Get the default width in text space */
@Override
public int getDefaultWidth() {
return defaultWidth;
}
/** Get the width of a given character */
@Override
public float getWidth(char code, String name) {
if (widths == null) {
return 1f;
}
Float w = widths.get(new Character(code));
if (w == null) {
return 1f;
}
return w.floatValue() / getDefaultWidth();
}
/** Get the default vertical width in text space */
public int getDefaultWidthVertical() {
return defaultWidthVertical;
}
/** Get the vertical width of a given character */
public float getWidthVertical(char code, String name) {
if (widthsVertical == null) {
return 1f;
}
Float w = widthsVertical.get(new Character(code));
if (w == null) {
return 1f;
}
return w.floatValue() / getDefaultWidth();
}
/**
* Get the outline of a character given the character code. We
* interpose here in order to avoid using the CMap of the font in
* a CID mapped font.
*/
@Override
protected synchronized Path getOutline(char src, float width) {
int glyphId = (int) (src & 0xffff);
// check if there is a cidToGidMap
if (cidToGidMap != null) {
// read the map
glyphId = cidToGidMap.getChar(glyphId * 2);
}
// call getOutline on the glyphId
return getOutline(glyphId, width);
}
}

View File

@ -0,0 +1,50 @@
/*
* $Id: FlPoint.java,v 1.2 2007/12/20 18:33:31 rbair Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview.font;
/**
* A floating-point Point, with public fields. Also contains a flag
* for "open" to indicate that the path this point is a member of has
* or hasn't been closed.
*
* @author Mike Wessler
*/
public class FlPoint {
/** x coordinate of the point */
public float x= 0;
/** y coordinate of the point */
public float y= 0;
/**
* whether the path this point is a part of is open or closed.
* used in Type1CFont.java.
*/
public boolean open= false;
/** reset the values to (0,0) and closed */
public final void reset() {
x= 0;
y= 0;
open= false;
}
}

View File

@ -0,0 +1,397 @@
/*
* $Id: FontSupport.java,v 1.3 2009/03/15 20:47:38 tomoke Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview.font;
/**
* some constants and utility functions for font support.
* @author Mike Wessler
*/
public class FontSupport {
/**
* names for glyphs in the standard Adobe order. This is the ordering
* of the glyphs in a font, not the mapping of character number to
* character.
*/
public static final String stdNames[] = {
".notdef", "space", "exclam", "quotedbl", "numbersign", "dollar",
"percent", "ampersand", "quoteright", "parenleft", "parenright",
"asterisk", "plus", "comma", "hyphen", "period", "slash", "zero",
"one", "two", "three", "four", "five", "six", "seven", "eight",
"nine", "colon", "semicolon", "less", "equal", "greater", "question",
"at", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
"N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
"bracketleft", "backslash", "bracketright", "asciicircum",
"underscore", "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h",
"i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v",
"w", "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde",
"exclamdown", "cent", "sterling", "fraction", "yen", "florin",
"section", "currency", "quotesingle", "quotedblleft", "guillemotleft",
"guilsinglleft", "guilsinglright", "fi", "fl", "endash", "dagger",
"daggerdbl", "periodcentered", "paragraph", "bullet",
"quotesinglbase", "quotedblbase", "quotedblright", "guillemotright",
"ellipsis", "perthousand", "questiondown", "grave", "acute",
"circumflex", "tilde", "macron", "breve", "dotaccent", "dieresis",
"ring", "cedilla", "hungarumlaut", "ogonek", "caron", "emdash", "AE",
"ordfeminine", "Lslash", "Oslash", "OE", "ordmasculine", "ae",
"dotlessi", "lslash", "oslash", "oe", "germandbls", "onesuperior",
"logicalnot", "mu", "trademark", "Eth", "onehalf", "plusminus",
"Thorn", "onequarter", "divide", "brokenbar", "degree", "thorn",
"threequarters", "twosuperior", "registered", "minus", "eth",
"multiply", "threesuperior", "copyright", "Aacute", "Acircumflex",
"Adieresis", "Agrave", "Aring", "Atilde", "Ccedilla", "Eacute",
"Ecircumflex", "Edieresis", "Egrave", "Iacute", "Icircumflex",
"Idieresis", "Igrave", "Ntilde", "Oacute", "Ocircumflex", "Odieresis",
"Ograve", "Otilde", "Scaron", "Uacute", "Ucircumflex", "Udieresis",
"Ugrave", "Yacute", "Ydieresis", "Zcaron", "aacute", "acircumflex",
"adieresis", "agrave", "aring", "atilde", "ccedilla", "eacute",
"ecircumflex", "edieresis", "egrave", "iacute", "icircumflex",
"idieresis", "igrave", "ntilde", "oacute", "ocircumflex", "odieresis",
"ograve", "otilde", "scaron", "uacute", "ucircumflex", "udieresis",
"ugrave", "yacute", "ydieresis", "zcaron", "exclamsmall",
"Hungarumlautsmall", "dollaroldstyle", "dollarsuperior",
"ampersandsmall", "Acutesmall", "parenleftsuperior",
"parenrightsuperior", "twodotenleader", "onedotenleader",
"zerooldstyle", "oneoldstyle", "twooldstyle", "threeoldstyle",
"fouroldstyle", "fiveoldstyle", "sixoldstyle", "sevenoldstyle",
"eightoldstyle", "nineoldstyle", "commasuperior",
"threequartersemdash", "periodsuperior", "questionsmall", "asuperior",
"bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior",
"lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior",
"ssuperior", "tsuperior", "ff", "ffi", "ffl", "parenleftinferior",
"parenrightinferior", "Circumflexsmall", "hyphensuperior",
"Gravesmall", "Asmall", "Bsmall", "Csmall", "Dsmall", "Esmall",
"Fsmall", "Gsmall", "Hsmall", "Ismall", "Jsmall", "Ksmall", "Lsmall",
"Msmall", "Nsmall", "Osmall", "Psmall", "Qsmall", "Rsmall", "Ssmall",
"Tsmall", "Usmall", "Vsmall", "Wsmall", "Xsmall", "Ysmall", "Zsmall",
"colonmonetary", "onefitted", "rupiah", "Tildesmall",
"exclamdownsmall", "centoldstyle", "Lslashsmall", "Scaronsmall",
"Zcaronsmall", "Dieresissmall", "Brevesmall", "Caronsmall",
"Dotaccentsmall", "Macronsmall", "figuredash", "hypheninferior",
"Ogoneksmall", "Ringsmall", "Cedillasmall", "questiondownsmall",
"oneeighth", "threeeighths", "fiveeighths", "seveneighths",
"onethird", "twothirds", "zerosuperior", "foursuperior",
"fivesuperior", "sixsuperior", "sevensuperior", "eightsuperior",
"ninesuperior", "zeroinferior", "oneinferior", "twoinferior",
"threeinferior", "fourinferior", "fiveinferior", "sixinferior",
"seveninferior", "eightinferior", "nineinferior", "centinferior",
"dollarinferior", "periodinferior", "commainferior", "Agravesmall",
"Aacutesmall", "Acircumflexsmall", "Atildesmall", "Adieresissmall",
"Aringsmall", "AEsmall", "Ccedillasmall", "Egravesmall",
"Eacutesmall", "Ecircumflexsmall", "Edieresissmall", "Igravesmall",
"Iacutesmall", "Icircumflexsmall", "Idieresissmall", "Ethsmall",
"Ntildesmall", "Ogravesmall", "Oacutesmall", "Ocircumflexsmall",
"Otildesmall", "Odieresissmall", "OEsmall", "Oslashsmall",
"Ugravesmall", "Uacutesmall", "Ucircumflexsmall", "Udieresissmall",
"Yacutesmall", "Thornsmall", "Ydieresissmall", "001.000", "001.001",
"001.002", "001.003", "Black", "Bold", "Book", "Light", "Medium",
"Regular", "Roman", "Semibold"
};
/**
* characters for glyphs in the standard order. These are string "values"
* to go with the names in stdNames. Not all glyphs have been translated
* to their unicode values. In many cases, the name of the glyph has
* been appended to an ASCII approximation of the glyph. Strings longer
* than 3 characters have this characteristic. To get the character,
* use the string if it contains 3 or fewer characters; otherwise,
* grab the first character off the string and use that.
*/
static final String stdValues[] = {
"", " ", "!", "\"", "#", "$",
"%", "&", "'", "(", ")",
"*", "+", ",", "-", ".", "/", "0",
"1", "2", "3", "4", "5", "6", "7", "8",
"9", ":", ";", "<", "=", ">", "?",
"@", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
"N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
"[", "\\", "]", "^",
"_", "`", "a", "b", "c", "d", "e", "f", "g", "h",
"i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v",
"w", "x", "y", "z", "{", "|", "}", "~",
"\u00a1", "\u00a2", "\u00a3", "/fraction", "\u00a5", "Fflorin",
"\u00a7", "\u00a4", "\u00b4quotesingle", "\u201c", "?guillemotleft",
"\u2039", "\u203a", "fi", "fl", "--", "\u2020",
"\u2021", "\u00b7", "\u00b6", "\u2022",
"'quotesinglbase", "\"quotedblbase", "\"quotedblright", "?guillemotright",
"...ellipsis", "%perthousand", "?questiondown", "`grave", "'acute",
"^circumflex", "~tilde", "-macron", "?breve", "?dotaccent", "?dieresis",
"oring", "ccedilla", ":hungarumlaut", "?ogonek", ",caron", "---emdash", "AE",
"aordfeminine", "LLslash", "OOslash", "OE", "oordmasculine", "ae",
"idotlessi", "llslash", "ooslash", "oe", "Bgermandbls", "1onesuperior",
"~logicalnot", "?mu", "(TM)trademark", "?Eth", "1/2", "+/-",
"?Thorn", "1/4", "/divide", "|brokenbar", "*degree", "?thorn",
"3/4", "2twosuperior", "(R)", "-minus", "?eth",
"*multiply", "3threesuperior", "(C)", "AAacute", "AAcircumflex",
"AAdieresis", "AAgrave", "AAring", "AAtilde", "CCcedilla", "EEacute",
"EEcircumflex", "EEdieresis", "EEgrave", "IIacute", "IIcircumflex",
"IIdieresis", "IIgrave", "NNtilde", "OOacute", "OOcircumflex", "OOdieresis",
"OOgrave", "OOtilde", "SScaron", "UUacute", "UUcircumflex", "UUdieresis",
"UUgrave", "YYacute", "YYdieresis", "ZZcaron", "aaacute", "aacircumflex",
"aadieresis", "aagrave", "aaring", "aatilde", "cccedilla", "eeacute",
"eecircumflex", "eedieresis", "eegrave", "iiacute", "iicircumflex",
"iidieresis", "iigrave", "nntilde", "ooacute", "oocircumflex", "oodieresis",
"oograve", "ootilde", "sscaron", "uuacute", "uucircumflex", "uudieresis",
"uugrave", "yyacute", "yydieresis", "zzcaron", "!exclamsmall",
"?Hungarumlautsmall", "$dollaroldstyle", "$dollarsuperior",
"&ampersandsmall", "'Acutesmall", "/parenleftsuperior",
"\\parenrightsuperior", "?twodotenleader", "?onedotenleader",
"0zerooldstyle", "1oneoldstyle", "2twooldstyle", "3threeoldstyle",
"4fouroldstyle", "5fiveoldstyle", "6sixoldstyle", "7sevenoldstyle",
"8eightoldstyle", "9nineoldstyle", "'commasuperior",
"--threequartersemdash", ".periodsuperior", "?questionsmall", "aasuperior",
"bbsuperior", "ccentsuperior", "ddsuperior", "eesuperior", "iisuperior",
"llsuperior", "mmsuperior", "nnsuperior", "oosuperior", "rrsuperior",
"sssuperior", "ttsuperior", "ff", "ffi", "ffl", "\\parenleftinferior",
"/parenrightinferior", "^Circumflexsmall", "-hyphensuperior",
"`Gravesmall", "AAsmall", "BBsmall", "CCsmall", "DDsmall", "EEsmall",
"FFsmall", "GGsmall", "HHsmall", "IIsmall", "JJsmall", "KKsmall", "LLsmall",
"MMsmall", "NNsmall", "OOsmall", "PPsmall", "QQsmall", "RRsmall", "SSsmall",
"TTsmall", "UUsmall", "VVsmall", "WWsmall", "XXsmall", "YYsmall", "ZZsmall",
":colonmonetary", "1onefitted", "?rupiah", "~Tildesmall",
"!exclamdownsmall", "ccentoldstyle", "LLslashsmall", "SScaronsmall",
"ZZcaronsmall", "?Dieresissmall", "?Brevesmall", "^Caronsmall",
"?Dotaccentsmall", "?Macronsmall", "--figuredash", "-hypheninferior",
"?Ogoneksmall", "oRingsmall", ",Cedillasmall", "?questiondownsmall",
"1/8oneeighth", "3/8threeeighths", "5/8fiveeighths", "7/8seveneighths",
"1/3onethird", "2/3twothirds", "0zerosuperior", "4foursuperior",
"5fivesuperior", "6sixsuperior", "7sevensuperior", "8eightsuperior",
"9ninesuperior", "0zeroinferior", "1oneinferior", "2twoinferior",
"3threeinferior", "4fourinferior", "5fiveinferior", "6sixinferior",
"7seveninferior", "8eightinferior", "9nineinferior", "ccentinferior",
"$dollarinferior", ".periodinferior", ",commainferior", "AAgravesmall",
"AAacutesmall", "AAcircumflexsmall", "AAtildesmall", "AAdieresissmall",
"AAringsmall", "AEAEsmall", "CCcedillasmall", "EEgravesmall",
"EEacutesmall", "EEcircumflexsmall", "EEdieresissmall", "IIgravesmall",
"IIacutesmall", "IIcircumflexsmall", "IIdieresissmall", "EthEthsmall",
"NNtildesmall", "OOgravesmall", "OOacutesmall", "OOcircumflexsmall",
"OOtildesmall", "OOdieresissmall", "OEOEsmall", "OOslashsmall",
"UUgravesmall", "UUacutesmall", "UUcircumflexsmall", "UUdieresissmall",
"YYacutesmall", "?Thornsmall", "YYdieresissmall", "?001.000", "?001.001",
"?001.002", "?001.003", " Black", " Bold", " Book", " Light", " Medium",
" Regular", " Roman", " Semibold",
/* extra mac stuff */
"?NUL", "?HT", " LF", " CR", "?DLE", "?DC1", "?DC2", "?DC3", "?DC4", "?RS",
"?US", "!=", "?DEL", "?infinity", "<=", ">=",
"?partialdiff", "?summation", "xproduct", "?pi", "?integral", "?Omega",
"?radical", "~=", "?Delta", " nbspace", "?lozenge", "?apple"
};
/**
* glyph order of the glyphs for the Type1C Expert character set. These
* are indices into the glyph name array.
*/
public static final int type1CExpertCharset[] = {
1, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 13, 14, 15, 99,
239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 27, 28, 249, 250,
251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264,
265, 266, 109, 110, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276,
277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290,
291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304,
305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318,
158, 155, 163, 319, 320, 321, 322, 323, 324, 325, 326, 150, 164, 169,
327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340,
341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354,
355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368,
369, 370, 371, 372, 373, 374, 375, 376, 377, 378
};
/**
* glyph order of the glyphs for the Type1C Expert Sub character set.
* These are indices into the glyph name array.
*/
public static final int type1CExpertSubCharset[] = {
1, 231, 232, 235, 236, 237, 238, 13, 14, 15, 99, 239, 240, 241, 242,
243, 244, 245, 246, 247, 248, 27, 28, 249, 250, 251, 253, 254, 255,
256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 109, 110, 267,
268, 269, 270, 272, 300, 301, 302, 305, 314, 315, 158, 155, 163, 320,
321, 322, 323, 324, 325, 326, 150, 164, 169, 327, 328, 329, 330, 331,
332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345,
346
};
/**
* extra names for the Macintosh glyph set. This array should be
* considered to be appended to the stdNames array. The stdValues array
* already contains values for this set.
*/
public static final String macExtras[] = { // index starts at 391=NUL
"NUL", "HT", "LF", "CR", "DLE", "DC1", "DC2", "DC3", "DC4", "RS",
"US", "notequal", "DEL", "infinity", "lessequal", "greaterequal",
"partialdiff", "summation", "product", "pi", "integral", "Omega",
"radical", "approxequal", "Delta", "nbspace", "lozenge", "apple"
};
/**
* character mapping from values to glyphs for the Macintosh MacRoman
* encoding
*/
public static final int macRomanEncoding[] = {
391, 154, 167, 140, 146, 192, 221, 197, 226, 392, 393, 157, 162, 394,
199, 228, 395, 396, 397, 398, 399, 155, 158, 150, 163, 169, 164, 160,
166, 168, 400, 401, 1, 2, 3, 4, 5, 6, 7, 104, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 124,
66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82,
83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 403, 173, 175,
177, 178, 186, 189, 195, 200, 203, 201, 202, 205, 204, 206, 207, 210,
208, 209, 211, 214, 212, 213, 215, 216, 219, 217, 218, 220, 222, 225,
223, 224, 112, 161, 97, 98, 102, 116, 115, 149, 165, 170, 153, 125,
131, 402, 138, 141, 404, 156, 405, 406, 100, 152, 407, 408, 409, 410,
411, 139, 143, 412, 144, 147, 123, 96, 151, 413, 101, 414, 415, 106,
120, 121, 416, 174, 176, 191, 142, 148, 111, 137, 105, 119, 65, 8,
159, 417, 227, 198, 99, 103, 107, 108, 109, 110, 113, 114, 117, 118,
122, 172, 179, 171, 180, 181, 182, 183, 184, 185, 187, 188, 418, 190,
193, 194, 196, 145, 126, 127, 128, 129, 130, 132, 133, 134, 135, 136
};
/**
* character mapping from values to glyphs for the isoLatin1Encoding
*/
public static final int isoLatin1Encoding[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
166, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81,
82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 145, 124, 125, 126, 127, 128,
129, 130, 131, 0, 132, 133, 0, 134, 135, 136, 1, 96, 97, 98, 103,
100, 160, 102, 131, 170, 139, 106, 151, 14, 165, 128, 161, 156, 164,
169, 125, 152, 115, 114, 133, 150, 143, 120, 158, 155, 163, 123, 174,
171, 172, 176, 173, 175, 138, 177, 181, 178, 179, 180, 185, 182, 183,
184, 154, 186, 190, 187, 188, 191, 189, 168, 141, 196, 193, 194, 195,
197, 157, 149, 203, 200, 201, 205, 202, 204, 144, 206, 210, 207, 208,
209, 214, 211, 212, 213, 167, 215, 219, 216, 217, 220, 218, 159, 147,
225, 222, 223, 224, 226, 162, 227
};
/**
* character mapping from values to glyphs for the Windows winAnsi
* character encoding
*/
public static final int winAnsiEncoding[] = {
124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 145,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5,
6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74,
75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91,
92, 93, 94, 95, 0, 0, 0, 117, 101, 118, 121, 112, 113, 0, 122, 192,
107, 142, 0, 0, 0, 0, 65, 8, 105, 119, 116, 111, 137, 0, 153, 221,
108, 148, 0, 0, 198, 1, 96, 97, 98, 103, 100, 160, 102, 131, 170,
139, 106, 151, 14, 165, 128, 161, 156, 164, 169, 125, 152, 115, 114,
133, 150, 143, 120, 158, 155, 163, 123, 174, 171, 172, 176, 173, 175,
138, 177, 181, 178, 179, 180, 185, 182, 183, 184, 154, 186, 190, 187,
188, 191, 189, 168, 141, 196, 193, 194, 195, 197, 157, 149, 203, 200,
201, 205, 202, 204, 144, 206, 210, 207, 208, 209, 214, 211, 212, 213,
167, 215, 219, 216, 217, 220, 218, 159, 147, 225, 222, 223, 224, 226,
162, 227
};
/**
* character mapping from values to glyphs for Adobe's standard
* character encoding
*/
public static final int standardEncoding[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81,
82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105,
106, 107, 108, 109, 110, 0, 111, 112, 113, 114, 0, 115, 116, 117,
118, 119, 120, 121, 122, 0, 123, 0, 124, 125, 126, 127, 128, 129,
130, 131, 0, 132, 133, 0, 134, 135, 136, 137, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 138, 0, 139, 0, 0, 0, 0, 140, 141, 142, 143,
0, 0, 0, 0, 0, 144, 0, 0, 0, 145, 0, 0, 146, 147, 148, 149, 0, 0, 0,
0
};
/**
* get the name of a glyph from its encoding value (NOT the character
* value), using the standard encoding.
*/
public static String getName (int i) {
if (i < stdNames.length) {
return stdNames[i];
} else {
i -= stdNames.length;
if (i < macExtras.length) {
return macExtras[i];
}
}
return ".notdef";
}
/**
* get the encoding value a glyph given its name and a name table.
* @param name the name of the glyph
* @param table the charset as an array of names
* @return the index of the name in the table, or -1 if the name
* cannot be found in the table
*/
public static int findName (String name, String[] table) {
for (int i = 0; i < table.length; i++) {
if (name.equals (table[i])) {
return i;
}
}
return -1;
}
/**
* get the encoding value of a glyph given its name and a charset.
* @param name the name of the glyph
* @param table the charset table
* @return the index of the name in the charset.
*/
public static int findName (String name, int[] table) {
for (int i = 0; i < table.length; i++) {
if (name.equals (getName (table[i]))) {
return i;
}
}
return -1;
}
/**
* get the encoding value of a glyph given its name, in the standard
* charset. This is equivalent to findName(name, FontSupport.stdNames).
* @param name the name of the glyph
* @return the index of the name in stdNames, or -1 if the name doesn't
* appear in stdNames.
*/
public static int getStrIndex (String name) {
for (int i = 0; i < stdNames.length; i++) {
if (name.equals (stdNames[i])) {
return i;
}
}
return -1;
}
}

View File

@ -0,0 +1,156 @@
/*
* $Id: OutlineFont.java,v 1.3 2009/02/09 16:29:58 tomoke Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview.font;
import java.io.IOException;
import android.graphics.Path;
import android.graphics.PointF;
import com.sun.pdfview.PDFObject;
/**
* Supports width operations for Type1, Type1C, TrueType and Type3 fonts
*/
public abstract class OutlineFont extends PDFFont {
/** the first character code */
private int firstChar = -1;
/** the last character code */
private int lastChar = -1;
/** the widths for each character code */
private float[] widths;
/** Creates a new instance of OutlineFont */
public OutlineFont(String baseFont, PDFObject fontObj,
PDFFontDescriptor descriptor) throws IOException {
super(baseFont, descriptor);
PDFObject firstCharObj = fontObj.getDictRef("FirstChar");
PDFObject lastCharObj = fontObj.getDictRef("LastChar");
PDFObject widthArrayObj = fontObj.getDictRef("Widths");
if (firstCharObj != null) {
firstChar = firstCharObj.getIntValue();
}
if (lastCharObj != null) {
lastChar = lastCharObj.getIntValue();
}
if (widthArrayObj != null) {
PDFObject[] widthArray = widthArrayObj.getArray();
widths = new float[widthArray.length];
for (int i = 0; i < widthArray.length; i++) {
widths[i] = widthArray[i].getFloatValue() / getDefaultWidth();
}
}
}
/** Get the first character code */
public int getFirstChar() {
return firstChar;
}
/** Get the last character code */
public int getLastChar() {
return lastChar;
}
/** Get the default width in text space */
public int getDefaultWidth() {
return 1000;
}
/** Get the number of characters */
public int getCharCount() {
return (getLastChar() - getFirstChar()) + 1;
}
/** Get the width of a given character */
public float getWidth(char code, String name) {
int idx = (code & 0xff) - getFirstChar();
// make sure we're in range
if (idx < 0 || widths == null || idx >= widths.length) {
// try to get the missing width from the font descriptor
if (getDescriptor() != null) {
return getDescriptor().getMissingWidth();
} else {
return 0;
}
}
return widths[idx];
}
/**
* Get the glyph for a given character code and name
*
* The preferred method of getting the glyph should be by name. If the
* name is null or not valid, then the character code should be used.
* If the both the code and the name are invalid, the undefined glyph
* should be returned.
*
* Note this method must *always* return a glyph.
*
* @param src the character code of this glyph
* @param name the name of this glyph or null if unknown
* @return a glyph for this character
*/
protected PDFGlyph getGlyph(char src, String name) {
Path outline = null;
float width = getWidth(src, name);
// first try by name
if (name != null) {
outline = getOutline(name, width);
}
// now try by character code (guaranteed to return)
if (outline == null) {
outline = getOutline(src, width);
}
// calculate the advance
PointF advance = new PointF(width, 0);
return new PDFGlyph(src, name, outline, advance);
}
/**
* Get a glyph outline by name
*
* @param name the name of the desired glyph
* @return the glyph outline, or null if unavailable
*/
protected abstract Path getOutline(String name, float width);
/**
* Get a glyph outline by character code
*
* Note this method must always return an outline
*
* @param src the character code of the desired glyph
* @return the glyph outline
*/
protected abstract Path getOutline(char src, float width);
}

View File

@ -0,0 +1,106 @@
/*
* $Id: PDFCMap.java,v 1.3 2009/02/12 13:53:54 tomoke Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview.font;
import java.io.IOException;
import java.util.HashMap;
import com.sun.pdfview.PDFObject;
/**
* A CMap maps from a character in a composite font to a font/glyph number
* pair in a CID font.
*
* @author jkaplan
*/
public abstract class PDFCMap {
/**
* A cache of known CMaps by name
*/
private static HashMap<String, PDFCMap> cache;
/** Creates a new instance of CMap */
protected PDFCMap() {}
/**
* Get a CMap, given a PDF object containing one of the following:
* a string name of a known CMap
* a stream containing a CMap definition
*/
public static PDFCMap getCMap(PDFObject map) throws IOException {
if (map.getType() == PDFObject.NAME) {
return getCMap(map.getStringValue());
} else if (map.getType() == PDFObject.STREAM) {
return parseCMap(map);
} else {
throw new IOException("CMap type not Name or Stream!");
}
}
/**
* Get a CMap, given a string name
*/
public static PDFCMap getCMap(String mapName) throws IOException {
if (cache == null) {
populateCache();
}
if (!cache.containsKey(mapName)) {
throw new IOException("Unknown CMap: " + mapName);
}
return (PDFCMap) cache.get(mapName);
}
/**
* Populate the cache with well-known types
*/
protected static void populateCache() {
cache = new HashMap<String, PDFCMap>();
// add the Identity-H map
cache.put("Identity-H", new PDFCMap() {
public char map(char src) {
return src;
}
});
}
/**
* Parse a CMap from a CMap stream
*/
protected static PDFCMap parseCMap(PDFObject map) throws IOException {
throw new IOException("Parsing CMap Files Unsupported!");
}
/**
* Map a given source character to a destination character
*/
public abstract char map(char src);
/**
* Get the font number assoicated with a given source character
*/
public int getFontID(char src) {
return 0;
}
}

Some files were not shown because too many files have changed in this diff Show More