PDFフラッシュからJavaを受け取った方法について

画像

むかしむかし、草がより緑になったとき、 私は長い間捕らえられ、拷問されました



建築家がタスクを理解したように



与えられた:それぞれ数千ページの多数のPDFの形式で製品のクレイジーなカタログがあります。 カラフルなアニメーションプレゼンテーションの形式でWeb上で発行する必要があります。



解決しようとしています。彼らは、この変換されたディレクトリにフィードするフラッシュとjavascriptでプレーヤーを作成し、特定のアルゴリズムでさまざまなカラフルな効果を使って広告をひねりました。



問題:ディレクトリは絶えず変化しており、ディレクトリから元帳を1つだけ変換するのに1時間以上かかります(!)。



改善する理由と方法







私たちの前でどのように行われますか



画像

質問は理にかなっています-なぜそんなに難しいのですか? そのため、デスクトップに加えて、携帯電話は必須であり、200台のデバイス用のラスターを生成し、すべてをテストすることは私たちの方法ではありません。 それから-そしてズームで何をすべきか?



したがって、デスクトップの場合-古代のブラウザのフラッシュ(企業IEに乾杯)、およびその他すべてのHTML5。 ベクター画像(プレビュー、または11個のSVGシェイクを除いて、タブレットはまだ弱くマスターされています)。



支援を急いで(常に)並べ替えを開きます。



そこで行われていることを分析します。 何がわかる





500ページのテストPDFですべてを順番に開始します。 稼働時間1時間2分



お祖母さん、そして聖ジョージの日です!



誰のせいですか?



画像

明らかに、PDFを連続して4回解析することは最良の選択ではありません。



ImageMagickが明らかに選択ではないことも明らかであり、時間の3/4は変換ユーティリティによって費やされました。



その瞬間、蒸気シールド付きのアイロンをかけた顧客が私の部屋を覗き込んで、「あなたの知恵を緊急に必要としています!」と言いました。 ゲームに入ります。



一般的な方向性は条件付きで真であると認識しています-どこに行けば、リリースはいつものように「昨日」ですが、プレーヤーに関する質問はありません。 しかし、迷惑メールの選択に失敗したと見なし、Javaに注目します。



新しいキャラクター



画像

次の紳士のセットを取ります。







結合を開始します。



まず、 マーカスとボリスについて覚え 、始めてください:



public class PdfConv { public int startConversion(String pdfFile) { ... } public int getPages() { ... } public int nextPage(int pageNo, String outputFileName) { ... } public int endConversion() { ... } }
      
      





そして、それをすべて使用する方法を書きます。



 public static void main(String[] argv) throws Exception { for(int jjk =0; jjk <argv.length; ++jjk) { PdfConv conv = new PdfConv(); conv.startConversion(argv[jjk]); int k = conv.getPages(); for (int j = 0; j < k; ++j) { conv.nextPage(j, argv[jjk] + "_" + j + ".svg"); conv.nextPage(j, argv[jjk] + "_" + j + ".png"); conv.nextPage(j, argv[jjk] + "_" + j + ".swf"); ... } } }
      
      





小さなことは、必要なすべてのコンバーターを作成することです。



最初の熊手



画像

私たちが選択したライブラリは本当に賢いことがわかりました。 しかし、彼女にはいくつかの重大な欠陥があります。



  1. SMaskサポートがありません
  2. 勾配もありません
  3. JPEG2000の慢性的な誤解
  4. そこにある矢印などの形のフォントで奇妙な すべての栄光に


グーグルはフォントの問題を解決し、画像形式のクラスパスにJAIを追加します。



ファイル付きのSMaskをPDFRendererコードに追加する必要があります。 簡単です-パーサーに認識を追加し、コンテキストに保存するコマンドを追加し、Shapeの描画をマスク付きの絵の描画に変更します。 控えめだが、テキスト。



単にグラデーションを無視します-スライドに落ちる場所にはありません。 作物と他の処理、簡単にするために、私はそれを示しません。



最初のステージが渡されます-必要に応じて描画します。 APIを実装します(エラー処理を削除しました):



 private PDFFile pdf = null; private FileChannel fic = null; public int startConversion(String pdfFile) throws Exception { File fix = new File(pdfFile); FileInputStream fin = new FileInputStream(fix); fic = fin.getChannel(); MappedByteBuffer mbb = fic.map(FileChannel.MapMode.READ_ONLY, 0, fix.length()); pdf = new PDFFile(mbb); return pdf.getNumPages(); } public int getPages() throws Exception { return pdf.getNumPages(); } public int endConversion() throws Exception { if (fic != null) fic.close(); pdf = null; fic = null; return 1; } public int nextPage(int pageNo, String outputMask) { PDFPage page = pdf.getPage(pageNo + 1); if (page == null) return -1; Rectangle bounds = page.getBBox().getBounds(); DrawingCtx ctx = DrawingCtxBuilder.build( outputMask, new Dimension(bounds.x + bounds.width, bounds.y + bounds.height)); PDFRenderer rx = new PDFRenderer(page, ctx.getContext(), bounds, null, null); rx.go(); rx.waitForFinish(); ctx.saveTo(outputMask); return 1; }
      
      





変換自体を始めましょう。



抽象化を描いてみましょう-しかし、rosveltsを忘れないでください



画像

サブジェクト領域は、ボリスでさえ混乱しないようなものです。



 abstract class DrawingCtx { protected Graphics2D g2; protected Dimension size; DrawingCtx(Dimension size) { this.size = size; } public Graphics2D getGraphics() { return g2; } public abstract void saveTo(String fileName) throws Exception; } class DrawingCtxBuilder { public static build(String fileName, Dimension size) throws Exception { String type = fileName.substring(fileName.lastIndexOf('.') + 1).toUpperCase(); if(type.equals("SVG")) return new SvgDrawingCtx(size); else if(type.equals("PNG")) return new ImageDrawingCtx(size); else if(type.equals("SWF")) return new SwfDrawingCtx(size); ... throw new Exception(type + ": unknown converter requested"); } }
      
      





骸骨を肉で満たしてください-死体は準備ができています。



SVGとバティック



画像

ここでは、一般的に、すべてがすでに私たちの前に行われています。ApacheBatik SVGgenは急いで助けています。 問題はありませんでした。 グラデーションがある場合、はい、放射状のグラデーションはさようならを言うか、バティックにパッチを適用する必要があります。 もちろん、グラデーション自体をPDFRendererに追加した後です。



唯一の微妙な点は、ヒントを正しく作成して、アンチエイリアスを忘れないようにすることです。また、写真は断片化されません。



 class SvgDrawingCtx extends DrawingCtx { private DOMImplementation domImpl; private Document doc; private SVGGraphics2D svgGenerator; SvgDrawingCtx(Dimension size) { super(size); domImpl = SVG12DOMImplementation.getDOMImplementation(); doc = domImpl.createDocument(SVGConstants.SVG_NAMESPACE_URI, SVGConstants.SVG_SVG_TAG, null); svgGenerator = new SVGGraphics2D(doc); svgGenerator.getGeneratorContext().setPrecision(4); svgGenerator.getGeneratorContext().setEmbeddedFontsOn(true); g2 = svgGenerator; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2.setRenderingHint( RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); g2.setRenderingHint( RenderingHintsKeyExt.KEY_AVOID_TILE_PAINTING, RenderingHintsKeyExt.VALUE_AVOID_TILE_PAINTING_ON); } public void saveTo(String fn) throws Exception { Element svgRoot = svgGenerator.getRoot(); OutputStream os = new FileOutputStream(fn); if (fn.endsWith(".svgz")) os = new GZIPOutputStream(os); svgGenerator.stream(svgRoot, new OutputStreamWriter(os), false /* CSS */, true /* escaped */); os.close(); } }
      
      







PNG:簡単ではありません



画像

ここではあまりにも汚いので、コードを提供します。



 class ImageDrawingCtx { private BufferedImage bf = null; ImageDrawingCtx(Dimension size) { super(size); bf = new BufferedImage(size.width, size.height, BufferedImage.TYPE_INT_ARGB); g2 = (Graphics2D) bf.getGraphics(); } public void saveTo(String fn) throws Exception { OutputStream os = new FileOutputStream(fn); ImageIO.write(bf, "PNG", os); os.close(); g2.dispose(); bf = null; } }
      
      







デザートに移りましょう-SWFを作成します



画像

コードも単純です(そして、ほとんどがFlex SDKの例から借用しています)。



 class SwfDrawingCtx extends DrawingCtx { SwfDrawingCtx(Dimension size) { super(size); g2 = new SpriteGraphics2D(size.width, size.height); } public void saveTo(String fn) throws Exception { OutputStream os = new FileOutputStream(fn); flash.swf.Frame frame1; Movie m = new Movie(); m.version = 7; m.bgcolor = new SetBackgroundColor(SwfUtils.colorToInt(255, 255, 255)); m.framerate = 12; frame1 = new flash.swf.Frame(); DefineSprite tag = ((MyG2D) g2).defineSprite("swf-test"); frame1.controlTags.add(new PlaceObject(tag, 0)); m.frames = new ArrayList(1); m.frames.add(frame1); TagEncoder tagEncoder = new TagEncoder(); MovieEncoder movieEncoder = new MovieEncoder(tagEncoder); movieEncoder.export(m); tagEncoder.writeTo(os); os.close(); g2.dispose(); g2 = null; } }
      
      





何も予見されず、突然。 SWFの一部の画像が表示されませんでした。



ホット追跡調査



画像

つまり、SVGにあり、PNGにありますが、SWFにはありません。



アドビソースの 光線で思考光線をたどると、思考に導かれ、コードを馬にしました。



 class MyG2D extends SpriteGraphics2D { public MyG2D(int width, int height) { super(width, height); } public MyG2D() { super(); } @Override public boolean drawImage(Image image, AffineTransform at, ImageObserver obs) { //    return super.drawImage(image, at, obs); } }
      
      







検死はそれを示した

 at.createTransformedShape(new Rectangle(0, 0, image.getWidth(), image.getHeight()).getBounds()
      
      



1x1の長方形を返します。



ユーレカ!



  @Override public boolean drawImage(Image image, AffineTransform at, ImageObserver obs) { AffineTransform good = getTransform(); good.concatenate(at); return super.drawImage(image, good, obs); }
      
      





問題を解決します。



まとめ



画像

テストを実行したところ、最初のバージョンはライブスレッドに縫い付けられており、 所要時間10分未満でした。 それはまだたくさんあり、考えるべきことがあります。



ただし、 kosher C ++からJavaへの移行により手順が6倍以上高速化され、新しいコンバーターをゼロから作成するには、数回のレーキと合計3日間の解決策が必要でした。



これで、連中は考えるべきことがあり、これらすべてのステップをC ++で繰り返します。 Javaが対処している間、彼らには時間があります。



コスト:40メガバイトのjarを私と一緒にドラッグする必要があり、このものをマシンに組み込みましたが、WebサービスはPHPでした。



All Articles