调用Android的PDF接口导出PDF文件,Webview生成和导出

2020-03-02 10:48 来源:未知

之前公司要求在android项目上实现一个简单的富文本编辑功能,搞的要死不活,好不容易搞出一个凑活着能用的,结果落下了一个PDF导出功能(其实是之前搞这一块搞得很火大,根本就不知道还有个PDF导出功能),所以在得知这一消息时,我是拒绝承认有这么一个功能的。时间一久,这一功能也就默认没有了。导出PD什么?不存在的。这几天闲着没事,就顺手来研究了一下android生成PDF文件的方案,踩了一些坑,最后也算搞出了一个能用的方案出来。

近期由于公司需求,需要在APP中优化保存文件功能,对除了jpg,png格式外,还需提供PDF格式,主要是为了将多个页面的内容生成一个文件,而不是保存多个图片文件到存储卡中,于是查了很多资料,看到更多的是通过第三方库进行导出的,但是由于时间紧迫,第三方库需要了解接口同时还需要配置库文件。且了解到android在19版本就提供了相关的PDF接口,于是直接去查看官方的文档:

创建实现用户期望行为的富媒体应用

    1. 用iText三方库解决扒了一下相关的帖子,貌似对中文支持不好,有乱码问题。不过具体会遇到什么问题我并没有尝试过,因为我在逛stackoverflow时了解到这玩意儿是基于GPLv3协议的,简单来说就是你用了这个库,你的相关模块也得开源,所以一般商用开发很少回去用GPL协议的三方库。因为这个也是和头争了很久,他的意思就是先不刁什么协议,搞出来把什么中文乱码一并解决了再说,出了纠纷他负责。奈何我也是一个老ass mong男了,我的想法是我们这种小作坊,没有大流氓的底子,就不要玩大流氓的手段。当然最关键的问题在于,出了问题负责有什么用,最后还不是得我改,这锅不能背,所以坚决不妥协。其他的三方库或多或少也是有各种问题,这里也就不在一一列举,也不知这篇文章的重点。这里扯句题外的,比如开发音视频常用的FFmpeg,这也是一个GPL协议的三方库。不过协议归协议,架不住别人耍流氓,所以FFmpeg上有一个耻辱墙,所有违反了这一协议的公司及其产品都被biao在了上面,其中不乏我们很熟悉的一些名字。
    2. 采用android原生的系统打印功能在android API19,即4.4版本开始,谷歌引入了打印API。鉴于当前时间下android4.4以下版本市场占有率已经不足10%,所以决定采取版本差异化处理,仅支持API19及其以上的版本。

developer.android.google.cn/reference/index.html

Capturing Photos

  1. PrintHelper这是专门用于打印bitmap的一个类,举一个栗子:

这个链接在国内也可以访问的,是google专门提供给中国开发者的。

Taking Photos Simply

具体的类的使用在官方文档中已经讲述的非常清楚:

Taking Photos Simply

使用已有的拍照程序拍照.

 PrintHelper photoPrinter = new PrintHelper(getActivity; photoPrinter.setScaleMode(PrintHelper.SCALE_MODE_FIT); Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.droids); photoPrinter.printBitmap("droids.jpg - test print", bitmap); 

// 创建一个PDF文本对象

Request Camera Permission

如果你的app必须有摄像硬件支持,则需要在Google Play上限制其安装渠道.

<manifest ... >
    <uses-feature android:name="android.hardware.camera"
                  android:required="true" />
</manifest>

如果你的app对于摄像硬件是可选的,则需要在运行时的时候检测系统是否有摄像硬件:hasSystemFeature(PackageManager.FEATURE_CAMERA)

启动命令

当我们执行photoPrinter.printBitmap()方法后系统的打印界面会弹出,用户可以自行设置一些参数(如纸张尺寸、方向、页数等),然后点击打印或者取消。另外,这里打印的bitmap不仅仅限与通过资源获取到的bitmap,例如你通过view获取到的bitmap也可以打印。

PdfDocument  document=new   PdfDocument();

Take a Photo with the Camera App

Android请求其他app来完成一个action通常会使用Intent.

static final int REQUEST_IMAGE_CAPTURE = 1;

private void dispatchTakePictureIntent() {
    Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
        startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);    
    }
}

sh /opt/openoffice4/program/soffice -headless -accept="socket,host=127.0.0.1,port=8100;urp;" -nofirststartwizard &

  1. PdfDocument这个类可以让我们通过原生的android View生成PDF文件,这里贴一下官方的例子:

//创建当前页的信息,Builder中的参数表示页面的宽高,以及第几页

Get the Thumbnail

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
        Bundle extras = data.getExtras();
        Bitmap imageBitmap = (Bitmap) extra.get("data");
        mImageView.setImageBitmap(imageBitmap);
    }    
}

美高梅网投平台 1

PageInfo  pageInfo=newPageInfo.Builder(newRect(0,0,100,100),1).create();

Save the Full-size Photo

如果提供一个具体的文件对象,Android拍照软件会保存未压缩的照片. 你必须提供一个完全限定的文件名称.

通常,用户拍摄的任何照片都应该存储在外部公共存储区域,以便所有app进行访问.
getExternalStorageDirectory() DIRECTORY_PICTURES: 共享照片的适宜存储位置.

权限请求:<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

然而,如果你只想要照片可被你个人的app访问,你需要改变存储区域:getExternalFilesDir().
在Android 4.3及以下,将照片写入该文件位置需要WRITE_EXTERNAL_STORAGE权限.
从Android 4.4开始,不再需要该权限是因为该文件位置不能再被其它app访问,所以你可以声明读权限的最高sdk版本: <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="18" />

你存储在getExternalFilesDir()或者getFilesDir()的文件会在用户卸载你的app以后一同被删除。

一旦你决定了存储文件的位置,你需要创建没有冲突的文件名称。以下为一个简单的小例子,演示如何生成独一无二的文件名称.

String mCurrentPhotoPath;

private File createImageFile() throws IOException {
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
    String imageFileName = "JPEG_"   timeStamp   "_";
    File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
    File image = File.createTempFile(imageFileName, ".jpg", storageDir);

    mCurrentPhotoPath = image.getAbsolutePath();
    return image;
}

通过结合该方法,你可以通过Intent来创建一个图片文件:

static final int REQUEST_TAKE_PHOTO = 1;

private void dispatchPictureIntent() {
    Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
        File photoFile = null;
        try {
            photoFile = createImageFile();    
        } catch (IOException ex) {}
       if (photoFile != null) {            
            Uri photoURI = FileProvider.getUriForFile(this, 
                    "com.example.android.fileprovider", photoFile);
            takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
            startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);    
        }
    }
}

getUriForFile(Context, String, File) => content://URI.在Android 7.0及以上的版本,使用file://URI将会抛出FileUriExposedException.因此,我们现在更常用FileProvider来生成图片的URI.

配置FileProvider => 在配置文件中添加一个provider

<application>
    <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="com.example.android.fileprovider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.name.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths">
        </meta-data>
    <provider>
</application>

确保android:authoritiesgetUriForFile(Context, String, File)第二个参数匹配.
在provider定义的meta-data部分,你可以看到provider提供了一个xml文件来配置合格的路径:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="my_images" path="Android/data/com.example.package.name/files/Pictures" />
</paths>

当调用带有Environment.DIRECTORY_PICTURESgetExternalFilesDir()时,路径组件会返回定义好的路径.

C:Program Files (x86)OpenOffice 4program>soffice -headless -accept="socket,host=127.0.0.1,port=8100;urp;" -nofirststartwizard

 // create a new document PdfDocument document = new PdfDocument(); // crate a page description PageInfo pageInfo = new PageInfo.Builder(new Rect(0, 0, 100, 100), 1).create(); // start a page Page page = document.startPage; // draw something on the page View content = getContentView(); content.draw(page.getCanvas; // finish the page document.finishPage; . . . // add more pages . . . // write the document content document.writeTo(getOutputStream; //close the document document.close();

// 生成当前页

Add the Photo to a Gallery

当你通过intent创建了一张照片时,你应该知道图片的存储位置. 对于其他的人来说,访问你创建的照片最简单的方式就是使其可被系统的Media Provider访问.

如果你将图片存储在了getExternalFilesDir(),media scanner不能访问该图片,因为其只对你的app可见.

private void galleryAddPic() {
    Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
    File f = new File(mCurrentPhotoPath);
    Uri contentUri = Uri.fromFile(f);
    mediaScanIntent.setData(contentUri);
    this.sendBroadcast(mediaScanIntent);
}

美高梅网投平台 2

可以看到,这里是将View的直接通过canvas画到了document的Page上面,然后通过writeTo()方法保存到指定位置。这里和下面讲到的PrintedPdfDocument都需要注意两点:一 这种方式将不再启动系统的打印界面,即可以用户操作直接生成PDF文件。二 这种方式是直接通过canvas简单粗暴地把content直接画到了PDF上,如果你的内容包含文字的话,生成的PDF是无法选中文字的,你可以理解为直接将整个View保存成了一张图片然后生成了PDF。如果包含文字的话,需要用到TextPaint:

Page  page=document.startPage(pageInfo);

Decode a Scaled Image

在低内存下管理多个未压缩图片会很复杂. 如果你发现应用在展示了很少的图片就内存溢出,你可以通过将JPEG扩展到已经缩放到与目标视图大小匹配的内存数组,大大减少使用的动态堆的数量.

private void setPic() {
    int targetW = mImageView.getWidth();
    int targetH = mImageView.getHeight();

    BitmapFactory.Options bmOptions = new BitmapFactory.Options();
    bmOptions.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
    int photoW = bmOptions.outWidth;
    int photoH = bmOptions.outHeight;

    int scaleFactor = Math.min(photoW/targetW, photoH/targetH);

    bmOptions.inJustDecodeBounds = false;
    bmOptions.isSampleSize = scaleFactor;
    bmOptions.inPurgeable = true;

    Bitmap bitmap = BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
    mImageView.setImageBitmap(bitmap);
}

  linux kill 进程

 TextPaint textPaint = new TextPaint(); textPaint.setColor(Color.BLACK); textPaint.setTextSize; textPaint.setTextAlign(Paint.Align.LEFT); Typeface textTypeface = Typeface.create(Typeface.MONOSPACE, Typeface.NORMAL); textPaint.setTypeface(textTypeface); String text = "some text"; StaticLayout mTextLayout = new StaticLayout(text, textPaint, page.getCanvas().getWidth(), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); mTextLayout.draw(page.getCanvas;

// 在当前页上画画,即把所需要的view的视图画到page的画布上

Recording Videos Simply

使用已有的相机应用录制音频.

美高梅网投平台 3

  1. PrintedPdfDocument继承于PdfDocument,区别在于可以为打印策略设置参数,比如纸张大小、外边距等。这里同样举个例子嗦一下:

View  content=getContentView();

Request Camera Permission

在 manifest 文件中使用<uses-feature>标签.

<manifest ... >
    <uses-feature android:name="android.hardware.camera"
                  android:required="true" />
</manifest>

查看8100端口号运行的程序

content.draw(page.getCanvas());

Record a Video with a Camera App

static final int REQUEST_VIDEO_CAPTURE = 1;

private void dispatchTakeVideoIntent() {
    Intent takeVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
    if (takeVideoIntent.resolveActivity(getPackageManager()) != null) {
        startActivityForResult(takeVideoIntent, REQUEST_VIDEO_CAPTURE);    
    }
}

美高梅网投平台 4

PrintAttributes attributes = new PrintAttributes.Builder() .setMediaSize(PrintAttributes.MediaSize.ISO_A4) .setResolution(new PrintAttributes.Resolution("id", Context.PRINT_SERVICE, 300, 300)) .setColorMode(PrintAttributes.COLOR_MODE_COLOR) .setMinMargins(new PrintAttributes.Margins(0, 0, 0, 0)) .build(); PdfDocument document = new PrintedPdfDocument(context, attributes); for (int i = 0; i < numberOfPages; i  ) { int webMarginTop = i * letterSizeHeight; PdfDocument.PageInfo pageInfo = new PdfDocument.PageInfo.Builder(webViewWidth, letterSizeHeight, i   1).create(); PdfDocument.Page page = document.startPage; page.getCanvas().translate(0, -webMarginTop); webView.draw(page.getCanvas; document.finishPage; } document.writeTo(getOutputStream; document.close();

// 结束当前页

View the Video

onActivityResult()中Android相机应用将会返回视频的Uri.

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
    if (requestCode == REQUEST_VIDEO_CAPTURE && resultCode == RESULT_OK) {
        Uri videoUri = intent.getData();
        mVideoView.setVideoURI(videoUri);
    }    
}

查看openoffice运行的情况

  1. PrintDocumentAdapter这是一个自定义打印文档的基础适配器类,而且是一个抽象类,需要我们自己去实现具体的打印过程。这个类主要是在打印自定义文档时配合PrintManager类使用。其内部生命周期主要有以下方法:onStart():开始打印时调用,注意这个方法需是在主线程调用的onLayout():在打印设置改变时调用,即当PrintAttributes发生变化时会调用这个方法以重新布局来适应新的打印参数onWrite():在将内容写入PDF文件时调用,这个方法同样是在主线程调用的onFinish():打印结束时调用通俗地讲,你要打印一个文件,扔过来一个View,你总得告诉别人怎么具体打印对吧,这就是这个适配器干的事。
  2. PrintManager不多BB了,直接上代码:

document.finishPage(page);

Controlling the Camera

美高梅网投平台 5

...

Open the Camera Object

  1. 获取Camera对象的一个实例,推荐在onCreate()时创建.
  2. onResume()方法中打开camera

如果在调用Camera.open()时相机正在被使用,会抛出一个异常.

Private boolean safeCameraOpen(int id) {
    boolean qOpened = false;

    try {
        releaseCameraAndPreview();
        mCamera = Camera.open(id);
        qOpened = (mCamera != null);
    } catch (Exception e) {
        Log.e(getString(R.string.app_name), "failed to open Camera");
        e.printStackTrace();
    }
    return qOpened;
}

private void releaseCameraAndPreview() {
    mPreview.setCamera(null);
    if (mCamera != null) {
        mCamera.release();
        mCamera = null;
    }
}

在API 9 及以上,camera framework 开始支持多个相机. 如果你调用open()不携带任何参数,你将会获取到后置摄像头的Camera对象.

kill掉2310进程id的程序

 PrintManager printManager = (PrintManager) getSystemService(Context.PRINT_SERVICE); // Get a print adapter instance // MyCreatePrintDocumentAdapter为继承PrintDocumentAdapter抽象类的子类 PrintDocumentAdapter printAdapter = new MyCreatePrintDocumentAdapter(); // Create a print job with name and adapter instance String jobName = context.getString(com.sumxiang.noteapp.R.string.app_name)   " Document"; PrintAttributes attributes = new PrintAttributes.Builder() .setMediaSize(PrintAttributes.MediaSize.ISO_A4) .setResolution(new PrintAttributes.Resolution("id", Context.PRINT_SERVICE, 300, 300)) .setColorMode(PrintAttributes.COLOR_MODE_COLOR) .setMinMargins(PrintAttributes.Margins.NO_MARGINS) .build(); // 这里第三个参数可以传自定义attributes,也可以直接传null,此时将使用默认配置。 printManager.print(jobName, printAdapter, attributes);

// 此处在还未调用writeTo()函数之前,可以继续添加页面步骤跟上述PageInfo开始的一样

Create the Camera Preview

使用SurfaceView显示相机传感器检测到的图像.

Java中将word生成缩略图  

上面的代码就是一个调用系统打印自定义文档的过程。当执行printManager.print()方法后就会调用系统打印界面,而打印的具体策略和方式由传递的attributes设置和我们具体实现printAdapter内部方法决定。

...

Preview Class

实现android.view.SurfaceHolder.Callback 接口—— 接收相机硬件的图片数据到app上.

class Preview extends ViewGroup implements SurfaceHolder.Callback {

    SurfaceView mSurfaceView;
    SufaceHolder mHolder;

    Preview(Context context) {
        super(context);

        mSurfaceView = new SurfaceView(context);
        addView(mSurfaceView);

        mHolder = mSurfaceView.getHolder();
        mHolder.addCallback(this);
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }
}

//将PDF文本对象写到一个输出流中,getOutputStream()可以是FileOutputStream

Set and Start the Preview

Camera实例和其相关的界面展示必须有确切的创建时序,先创建Camera实例.

在下面的代码段中,初始化相机的过程被封装. 任何时间用户做了一些改变相机状态(setCamera())的操作,Camera.startPreview()都会被调用. 预览也应该在surfaceChanged()中重启.

public void setCamera(Camera camera) {
    if (mCamera == camera) { return; }

    stopPreviewAndFreeCamera();

    mCamera = camera;

    if (mCamera != null) {
        mCamera.setPreviewDisplay(mHolder);    
    } catch (IOException e) {
        e.printStackTrace();    
    }

    mCamera.startPreview();
}

Java jacob 将Word 生成PDF文件

  1. 低配版方案首先我实现了一个低配版方案,即调用自定义打印服务,然后手动选择打印设置、打印并保存。使用自定义打印方式的原因很明显,因为Webview中的元素实在是过于复杂,如果将其当做普通的View采用PdfDocument来自行实现打印过程对我来说是不现实的。更重要的是,自定义打印过程中最关键的PrintDocumentAdapter,webview是已经帮我们实现了的,无需我们再自己实现。废话不多说,贴上代码:

document.writeTo(getOutputStream());

Modify Camera Setting

Camera Settings 更改相机拍摄照片的方式,从缩放级别到曝光补偿. 此示例仅更改预览大小.

public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
    Camera.Parameters parameters = mCamera.getParameters();
    parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
    requestLayout();
    mCamera.setParameters(parameters);

    mCamera.startPreview();
}

// close the document

Set the Preview Orientation

大多数相机应用将显示锁定为横向模式,因为这是相机传感器的自然方向. 此设置不会阻止你拍摄人像模式的照片,因为设备的方向记录在EXIF标头中.setCameraDisplayOrientation()允许你修改预览的显示方式,而不影响图像的记录方式. 但是在API 14以下,如果要改变方向,必须先停止预览,然后再更改方向,再重新启动.

实例——使用PDFRenderer将pdf转化成图片

 PrintManager printManager = (PrintManager) context.getSystemService(Context.PRINT_SERVICE); // Get a print adapter instance PrintDocumentAdapter printAdapter = webView.createPrintDocumentAdapter(); // Create a print job with name and adapter instance String jobName = context.getString(com.sumxiang.noteapp.R.string.app_name)   " Document"; PrintAttributes attributes = new PrintAttributes.Builder() .setMediaSize(PrintAttributes.MediaSize.ISO_A4) .setResolution(new PrintAttributes.Resolution("id", Context.PRINT_SERVICE, 200, 200)) .setColorMode(PrintAttributes.COLOR_MODE_COLOR) .setMinMargins(PrintAttributes.Margins.NO_MARGINS) .build(); printManager.print(jobName, printAdapter, attributes);

document.close();

Take a Picture

调用Camera.takePicture()拍摄照片在preview已经开始的前提下. 你可以创建Camera.PictureCallbackCamera.ShutterCallback,并将他们传递给Camera.takePicture().

如果你想延迟拍照,你可以创建Camera.PreviewCallback,实现onPreviewFrame(). 对于捕捉到的镜头数据,你可以仅捕获所选的预览帧,或设置一个延迟操作来调用takePicture().

基本上就是把自定义打印那一段照搬了过来,只是这里适配器是调webView的createPrintDocumentAdapter()方法拿到的。实现到这里可用性其实已经很高了,而打印出来的效果还不错,拿去和公司iOS版本的比较了一下,排版基本上没有差异,连图片换页都帮我们处理好了。

注:其中getContentView()表示当前需要保存的视图,可以是Layout、Button等View对象

Restart the Preview

@Override
public void onClick(View v) {
    switch(mPreviewState) {
        case K_STATE_FROZEN:
            mCamera.startPreview();
            mPreviewState = K_STATE_PREVIEW;
            break;

        default:
            mCamera.takePicture(null, rawCallback, null);
            mPreviewState = K_STATE_BUSY;
    }
    shutterBtnConfig();
}

Java利用OpenOffice将word等office文档转换成PDF(附件在最后)

  1. 改进交互时遇到的问题当然如果只是止步于这种程度,那前面的一大堆东西就白写了,大家也白看了。在公司里,这个玩意儿一掏出来用脚想也知道,产品肯定会说,哎。那个sei,我们能不能省掉用户自己选择打印这个过程。给他一个按钮,他一按,duang~,PDF就蹦出来了。这么一duang,我们的第一反应就是去看看能不能自己调用开始打印自定义文档的方法,给个路径不就完了嘛。不过很可惜,官方似乎并不支持这种做法,没有提供相应的函数。PrintManager只提供了getPrintJobs两个方法。所以我们转而来研究PrintDocumentAdapter ,如果能自行调用PrintDocumentAdapter 相应的声明周期方法不就完事了嘛。所以就有了下面的尝试:

本人在使用的时候遇到了一个问题,就是当这个View是SurfaceView的时候,保存的内容是空白的,因此特地的查了下SurfaceView的源码。因为SurfaceView绘图不是在UI主线程上绘制的,而是通过getHolder(),再通过lockCanvas()取得画布后再绘图,绘制完毕还需要调用unlockCanvasAndPost()解锁并刷新画布。而原本的View中的方法则被处理掉了。

Stop the Preview and Release the Camera

一旦你的app不再使用相机,就应该将其处理掉. 尤其需要释放掉Camera对象,否则将可能造成其他app的crash,当然包括你自己的app.

你什么时候需要停止预览释放相机资源?当你的预览窗口被摧毁的时候.

public void surfaceDestroyed(SurfaceHolder holder) {
    if (mCamera != null) {
        mCamera.stopPreview();
    }    
}

private void stopPreviewAndFreeCamera() {
    if (mCamera != null) {
        mCamera.stopPreview();
        mCamera.release();

        mCamera = null;
    }    
}

美高梅网投平台 6

Printing Content

Android 4.4 以上提供了直接显示图片和文件的框架.

java/poi读取word,并替换word中的文本内容,向word中插入图片的操作

 printAdapter = webView.createPrintDocumentAdapter(); printAdapter.onStart(); printAdapter.onLayout(attributes, attributes, new CancellationSignal(), new PrintDocumentAdapter.LayoutResultCallback() { @Override public void onLayoutFinished(PrintDocumentInfo info, boolean changed) { super.onLayoutFinished(info, changed); } @Override public void onLayoutFailed(CharSequence error) { super.onLayoutFailed; } @Override public void onLayoutCancelled() { super.onLayoutCancelled(); } },new Bundle;

于是只能换种方式了,下面附上代码,思路还是与官方的一致,但代码有区别:

Photos

使用PrintHelper类来显示一张图片.

PrintHelper: 来自Android v4 support library

写到这里就写不下去了。第一,PrintDocumentAdapter.LayoutResultCallback按照我们的逻辑应该是由Webview提供的适配器实现的,而PrintDocumentAdapter又是一个抽象类,webview的具体实现子类里面长啥样我们并不知道。第二,PrintDocumentAdapter.LayoutResultCallback同样也是个抽象类,所以上面的代码编译都别想过。到这里就懵逼了。于是乎,又开始满大街的翻帖子。不过翻过去翻过来,就像我初中化学老师经常说的一句话,你们班的作业就几个版本。要么起手式就是iText,要么就是上面打印服务的那一段代码。最后同样是在stackoverflow的这篇回答下找到了答案,答案是最后的那条0支持的回答,而非第一条,核心就是用DexMaker黑科技来解决。

//先判断系统的版本信息

Print an Image

PrintHelper提供一种简单的方式来显示图片. 该类只有一个显示设置:setScaleMode(),有如下两种选择:

  • SCALE_MODE_FIT: 按照图片的大小平铺在界面上
  • SCALE_MODE_FILL: 默认值. 按照屏幕大小平铺整个图片.

以下代码示例怎样显示图片的全过程.

private void doPhotoPrint() {
    PrintHelper photoPrinter = new PrintHelper(getActivity());
    photoPrinter.setScaleMode(PrintHelper.SCALE_MODE_FIT);
    Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.droids);
    photoPrint.printBitmap("droids.jpg - test print", bitmap);
}

[简单]poi导出简单word2007带页眉页脚

if(android.os.Build.VERSION.SDK_INT>= android.os.Build.VERSION_CODES.KITKAT) {

HTML Documents

在Android 4.4以上,WebView类做了更新,支持显示HTML内容. 该类允许你加载本地HTML或者从web端下载一个页面并显示.

github地址:

File sdcarddir = android.os.Environment.getExternalStorageDirectory();

Load an HTML Document

创建本地输出视图的主要步骤如下:

  1. 在HTML资源家在以后创建一个WebViewClient对象
  2. 使用WebView对象加载HTML资源

示例.

private WebView mWebView;

private void doWebViewPrint() {
    WebView webView = new WebView(getActivity());
    webView.setWebViewContent(new WebViewClient() {
        public boolean shouldOverrideUriLoading(Webview webView, String url) {
            return false;    
        }    

        @Override
        public void onPageFinished(WebView view, String url) {
            Log.i(TAG, "page finished loading "   url);
            createWebPrintJob(view);
            mWebView = null;
        }
    });

    String htmlDocument = "<html><body><h1>Test Content</h1><p>Testing, testing, testing ...</p></body></html>";
    webView.loadDataWithBaseURL(null, htmlDocument, "text/HTML", "utf-8", null);
    mWebView = webView;
}

Note 一定要确认你在WebViewClient.onPageFinished()方法被调用以后再做资源输出的工作.
Note美高梅网投平台, 以上的示例代码保存了对一个WebView对象实例的引用,所以在进行资源输出之前不会对其进行垃圾回收. 你需要确保在自己的实现中也要使用同样的思路来执行代码,否则打印过程可能会失败.

如果要在页面中包含图形,请将图形文件放在项目的assets/目录中,并在loadDataWithBaseURL()方法的第一个参数中指定基本URL.

webView.loadDataWithBaseURL("file://android_asset/images", htmlBody,
        "text/HTML", "utf-8", null);

你也可以通过调用loadUrl()加载网页.

webView.loadUrl("http://developer.android.com/about/index.html");

当使用WebView来创建输出文件时,有如下几条注意事项:

  • 不能添加头惑尾,包括页码等.
  • HTML 文档的打印选项不包括打印页面范围的功能,例如:不支持打印10页HTML文档的第2页到第4页
  • 一个WebView一次只能支持一个输出工作
  • 不支持包含CSS打印属性的HTML文档
  • 不能在HTML中使用 JavaScript

Note 包含在布局中的WebView对象的内容也可以在加载文档后打印.

java itext生成word

FileOutputStream outputStream =null;

Create a Print Job

做完以上事情以后,终于要做最后一步工作了:访问PrintManager,创建打印适配器,最后创建打印作业.

private void createWebPrintJob(WebView webView) {
    PrintManager printManager = (PrintManager) getActivity()
            .getSystemService(Context.PRINT_SERVICE);

            PrintDocumentAdapter printAdapter = webView.createPrintDocumentAdapter();

            String jobName = getString(R.string.app_name)   "Document";
            PrintJob printJob = printManager.print(jobName, printAdapter,
                    new PrintAttributes.Builder().build());

            mPrintJobs.add(printJob);
}   

通过之前自定义打印文档的过程,我们可以推断:在弹出系统打印界面并且我们确定打印之后,后续的打印操作即是系统控制调用了PrintDocumentAdapter的生命周期方法完成了打印任务。所以当前我们的重点就是获取到系统内部实现的继承于PrintDocumentAdapter.LayoutResultCallback的实例。通过上文提到的stackoverflow的解决思路,这里用到了ProxyBuilder这么一个类,通过官方文档的描述可以知道这个类可以为我们创建一个动态生成的代理类来替代真正的实体类。所以,我们就先来生成一个PrintDocumentAdapter.LayoutResultCallback的动态代理类:

try{

Custom Documents

[简单]poi操作word 2007 常用方法总结

public static PrintDocumentAdapter.LayoutResultCallback getLayoutResultCallback(InvocationHandler invocationHandler, File dexCacheDir) throws IOException { return ProxyBuilder.forClass(PrintDocumentAdapter.LayoutResultCallback.class) .dexCache(dexCacheDir) .handler(invocationHandler) .build(); }

outputStream =newFileOutputStream(newFile(sdcarddir.getPath() "/123.pdf"));

Connect to the Print Manager

当应用程序直接管理打印过程时,从用户收到打印请求后的第一步是连接到Android打印框架并获取PrintManager类的实例. 以下代码示例显示如何获取打印管理器并开始打印过程.

private void doPrint() {
    PrintManager printManager = (PrintManager) getActivity().getSystemService(Context.PRINT_SERVICE);
    String jobName = getActivity().getString(R.string.app_name)   " Document";
    printManager.print(jobName, new MyPrintDocumentAdapter(getActivity(), null));
}

Note print() 最后一个参数需要是PrintAttribute对象. 你可以使用此参数向打印框架提供提示,并根据先前的打印周期提供预设选项,从而改善用户体验. 你还可以使用此参数设置更适合正在打印的内容的选项,例如在打印处于该方向的照片时将方向设置为横向.

dexCacheDir参数是动态编码保存的路径,因为DexMaker的原理就是在程序运行的时候才去生成需要编译的文件,所以得指定一个路径。invocationHandler的作用是监听内部方法的调用,这里的作用自然就是监听PrintDocumentAdapter内部的生命周期方法的调用情况到这里,核心问题就解决了,下面再来梳理一下打印的具体逻辑:

PdfDocument document =newPdfDocument();

Create a Print Adapter

输出适配器与Android输出框架交互,处理输出流程. 该过程需要用户选择打印机和打印选项. 在打印过程中,用户可以选择取消打印行为,所以你的打印适配器需要监听和处理取消请求。

PrintDocumentAdapter 抽象类有4个主要的回调函数,被用来设计处理打印生命周期.

  • onStart(): 一次调用. 如果你的应用有任何一次性的准备工作,都可以写在这里. 不是必须实现的方法.
  • onLayout(): 任何时间用户的行为影响了输出都会被调用.
  • onWrite(): 调用将打印的页面转换为要打印的文件. 在每次onLayout()被调用后都必须必须被调用至少一次.
  • onFinish(): 在打印任务结束时被调用一次.

Note 这些适配器的方法在主线程中被调用. 如果你期望在执行这些方法时消耗大量的时间,需要在非UI线程执行实现函数.

[简单] poi word2007简单图文混排

  1. 首先,我们获取到webview的PrintDocumentAdapter实现子类,然后手动触发其onStart()方法,之后执行onLayout()完成布局,这里利用DexMaker的机制传递一个代理的回调参数。
  2. 在PrintDocumentAdapter.LayoutResultCallback的代理类中,我们通过监听LayoutResultCallback内部方法的调用情况来判断打印任务的状态。LayoutResultCallback有三个抽象方法:onLayoutFinished():表示打印完成onLayoutFailed():表示打印失败onLayoutCancelled():表示打印取消
  3. 根据不同的方法调用情况,我们就可以得到具体的打印结果然后做进一步操作了。其中,在监听到打印完成后,我们就可以调用PrintAdapter.onWrite()方法写入本地保存了,这里同样需要我们传递一个PrintDocumentAdapter.WriteResultCallback的实现子类对写入过程和结果进行监听,方式和上面一样我们可以利用DexMaker来完成到这里整个打印和写入本地的逻辑就完成了,这里贴一下整个过程的代码:

for(inti=1;i<=20;i ) {   //循坏表示生成20页PDF

Compute print document info

在实现PrintDocumentAdapter的过程中,app必须能够在打印作业中确认文件类型以及需要打印的页数,和打印页面的大小. 实现onLayout(),计算打印作业相关信息,将期望参数通过PrintDocumentInfo类传输.

@Override
public void onLayout(PrintAttributes oldAttributes,
                    PrintAttributes newAttributes,
                    CancellationSignal cancellationSignal,
                    Bundle metadata) {
    mPdfDocument = new PrintedPdfDocument(getActivity(), newAttributes);
    if (cancellationSignal.isCancelled()) {
        callback.onLayoutCancelled();
        return;
    }

    int pages = computPageCount(newAttributes);
    if (pages > 0) {
        PrintDocumentInfo info = new PrintDocumentInfo()
                    .Builder("print_output.pdf")
                    .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
                    .setPageCount(pages)
                    .build();
        callback.onLayoutFinished(info, true);
    } else {
        callback.onLayoutFailed("Page count calculation failed.");
    }
}

执行onLayout()方法会有三种可能的执行结果:完成、取消或者失败. 你必须通过实现PrintDocumentAdapter.LayoutResultCallback 来处理执行结果.

Note onLayoutFinished() 方法的布尔参数指示自上次请求以来布局内容是否实际已更改. 正确设置此参数允许打印框架避免不必要地调用onWrite()方法,缓存先前写入的打印文档并提高性能.

以下的代码示例展示如何根据打印方向计算页面页数:

private int computePageCount(PrintAttributes printAttributes) {
    int itemsPerPage = 4;

    MediaSize pageSize = printAttributes.getMediaSize();
    if (!pageSize.isPortrait()) {
        itemsPerPage = 6;    
    }

    int printItemCount = getPrintItemCount();

    return (int) Math.ceil(printItemCount / itemsPerPage);
}

PdfDocument.PageInfo pageInfo =newPdfDocument.PageInfo.Builder(

Write a print document file

PrintDocumentAdapter.onWrite() 在要进行打印作业时调用.
onWrite(PageRange[] pageRanges, ParcelFileDescriptor destination, CancellationSignal cancellationSignal, WriteResultCallback callback)

当打印任务完成时,需要调用callback.onWriteFinished().

Note 每次onLayout()被调用以后,onWrite()都会被调用至少一次. 因此如果输出内容没有改变的话,需要设置onLayoutFinished()false,以避免不必要地重新打印.

Note onLayoutFinished()的boolean型参数指示输出内容在上次调用时有没有改变.

@Override
public void onWrite(final PageRange[] pageRanges,
                    final ParcelFileDescriptor destination,
                    final CancellationSignal cancellationSignal,
                    final WriteResultCallback callback) {
    for (int i = 0; i < totalPages; i   ) {
        if (containsPage(pageRanges, i)) {
            writtenPagesArray.append(writtenPagesArray.size(), i);
            PdfDocument.Page page = mPdfDocument.startPage(i);

            if (cancellationSignal.isCancelled()) {
                callback.onWriteCancelled();
                mPdfDocument.close();
                mPdfDocument = null;
                return;
            }

            drawPage(page);

            mPdfDocument.finishPage(page);
        }    
    }

    try {
        mPdfDocument.writeTo(new FileOutputStream(
                destination.getFileDescriptor()));
    } catch (IOException e) {
        callback.onWriteFailed(e.toString());
        return;
    } finally {
        mPdfDocument.close();
        mPdfDocument = null;
    }

    PageRange[] writtenPages = computeWrittenPages();
    callback.onWriteFinished(writtenPages);
    ...
}

PrintDocumentAdapter.WriteResultCallback 监听onWrite() 结果.

OpenOffice实现Office转Pdf支持自定义添加水印、页眉、页脚

 private void printPDFFile() { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { /** * android 5.0之后,出于对动态注入字节码安全性德考虑,已经不允许随意指定字节码的保存路径了,需要放在应用自己的包名文件夹下。 */ //创建DexMaker缓存目录 //File dexCacheFile = new File(dexCacheDirPath); //if (!dexCacheFile.exists { //file.mkdir(); //} //新的创建DexMaker缓存目录的方式,直接通过context获取路径 File dexCacheFile = context.getDir; if (!dexCacheFile.exists { dexCacheFile.mkdir(); } try { //创建待写入的PDF文件,pdfFilePath为自行指定的PDF文件路径 File pdfFile = new File(pdfFilePath); if (pdfFile.exists { pdfFile.delete(); } pdfFile.createNewFile(); descriptor = ParcelFileDescriptor.open(pdfFile, ParcelFileDescriptor.MODE_READ_WRITE); // 设置打印参数 PrintAttributes attributes = new PrintAttributes.Builder() .setMediaSize(PrintAttributes.MediaSize.ISO_A4) .setResolution(new PrintAttributes.Resolution("id", Context.PRINT_SERVICE, 300, 300)) .setColorMode(PrintAttributes.COLOR_MODE_COLOR) .setMinMargins(PrintAttributes.Margins.NO_MARGINS) .build(); // 计算webview打印需要的页数 int numberOfPages = (webviewHeight / pageHeight)   1; ranges = new PageRange[]{new PageRange(1, numberOfPages)}; // 获取需要打印的webview适配器 printAdapter = webView.createPrintDocumentAdapter(); // 开始打印 printAdapter.onStart(); printAdapter.onLayout(attributes, attributes, new CancellationSignal(), getLayoutResultCallback(new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.getName().equals("onLayoutFinished")) { // 监听到内部调用了onLayoutFinished()方法,即打印成功 onLayoutSuccess(); } else { // 监听到打印失败或者取消了打印 do something... } return null; } }, dexCacheFile.getAbsoluteFile, new Bundle; } catch (IOException e) { e.printStackTrace(); } } } private void onLayoutSuccess() throws IOException { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { PrintDocumentAdapter.WriteResultCallback callback = getWriteResultCallback(new InvocationHandler() { @Override public Object invoke(Object o, Method method, Object[] objects) throws Throwable { if (method.getName().equals("onWriteFinished")) { // PDF文件写入本地完成,导出成功 do something for succeed... } else { // 导出失败 do something for extract failed... } return null; } }, dexCacheFile.getAbsoluteFile; printAdapter.onWrite(ranges, descriptor, new CancellationSignal(), callback); } } public static PrintDocumentAdapter.LayoutResultCallback getLayoutResultCallback(InvocationHandler invocationHandler, File dexCacheDir) throws IOException { return ProxyBuilder.forClass(PrintDocumentAdapter.LayoutResultCallback.class) .dexCache(dexCacheDir) .handler(invocationHandler) .build(); } public static PrintDocumentAdapter.WriteResultCallback getWriteResultCallback(InvocationHandler invocationHandler, File dexCacheDir) throws IOException { return ProxyBuilder.forClass(PrintDocumentAdapter.WriteResultCallback.class) .dexCache(dexCacheDir) .handler(invocationHandler) .build(); }

Constants.displayWidth, Constants.displayHeight, i).create();

Drawing PDF Page Content

你的app在打印时必须生成一个PDF文件,并将其传输给Android打印框架. 你可以使用PrintedPdfDocument来收入能够承认那个PDF文件.

PrintedPdfDocument使用Canvas绘出PDF页面.

private void drawPage(PdfDocument.Page page) {
    Canvas canvas = page.getCanvas();

    int titleBaseLine = 72;
    int leftMargin = 54;

    Paint paint = new Paint();
    paint.setColor(Color.BLACK);
    paint.setTextSize(36);
    canvas.drawText("Test Title", leftMargin, titleBaseLine, paint);

    paint.setTextSize(11);
    canvas.drawText("Test paragragh", leftMargin, titleBaseLine   25, paint);

    paint.setColor(Color.BLUE);
    canvas.drawRect(100, 100, 172, 172, paint);
}

代码中部分基础的变量并未做出声明,需要自行补充。

PdfDocument.Page page = document.startPage(pageInfo);   

OpenOffice实现Office转Pdf(支持自定义添加水印、页眉、页脚)

//此处是与官方的不同之处,官方的view.draw(page.getCanvas()) 无非也是在page返回的canvas对象上绘图,那么我们可以获取SurfaceView对象的Bitmap后再绘制到page的//canvas上即可          

在PDF导出功能的后续测试中,发现了android4.4版本生成的PDF文件异常巨大的问题,在android5.0以上版本中,同样的数据生成的文件大概在600KB左右,而4.4居然达到了17MB。即使是纯文本,差距也在10倍左右,去stackoverflow上又逛了一圈,最后发现android4.4版本的PDF打印是没有经过压缩的。我重新调用系统的打印页面,生成的文件同样大的吓死人。不过这也仅限于android4.4版本,目前也没找到什么有效的解决方案。如果大家有什么好的解决办法欢迎留言交流。

page.getCanvas().drawBitmap(this.mSurfaceView.getDrawingBitmap(),0,0,newPaint());

linux下word模板操作及PDF处理笔记

document.finishPage(page);

这篇文章基本上算是自己扒了各种帖子、文章、问答之后根据自己爬坑之路写的,主要在于分享自己的实现过程和方法,并没有对源码进行深入探究。自己水平有限,对于一些概念的理解肯能会有偏差,欢迎大家指出文中的错误,也欢迎留言交流。

}

关于POI提取word中文本,除掉页眉页脚

  • 1.原文中创建动态字节码缓存目录处:

     //创建DexMaker缓存目录 File dexCacheFile = new File(dexCacheDirPath); if (!dexCacheFile.exists { file.mkdir(); }
    

document.writeTo(outputStream);

在后续的测试过程中,测试反应了部分手机无法生成PDF的情况,发现是android在高版本中已经对动态注入字节码这种编译方式做出了限制。想想也是,可以随意指定动态字节码的路径意味着可以完全无压力的动态注入修改,对应用来说是非常不安全的,所以现在DexMaker的缓存目录推荐通过context.getDir方法获取,原来的方法在高版本中应该会抛出异常。现修改为:

document.close();

使用POI读写Word doc文件

 //新的创建DexMaker缓存目录的方式,直接通过context获取路径 File dexCacheFile = context.getDir; if (!dexCacheFile.exists { dexCacheFile.mkdir(); }

}catch(FileNotFoundException e) {

2.原文中创建pdf文件缓存目录

e.printStackTrace();

java对文档等、附件在线预览的详细开发步骤解析:openOffice SwfTools FlexPaper

 // 创建pdf文件缓存目录 cacheFolder = new File(pdfFileDirPath); if (!cacheFolder.exists { cacheFolder.mkdir(); }

}catch(IOException e) {

这里由于当时疏忽,写错了,这段代码并没有什么吊用。。。pdfFileDirPath也是不存在的。cacheFolder和dexCacheFile实际上是一个东西,新的代码把cacheFolder改为dexCacheFile就可以了。

e.printStackTrace();

借用OpenOffice将上传的Word文档转换成Html格式

  1. dexCacheFile在回调中使用时需要用dexCacheFile.getAbsoluteFile()来获取绝对路径。上述问题在之前修改了之后因为时间关系并没有第一时间在文章中更新,后面有读者私信反馈这个问题,所以就一并修改了,同时对部分读者造成了误导表示抱歉。在此感谢提出问题的读者,同时欢迎大家能继续指正文中的不足。

}

}

jodconverter

使用过程中还发现了一些缺点,即当视图的内容比较丰富的时候,存储的速度会较慢,因此建议另外建一个线程,避免阻塞UI主线程。另Android在21版本后添加了PdfRenderer类,此类可以用于查阅PDF文件内容,大家有空的不妨试试。

doc/jpg等文件在线浏览解决方案搭建

Ubuntu Server(64位)安装Openoffice4,SWFTools完成

Linux openoffice 安装测试

CentOS 6.6下安装OpenOffice4.0

文件转换工具office to swf

openOffice conversion failed: could not load input document

openOffice pdf2swf类百度文库):pdf转swf(Linux,window)

[JODConverter]word转pdf心得分享(转)

阿里云安装OpenOffice转换pdf文档乱码解决方式

仿百度文库解决方案(二)——利用Jacob调用MS Office转换文档为PDF

Java跨平台将word转为pdf(结合Jodconverter 和OpenOffice.org)

利用jodconverter(基于OpenOffice服务)将word文件(*.doc)转化为html格式

CentOS6.6安装OpenOffice4.0

http://www.dianju.cn/demos/demos

对点聚weboffice插件的使用说明

Java操作PDF之iText超入门

OpenDocument文档转换器 JODConverter

对象存储 OSS>SDK手册>Java-SDK

Poi之Word文档结构介绍

java生成word文档【一】

Apache POI使用详解

Java使用iText生成word文件的完美解决方案(亲测可行)

利用IText导出Word

itext写word并实现下载

iText生成word代码及jar包(含页眉页脚)

IText导出word之页眉页脚页码

使用Aspose.words for java去掉Word文档的水印(底图)

用JAVA移除Word文档的底图(水印)

POI 替换word2003 页眉页脚中的内容

Java使用iText生成word文 表格、图片、表格里插图片、页眉、页脚、图片页脚、这一次更全面

POI用addPicture插入图片到word里面无法显示

利用POI操作不同版本word文档中的图片以及创建word文档,poi不同版本

如何把图片放入到页面的合适位置

使用POI如何把图片插入到Word文件的第一行。。。

如何在已经有内容的word里面通过java程序加入页眉页脚

美高梅网投平台 7

。。

TAG标签:
版权声明:本文由美高梅网投平台发布于新闻中心,转载请注明出处:调用Android的PDF接口导出PDF文件,Webview生成和导出