善良な若者が䞉頭蛇ず戊った方法、たたはAdobe InDesignドキュメントにSVGグラフィックスを埋め蟌む方法の話-パヌト1

すべおのhabrazhitelぞのご挚拶
最初に少し䜙談。 この投皿は私によっお曞かれたのではなく、 ただ原理に基づいおいない updによっお曞かれたした viklequickはすでに頑固で 、私の意芋では、すべおの結果であなたの泚意に倀したす。 だから...

原因

画像 凍えるような冬の季節になるず、 著者はEPS圢匏のベクタヌ画像ず戊う良い仲間にうんざりしたした 。 そしお、圌は進捗状況を把握し、SVGの圢匏でAdobe InDesignドキュメントにグラフずチャヌトを埋め蟌むこずにしたした。 アドビシステムズはAdobe Flashを奜んでおり、Adobe InDesignではSVGがサポヌトされおいないため、倧きな倱望が圌に降りかかりたした。 しかし、良い仲間はInDesignのプラグむンを䜜成する際に顕著な経隓を蓄積しおおり、圌は匷力な英雄を䜿甚しお3頭のヒドラを手に入れるこずにしたした。 戊士は蚀った-戊士がやった、぀たり-圌はそれを取った。
この闘争の詳现に぀いお、私たちは物語をリヌドしたす。

物語

ステップ1、たたは䞊腕䞉頭筋の解剖孊の研究

画像 開始するには、InDesignが写真でどのように機胜するかを簡単に説明する必芁がありたす。
InDesignの芳点から芋るず、画像は自分自身を描画しお印刷できる特別な皮類のPageItemです。 たた、このPageItemは、フレヌム内たたはテヌブルセル内にむンラむンでペヌゞに配眮でき、これらすべおを凊理する必芁がありたす。 それでも、3皮類のレンダリングがありたす-高速亀差した長方圢が描かれたす、最適ラスタヌプロキシむメヌゞ、完党䜎速です。 このすべおをサポヌトするこずも必芁です。
したがっお、次のようなImport Plug-in、Page Item、およびいく぀かのヘルパヌクラスがありたす。

 クラス
 {
   kSVGItem、
   kDisplayListPageItemBoss、
   {
     IID_ISHAPE、kSVGShapeImpl、
     IID_IINKRESOURCES、kAllProcessInkResourcesImpl、
     IID_IFLATTENERUSAGE、kSVGItemFlattenerUsageImpl、
     IID_IVISITORHELPER、kEPSItemVisitorHelperImpl、
     IID_ISCRIPT、kSVGItemScriptImpl、
   }
 }、


など

Class
{
kSVGPlaceProviderBoss,
kInvalidClass,
{
IID_IK2SERVICEPROVIDER, kImportServiceImpl,
IID_IIMPORTPROVIDER, kSVGPlaceProviderImpl,
IID_IIMPORTPREVIEW, kSVGImportPreviewImpl,
}
},


そしおもちろん、起動/シャットダりン甚の暙準ヘッダヌもありたすが、これは意味をなさないものです。
それが䜕で、なぜなのか芋おみたしょう。
•IID_ISHAPEは、レンダリングを凊理する実際のペヌゞアむテムの実装です。
•IID_IINKRESOURCES-倖郚ファむルむンクぞのリンクを凊理したす。
•IID_IFLATTENERUSAGE-アルファチャネルラスタラむズをPDFで凊理したす。 実際、これはフラットナヌを含めるこずを明確に芁求する1行であり、それ以䞊は䜕もありたせん。
•IID_IVISITORHELPER-EPSから借甚しお暙準を維持したす。
•IID_ISCRIPT-スクリプトを通じお芁玠のサポヌトを提䟛したす。 これも非垞に単玔な郚分であり、オブゞェクトのタむプを正しく指定するだけです。
•IID_IK2SERVICEPROVIDERおよびIID_IIMPORTPROVIDER-Placeコマンドのサポヌトを远加したす。
•IID_IIMPORTPREVIEW-ファむル遞択ダむアログでプレビュヌを提䟛したすWindows固有。
最も興味深いのは実際にはIID_ISHAPEです。これがプラグむンの䞭心です。 圌は、IGraphicsPortを䜿甚しお盎接描画する胜力ず、ラスタヌプロキシむメヌゞを取埗する胜力を必芁ずしたす。 将来を芋据えお、私は泚意したい-十分な驚きがあった。

ステップ2、たたはヒロむック゜ヌドを遞択

画像 ゚クスプレスグヌグル、およびOpenClipArtのサンプル画像を衚瀺するこずで、Apache Batikにずどたるこずを考えたした。 私はすぐにバティック自䜓を修正するためにいく぀かの努力をしなければならなかったず蚀わなければならないが、結局圌らは取るに足らないものであるこずが刀明した。 基本的には「コンテンツごずにSVGのバヌゞョンを掚枬する」に芁玄されたす。 ただし、「IGraphicsPortを䜿甚しお絵を描く方法」ずいうタスクはバティックに登堎し、最も難しいものずしお焊点を圓おたす。 そしお、退屈なC ++のものから楜しいJavaクリ゚むティブぞ、そしおその逆に移行したす。
しかし、恥ずかしいミンストレルは私たちに教えおくれたす-あなた、ヒヌロヌ、間違った物語にさたよいたした、そしおあなたはヘビずハリネズミを暪切るずきい぀も起こるように、あなたは5メヌトルの有刺鉄線で終わるでしょう。 困難な過ちの息子である経隓が私たちに教えおくれたす。この堎合、もっず圹に立぀ものが埗られたす。

ステップ3、たたは開始
前述のように、デザむンストリヌムの操䜜方法をJavaに教える必芁がありたす。 実際、タスクは明癜であり、JNIを介しお解決されたす。
画像
/**
* Class for reading from InDesign IPMStream
*/
public class PMInputStream extends InputStream
{
/**
* Creates PMInputStream object and attaches it to IPMStream* already
* opened by InDesign.
* @param iPMStreamPtr IPMStream* to attach to
* @throws IOException The stream has no ability to read.
*/
public PMInputStream(long iPMStreamPtr) throws IOException {
this.ownedStreamPtr = iPMStreamPtr;
this.retain();
}

@Override
public int read() throws IOException {
return readByte();
}

@Override
public int read(byte b[], int off, int len) throws IOException {
if (len == 0) return 0;
return readBytes(b, off, len);
}

@Override
public long skip(long n) throws IOException {
return performSeek(n, SeekFromCurrent);
}

public long seek(long numberOfBytes, int fromHere) throws IOException {
return performSeek(numberOfBytes, fromHere);
}

@Override
public int available() {
return availableBytes();
}

// native functions below
...


実装はネむティブ関数を介しお構築されたす。それらをリストしたす。 それらは自明であり、特別なコメントを必芁ずしたせん。 興味深い点が1぀だけありたす。ここにありたす

/**
* 'Cause IPMStream is a COM-like object we gotta call AddRef() for it
* if we wanna save pointer in our class.
*/
private native void retain();
/**
* Once we called AddRef() we shouldn't forget calling corresponding
* Release(). Let's do it!
*/
private native void release();


そしおリスト自䜓

protected native int readByte();
protected native int readBytes(byte b[], int off, int len);
protected native long performSeek(long numberOfBytes, int fromHere);
protected native int availableBytes();

protected native String getFileName();
protected native long getLastModifiedTime();


最埌の2぀の関数は、レンダリングごずにDOMモデルを再構築しないように、Javaで盎接芁玠をキャッシュするために远加されたした。 必芁なのは、デヌタの倉曎を远跡し、倉曎時にそれらをすばやく読み盎すこずだけです。
そしお最埌の仕䞊げは、ネむティブメ゜ッドの実際の実装です。

class IPMStream; class PMInputStream
{
private:
IPMStream* fStream;

public:
PMInputStream(IPMStream* stream);
~PMInputStream() { close(); }

void close();

int read() {
unsigned char result;
return read(&result, 1) == 1? result: -1;
}

int read(unsigned char* buffer, int len){ return fStream->XferByte(buffer, len); }
XInt64 seek(XInt64 numberOfBytes, SeekFrom fromHere) {
return fStream->Seek((int32)numberOfBytes, fromHere);
}

public:
enum SeekFrom { SeekFromStart = kSeekFromStart,
SeekFromCurrent = kSeekFromCurrent,
SeekFromEnd = kSeekFromEnd
};
};


すでにSVGに到達しおいたすが、今の仕事は絵を取埗しお描くこずです。 実際に、BufferedImageでベクタヌ画像を描画した埌、プロキシ画像を取埗したす。 バティックの暙準的な䟋は、それがどのように取埗されたかを明確に瀺しおいるため、スペヌスを節玄するために、このフットクロスをここに持ち蟌たないでください。 PixelGrabberを䜿甚しお、BufferedImageからRGBのバむト配列を取埗するこずも明らかです。 結果の配列を、蚘事の2番目のパヌトで説明したのず同じ方法でAGMImageRecordに枡したす。
興味深いこずに、BufferedImageではなくIGraphicsPortでのレンダリングは、暙準のオフスクリヌンGraphics2Dではなく、実装を眮き換える必芁がある1行だけ異なりたす。 これが私たちがやるこずです。

4番目のステップ、たたは暗黙のうちに電流を剣に接続しお火花が飛ぶようにする
Graphics2Dの有胜な継承のためのコヌドの量は倧きく、退屈ですが、明らかであり、同じApache Batikから簡単に芗くこずができたす。 そのため、最も興味深い郚分に焊点を圓おたすが、ここではPaint getPaint(){ return paint; } Paint getPaint(){ return paint; }省略したす。
䞀般に、この皮のプラグを安党に挿入できるこずに泚意しおください。

public void setXORMode(Color c1){ throw new RuntimeException("setXORMode: N/A"); }

など

public void fillRect(int x, int y, int w, int h) { fill(new Rectangle(x, y, w, h)); }

このような単玔化ず重耇の排陀の結果、次の実際の機胜が埗られたす。

public void draw(Shape s);
public void fill(Shape s);
public boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer);

public void drawString(String str, float x, float y);
public void drawString(AttributedCharacterIterator iterator, float x, float y);


StrokeTextPainterを䜿甚しおさらに進むこずができ、このセットは3぀の関数に削枛されたす。これは良いこずです。
次に、反察偎にアプロヌチしお、IGraphicsPortから必芁なものを芋おみたしょう。

private native void newpath();
private native void moveto(float x, float y);
private native void lineto(float x, float y);
private native void curveto(float x1, float y1, float x2, float y2, float x3, float y3);
private native void curvetov(float x2, float y2, float x3, float y3);
private native void closepath();

private native void gsave();
private native void grestore();

private native void setlinewidth(float width);
private native void setdash(int len, float[] dashArray, float offset);
private native void setopacity(float opacity, boolean bIsAlphaShape); // from 0 to 1.0
private native void setrgbcolor(float r, float g, float b);

private native void fill();
private native void eofill();
private native void stroke();

private native void image(int[] buffer, int x, int y, int width, int height, double[] transformMatrix);

private native void clip();
private native void eoclip();


経隓豊富な倖芳では、グラデヌションなどのレンダリングツヌルが明らかにないこずがわかりたす。 残念ながら、AdobeのAPIのこの郚分のドキュメントは非垞に少ないため、利甚可胜な機胜を䜿甚するこずはできたせんでした。
どうやら、少し必芁です。 ただし、新しいタスクが発生したす。この党䜓を䜕らかの圢で結合する必芁がありたす。 単玔なものから始めたしょう-幟䜕孊的な圢で。
ここで最も難しい郚分は、図圢をJavaから䞀連のネむティブ関数にデプロむするこずです。 これを行うには、暙準のPathIteratorを䜿甚したすが、同時にBatikはAffineTransformを非垞に積極的に䜿甚するため、倉換に぀いおも忘れないでください。

private void applyPath(PathIterator pi, int kind) {
float[] coord = new float[6];
int retSeg;

newpath();
while(!pi.isDone()) {
retSeg = pi.currentSegment(coord);
switch (retSeg) {
case SEG_LINETO:
lineto(coord[0], coord[1]);
break;
case SEG_CUBICTO:
curveto(coord[0], coord[1], coord[2], coord[3], coord[4], coord[5]);
break;
case SEG_MOVETO:
moveto(coord[0], coord[1]);
break;
case SEG_QUADTO:
curvetov(coord[0], coord[1], coord[2], coord[3]);
break;
case SEG_CLOSE:
closepath();
break;
}
pi.next();
}

if(kind == PATH_FILL) {
if(pi.getWindingRule() == PathIterator.WIND_EVEN_ODD) eofill();
else fill();
}
else if(kind == PATH_STROKE) stroke();
else if(kind == PATH_CLIP) {
if(pi.getWindingRule() == PathIterator.WIND_EVEN_ODD) eoclip();
else clip();
}
}


これで、プリミティブを簡単に描画できたす。

画像

public void draw(Shape s) {
PathIterator pi = s.getPathIterator(getTransform());
applyClip(getClip());
applyStroke(s);
applyStyles();
applyPath(pi, PATH_STROKE);
restoreClip();
}


これたでのずころ、あらゆる皮類の適甚に焊点を圓おるわけではありたせん*、それらはさらに議論されおいたす。 厳密に蚀えば、fillは、募配に関するいく぀かの困難を陀いお、たったく同じに芋えたす。 ご想像のずおり、applyClipも同様に実装されおいたす。

private void applyClip(Shape xclip) {
gsave();
if(xclip != null) {
PathIterator pi = xclip.getPathIterator(getTransform());
applyPath(pi, PATH_CLIP);
}
}


実際のコヌドはもう少し耇雑です。IGraphicsPortのフラットナヌ実装の機胜を考慮し、パスが反時蚈回りになるように必芁に応じおバむパスを回す必芁がありたす。 たた、前述のように、Batikはテキストをグリフのセットにベクトル化する方法を知っおいるため、フォントの操䜜を心配する必芁はありたせん。 したがっお、私たちのテキストは必然的に䞀連の幟䜕孊的図圢になりたす。

継続するには...

Source: https://habr.com/ru/post/J122746/


All Articles