Initial commit
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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);
|
||||
|
||||
|
|
@ -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>
|
||||
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package org.bouncycastle.crypto;
|
||||
|
||||
/**
|
||||
* all parameter classes implement this.
|
||||
*/
|
||||
public interface CipherParameters
|
||||
{
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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 *;
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 983 B |
|
After Width: | Height: | Size: 904 B |
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
.DS_Store
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 >= 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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 <= 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 >= 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
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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]];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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()+"]";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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 '~>'.
|
||||
*
|
||||
* @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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
* '>' 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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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",
|
||||
"&ersandsmall", "'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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||