001/*
002 * $Id: MetaDo.java 4784 2011-03-15 08:33:00Z blowagie $
003 *
004 * This file is part of the iText (R) project.
005 * Copyright (c) 1998-2011 1T3XT BVBA
006 * Authors: Bruno Lowagie, Paulo Soares, et al.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU Affero General Public License version 3
010 * as published by the Free Software Foundation with the addition of the
011 * following permission added to Section 15 as permitted in Section 7(a):
012 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY 1T3XT,
013 * 1T3XT DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
014 *
015 * This program is distributed in the hope that it will be useful, but
016 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
017 * or FITNESS FOR A PARTICULAR PURPOSE.
018 * See the GNU Affero General Public License for more details.
019 * You should have received a copy of the GNU Affero General Public License
020 * along with this program; if not, see http://www.gnu.org/licenses or write to
021 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
022 * Boston, MA, 02110-1301 USA, or download the license from the following URL:
023 * http://itextpdf.com/terms-of-use/
024 *
025 * The interactive user interfaces in modified source and object code versions
026 * of this program must display Appropriate Legal Notices, as required under
027 * Section 5 of the GNU Affero General Public License.
028 *
029 * In accordance with Section 7(b) of the GNU Affero General Public License,
030 * a covered work must retain the producer line in every PDF that is created
031 * or manipulated using iText.
032 *
033 * You can be released from the requirements of the license by purchasing
034 * a commercial license. Buying such a license is mandatory as soon as you
035 * develop commercial activities involving the iText software without
036 * disclosing the source code of your own applications.
037 * These activities include: offering paid services to customers as an ASP,
038 * serving PDFs on the fly in a web application, shipping iText with a closed
039 * source product.
040 *
041 * For more information, please contact iText Software Corp. at this
042 * address: sales@itextpdf.com
043 */
044package com.itextpdf.text.pdf.codec.wmf;
045import java.io.ByteArrayInputStream;
046import java.io.ByteArrayOutputStream;
047import java.io.IOException;
048import java.io.InputStream;
049import java.io.OutputStream;
050import java.io.UnsupportedEncodingException;
051import java.util.ArrayList;
052
053import com.itextpdf.text.BaseColor;
054import com.itextpdf.text.DocumentException;
055import com.itextpdf.text.Image;
056import com.itextpdf.text.error_messages.MessageLocalization;
057import com.itextpdf.text.pdf.BaseFont;
058import com.itextpdf.text.pdf.PdfContentByte;
059import com.itextpdf.text.pdf.codec.BmpImage;
060
061public class MetaDo {
062
063    public static final int META_SETBKCOLOR            = 0x0201;
064    public static final int META_SETBKMODE             = 0x0102;
065    public static final int META_SETMAPMODE            = 0x0103;
066    public static final int META_SETROP2               = 0x0104;
067    public static final int META_SETRELABS             = 0x0105;
068    public static final int META_SETPOLYFILLMODE       = 0x0106;
069    public static final int META_SETSTRETCHBLTMODE     = 0x0107;
070    public static final int META_SETTEXTCHAREXTRA      = 0x0108;
071    public static final int META_SETTEXTCOLOR          = 0x0209;
072    public static final int META_SETTEXTJUSTIFICATION  = 0x020A;
073    public static final int META_SETWINDOWORG          = 0x020B;
074    public static final int META_SETWINDOWEXT          = 0x020C;
075    public static final int META_SETVIEWPORTORG        = 0x020D;
076    public static final int META_SETVIEWPORTEXT        = 0x020E;
077    public static final int META_OFFSETWINDOWORG       = 0x020F;
078    public static final int META_SCALEWINDOWEXT        = 0x0410;
079    public static final int META_OFFSETVIEWPORTORG     = 0x0211;
080    public static final int META_SCALEVIEWPORTEXT      = 0x0412;
081    public static final int META_LINETO                = 0x0213;
082    public static final int META_MOVETO                = 0x0214;
083    public static final int META_EXCLUDECLIPRECT       = 0x0415;
084    public static final int META_INTERSECTCLIPRECT     = 0x0416;
085    public static final int META_ARC                   = 0x0817;
086    public static final int META_ELLIPSE               = 0x0418;
087    public static final int META_FLOODFILL             = 0x0419;
088    public static final int META_PIE                   = 0x081A;
089    public static final int META_RECTANGLE             = 0x041B;
090    public static final int META_ROUNDRECT             = 0x061C;
091    public static final int META_PATBLT                = 0x061D;
092    public static final int META_SAVEDC                = 0x001E;
093    public static final int META_SETPIXEL              = 0x041F;
094    public static final int META_OFFSETCLIPRGN         = 0x0220;
095    public static final int META_TEXTOUT               = 0x0521;
096    public static final int META_BITBLT                = 0x0922;
097    public static final int META_STRETCHBLT            = 0x0B23;
098    public static final int META_POLYGON               = 0x0324;
099    public static final int META_POLYLINE              = 0x0325;
100    public static final int META_ESCAPE                = 0x0626;
101    public static final int META_RESTOREDC             = 0x0127;
102    public static final int META_FILLREGION            = 0x0228;
103    public static final int META_FRAMEREGION           = 0x0429;
104    public static final int META_INVERTREGION          = 0x012A;
105    public static final int META_PAINTREGION           = 0x012B;
106    public static final int META_SELECTCLIPREGION      = 0x012C;
107    public static final int META_SELECTOBJECT          = 0x012D;
108    public static final int META_SETTEXTALIGN          = 0x012E;
109    public static final int META_CHORD                 = 0x0830;
110    public static final int META_SETMAPPERFLAGS        = 0x0231;
111    public static final int META_EXTTEXTOUT            = 0x0a32;
112    public static final int META_SETDIBTODEV           = 0x0d33;
113    public static final int META_SELECTPALETTE         = 0x0234;
114    public static final int META_REALIZEPALETTE        = 0x0035;
115    public static final int META_ANIMATEPALETTE        = 0x0436;
116    public static final int META_SETPALENTRIES         = 0x0037;
117    public static final int META_POLYPOLYGON           = 0x0538;
118    public static final int META_RESIZEPALETTE         = 0x0139;
119    public static final int META_DIBBITBLT             = 0x0940;
120    public static final int META_DIBSTRETCHBLT         = 0x0b41;
121    public static final int META_DIBCREATEPATTERNBRUSH = 0x0142;
122    public static final int META_STRETCHDIB            = 0x0f43;
123    public static final int META_EXTFLOODFILL          = 0x0548;
124    public static final int META_DELETEOBJECT          = 0x01f0;
125    public static final int META_CREATEPALETTE         = 0x00f7;
126    public static final int META_CREATEPATTERNBRUSH    = 0x01F9;
127    public static final int META_CREATEPENINDIRECT     = 0x02FA;
128    public static final int META_CREATEFONTINDIRECT    = 0x02FB;
129    public static final int META_CREATEBRUSHINDIRECT   = 0x02FC;
130    public static final int META_CREATEREGION          = 0x06FF;
131
132    public PdfContentByte cb;
133    public InputMeta in;
134    int left;
135    int top;
136    int right;
137    int bottom;
138    int inch;
139    MetaState state = new MetaState();
140
141    public MetaDo(InputStream in, PdfContentByte cb) {
142        this.cb = cb;
143        this.in = new InputMeta(in);
144    }
145
146    public void readAll() throws IOException, DocumentException{
147        if (in.readInt() != 0x9AC6CDD7) {
148            throw new DocumentException(MessageLocalization.getComposedMessage("not.a.placeable.windows.metafile"));
149        }
150        in.readWord();
151        left = in.readShort();
152        top = in.readShort();
153        right = in.readShort();
154        bottom = in.readShort();
155        inch = in.readWord();
156        state.setScalingX((float)(right - left) / (float)inch * 72f);
157        state.setScalingY((float)(bottom - top) / (float)inch * 72f);
158        state.setOffsetWx(left);
159        state.setOffsetWy(top);
160        state.setExtentWx(right - left);
161        state.setExtentWy(bottom - top);
162        in.readInt();
163        in.readWord();
164        in.skip(18);
165
166        int tsize;
167        int function;
168        cb.setLineCap(1);
169        cb.setLineJoin(1);
170        for (;;) {
171            int lenMarker = in.getLength();
172            tsize = in.readInt();
173            if (tsize < 3)
174                break;
175            function = in.readWord();
176            switch (function) {
177                case 0:
178                    break;
179                case META_CREATEPALETTE:
180                case META_CREATEREGION:
181                case META_DIBCREATEPATTERNBRUSH:
182                    state.addMetaObject(new MetaObject());
183                    break;
184                case META_CREATEPENINDIRECT:
185                {
186                    MetaPen pen = new MetaPen();
187                    pen.init(in);
188                    state.addMetaObject(pen);
189                    break;
190                }
191                case META_CREATEBRUSHINDIRECT:
192                {
193                    MetaBrush brush = new MetaBrush();
194                    brush.init(in);
195                    state.addMetaObject(brush);
196                    break;
197                }
198                case META_CREATEFONTINDIRECT:
199                {
200                    MetaFont font = new MetaFont();
201                    font.init(in);
202                    state.addMetaObject(font);
203                    break;
204                }
205                case META_SELECTOBJECT:
206                {
207                    int idx = in.readWord();
208                    state.selectMetaObject(idx, cb);
209                    break;
210                }
211                case META_DELETEOBJECT:
212                {
213                    int idx = in.readWord();
214                    state.deleteMetaObject(idx);
215                    break;
216                }
217                case META_SAVEDC:
218                    state.saveState(cb);
219                    break;
220                case META_RESTOREDC:
221                {
222                    int idx = in.readShort();
223                    state.restoreState(idx, cb);
224                    break;
225                }
226                case META_SETWINDOWORG:
227                    state.setOffsetWy(in.readShort());
228                    state.setOffsetWx(in.readShort());
229                    break;
230                case META_SETWINDOWEXT:
231                    state.setExtentWy(in.readShort());
232                    state.setExtentWx(in.readShort());
233                    break;
234                case META_MOVETO:
235                {
236                    int y = in.readShort();
237                    Point p = new Point(in.readShort(), y);
238                    state.setCurrentPoint(p);
239                    break;
240                }
241                case META_LINETO:
242                {
243                    int y = in.readShort();
244                    int x = in.readShort();
245                    Point p = state.getCurrentPoint();
246                    cb.moveTo(state.transformX(p.x), state.transformY(p.y));
247                    cb.lineTo(state.transformX(x), state.transformY(y));
248                    cb.stroke();
249                    state.setCurrentPoint(new Point(x, y));
250                    break;
251                }
252                case META_POLYLINE:
253                {
254                    state.setLineJoinPolygon(cb);
255                    int len = in.readWord();
256                    int x = in.readShort();
257                    int y = in.readShort();
258                    cb.moveTo(state.transformX(x), state.transformY(y));
259                    for (int k = 1; k < len; ++k) {
260                        x = in.readShort();
261                        y = in.readShort();
262                        cb.lineTo(state.transformX(x), state.transformY(y));
263                    }
264                    cb.stroke();
265                    break;
266                }
267                case META_POLYGON:
268                {
269                    if (isNullStrokeFill(false))
270                        break;
271                    int len = in.readWord();
272                    int sx = in.readShort();
273                    int sy = in.readShort();
274                    cb.moveTo(state.transformX(sx), state.transformY(sy));
275                    for (int k = 1; k < len; ++k) {
276                        int x = in.readShort();
277                        int y = in.readShort();
278                        cb.lineTo(state.transformX(x), state.transformY(y));
279                    }
280                    cb.lineTo(state.transformX(sx), state.transformY(sy));
281                    strokeAndFill();
282                    break;
283                }
284                case META_POLYPOLYGON:
285                {
286                    if (isNullStrokeFill(false))
287                        break;
288                    int numPoly = in.readWord();
289                    int lens[] = new int[numPoly];
290                    for (int k = 0; k < lens.length; ++k)
291                        lens[k] = in.readWord();
292                    for (int j = 0; j < lens.length; ++j) {
293                        int len = lens[j];
294                        int sx = in.readShort();
295                        int sy = in.readShort();
296                        cb.moveTo(state.transformX(sx), state.transformY(sy));
297                        for (int k = 1; k < len; ++k) {
298                            int x = in.readShort();
299                            int y = in.readShort();
300                            cb.lineTo(state.transformX(x), state.transformY(y));
301                        }
302                        cb.lineTo(state.transformX(sx), state.transformY(sy));
303                    }
304                    strokeAndFill();
305                    break;
306                }
307                case META_ELLIPSE:
308                {
309                    if (isNullStrokeFill(state.getLineNeutral()))
310                        break;
311                    int b = in.readShort();
312                    int r = in.readShort();
313                    int t = in.readShort();
314                    int l = in.readShort();
315                    cb.arc(state.transformX(l), state.transformY(b), state.transformX(r), state.transformY(t), 0, 360);
316                    strokeAndFill();
317                    break;
318                }
319                case META_ARC:
320                {
321                    if (isNullStrokeFill(state.getLineNeutral()))
322                        break;
323                    float yend = state.transformY(in.readShort());
324                    float xend = state.transformX(in.readShort());
325                    float ystart = state.transformY(in.readShort());
326                    float xstart = state.transformX(in.readShort());
327                    float b = state.transformY(in.readShort());
328                    float r = state.transformX(in.readShort());
329                    float t = state.transformY(in.readShort());
330                    float l = state.transformX(in.readShort());
331                    float cx = (r + l) / 2;
332                    float cy = (t + b) / 2;
333                    float arc1 = getArc(cx, cy, xstart, ystart);
334                    float arc2 = getArc(cx, cy, xend, yend);
335                    arc2 -= arc1;
336                    if (arc2 <= 0)
337                        arc2 += 360;
338                    cb.arc(l, b, r, t, arc1, arc2);
339                    cb.stroke();
340                    break;
341                }
342                case META_PIE:
343                {
344                    if (isNullStrokeFill(state.getLineNeutral()))
345                        break;
346                    float yend = state.transformY(in.readShort());
347                    float xend = state.transformX(in.readShort());
348                    float ystart = state.transformY(in.readShort());
349                    float xstart = state.transformX(in.readShort());
350                    float b = state.transformY(in.readShort());
351                    float r = state.transformX(in.readShort());
352                    float t = state.transformY(in.readShort());
353                    float l = state.transformX(in.readShort());
354                    float cx = (r + l) / 2;
355                    float cy = (t + b) / 2;
356                    float arc1 = getArc(cx, cy, xstart, ystart);
357                    float arc2 = getArc(cx, cy, xend, yend);
358                    arc2 -= arc1;
359                    if (arc2 <= 0)
360                        arc2 += 360;
361                    ArrayList<float[]> ar = PdfContentByte.bezierArc(l, b, r, t, arc1, arc2);
362                    if (ar.isEmpty())
363                        break;
364                    float pt[] = ar.get(0);
365                    cb.moveTo(cx, cy);
366                    cb.lineTo(pt[0], pt[1]);
367                    for (int k = 0; k < ar.size(); ++k) {
368                        pt = ar.get(k);
369                        cb.curveTo(pt[2], pt[3], pt[4], pt[5], pt[6], pt[7]);
370                    }
371                    cb.lineTo(cx, cy);
372                    strokeAndFill();
373                    break;
374                }
375                case META_CHORD:
376                {
377                    if (isNullStrokeFill(state.getLineNeutral()))
378                        break;
379                    float yend = state.transformY(in.readShort());
380                    float xend = state.transformX(in.readShort());
381                    float ystart = state.transformY(in.readShort());
382                    float xstart = state.transformX(in.readShort());
383                    float b = state.transformY(in.readShort());
384                    float r = state.transformX(in.readShort());
385                    float t = state.transformY(in.readShort());
386                    float l = state.transformX(in.readShort());
387                    float cx = (r + l) / 2;
388                    float cy = (t + b) / 2;
389                    float arc1 = getArc(cx, cy, xstart, ystart);
390                    float arc2 = getArc(cx, cy, xend, yend);
391                    arc2 -= arc1;
392                    if (arc2 <= 0)
393                        arc2 += 360;
394                    ArrayList<float[]> ar = PdfContentByte.bezierArc(l, b, r, t, arc1, arc2);
395                    if (ar.isEmpty())
396                        break;
397                    float pt[] = ar.get(0);
398                    cx = pt[0];
399                    cy = pt[1];
400                    cb.moveTo(cx, cy);
401                    for (int k = 0; k < ar.size(); ++k) {
402                        pt = ar.get(k);
403                        cb.curveTo(pt[2], pt[3], pt[4], pt[5], pt[6], pt[7]);
404                    }
405                    cb.lineTo(cx, cy);
406                    strokeAndFill();
407                    break;
408                }
409                case META_RECTANGLE:
410                {
411                    if (isNullStrokeFill(true))
412                        break;
413                    float b = state.transformY(in.readShort());
414                    float r = state.transformX(in.readShort());
415                    float t = state.transformY(in.readShort());
416                    float l = state.transformX(in.readShort());
417                    cb.rectangle(l, b, r - l, t - b);
418                    strokeAndFill();
419                    break;
420                }
421                case META_ROUNDRECT:
422                {
423                    if (isNullStrokeFill(true))
424                        break;
425                    float h = state.transformY(0) - state.transformY(in.readShort());
426                    float w = state.transformX(in.readShort()) - state.transformX(0);
427                    float b = state.transformY(in.readShort());
428                    float r = state.transformX(in.readShort());
429                    float t = state.transformY(in.readShort());
430                    float l = state.transformX(in.readShort());
431                    cb.roundRectangle(l, b, r - l, t - b, (h + w) / 4);
432                    strokeAndFill();
433                    break;
434                }
435                case META_INTERSECTCLIPRECT:
436                {
437                    float b = state.transformY(in.readShort());
438                    float r = state.transformX(in.readShort());
439                    float t = state.transformY(in.readShort());
440                    float l = state.transformX(in.readShort());
441                    cb.rectangle(l, b, r - l, t - b);
442                    cb.eoClip();
443                    cb.newPath();
444                    break;
445                }
446                case META_EXTTEXTOUT:
447                {
448                    int y = in.readShort();
449                    int x = in.readShort();
450                    int count = in.readWord();
451                    int flag = in.readWord();
452                    int x1 = 0;
453                    int y1 = 0;
454                    int x2 = 0;
455                    int y2 = 0;
456                    if ((flag & (MetaFont.ETO_CLIPPED | MetaFont.ETO_OPAQUE)) != 0) {
457                        x1 = in.readShort();
458                        y1 = in.readShort();
459                        x2 = in.readShort();
460                        y2 = in.readShort();
461                    }
462                    byte text[] = new byte[count];
463                    int k;
464                    for (k = 0; k < count; ++k) {
465                        byte c = (byte)in.readByte();
466                        if (c == 0)
467                            break;
468                        text[k] = c;
469                    }
470                    String s;
471                    try {
472                        s = new String(text, 0, k, "Cp1252");
473                    }
474                    catch (UnsupportedEncodingException e) {
475                        s = new String(text, 0, k);
476                    }
477                    outputText(x, y, flag, x1, y1, x2, y2, s);
478                    break;
479                }
480                case META_TEXTOUT:
481                {
482                    int count = in.readWord();
483                    byte text[] = new byte[count];
484                    int k;
485                    for (k = 0; k < count; ++k) {
486                        byte c = (byte)in.readByte();
487                        if (c == 0)
488                            break;
489                        text[k] = c;
490                    }
491                    String s;
492                    try {
493                        s = new String(text, 0, k, "Cp1252");
494                    }
495                    catch (UnsupportedEncodingException e) {
496                        s = new String(text, 0, k);
497                    }
498                    count = count + 1 & 0xfffe;
499                    in.skip(count - k);
500                    int y = in.readShort();
501                    int x = in.readShort();
502                    outputText(x, y, 0, 0, 0, 0, 0, s);
503                    break;
504                }
505                case META_SETBKCOLOR:
506                    state.setCurrentBackgroundColor(in.readColor());
507                    break;
508                case META_SETTEXTCOLOR:
509                    state.setCurrentTextColor(in.readColor());
510                    break;
511                case META_SETTEXTALIGN:
512                    state.setTextAlign(in.readWord());
513                    break;
514                case META_SETBKMODE:
515                    state.setBackgroundMode(in.readWord());
516                    break;
517                case META_SETPOLYFILLMODE:
518                    state.setPolyFillMode(in.readWord());
519                    break;
520                case META_SETPIXEL:
521                {
522                    BaseColor color = in.readColor();
523                    int y = in.readShort();
524                    int x = in.readShort();
525                    cb.saveState();
526                    cb.setColorFill(color);
527                    cb.rectangle(state.transformX(x), state.transformY(y), .2f, .2f);
528                    cb.fill();
529                    cb.restoreState();
530                    break;
531                }
532                case META_DIBSTRETCHBLT:
533                case META_STRETCHDIB: {
534                    int rop = in.readInt();
535                    if (function == META_STRETCHDIB) {
536                        /*int usage = */ in.readWord();
537                    }
538                    int srcHeight = in.readShort();
539                    int srcWidth = in.readShort();
540                    int ySrc = in.readShort();
541                    int xSrc = in.readShort();
542                    float destHeight = state.transformY(in.readShort()) - state.transformY(0);
543                    float destWidth = state.transformX(in.readShort()) - state.transformX(0);
544                    float yDest = state.transformY(in.readShort());
545                    float xDest = state.transformX(in.readShort());
546                    byte b[] = new byte[tsize * 2 - (in.getLength() - lenMarker)];
547                    for (int k = 0; k < b.length; ++k)
548                        b[k] = (byte)in.readByte();
549                    try {
550                        ByteArrayInputStream inb = new ByteArrayInputStream(b);
551                        Image bmp = BmpImage.getImage(inb, true, b.length);
552                        cb.saveState();
553                        cb.rectangle(xDest, yDest, destWidth, destHeight);
554                        cb.clip();
555                        cb.newPath();
556                        bmp.scaleAbsolute(destWidth * bmp.getWidth() / srcWidth, -destHeight * bmp.getHeight() / srcHeight);
557                        bmp.setAbsolutePosition(xDest - destWidth * xSrc / srcWidth, yDest + destHeight * ySrc / srcHeight - bmp.getScaledHeight());
558                        cb.addImage(bmp);
559                        cb.restoreState();
560                    }
561                    catch (Exception e) {
562                        // empty on purpose
563                    }
564                    break;
565                }
566            }
567            in.skip(tsize * 2 - (in.getLength() - lenMarker));
568        }
569        state.cleanup(cb);
570    }
571
572    public void outputText(int x, int y, int flag, int x1, int y1, int x2, int y2, String text) {
573        MetaFont font = state.getCurrentFont();
574        float refX = state.transformX(x);
575        float refY = state.transformY(y);
576        float angle = state.transformAngle(font.getAngle());
577        float sin = (float)Math.sin(angle);
578        float cos = (float)Math.cos(angle);
579        float fontSize = font.getFontSize(state);
580        BaseFont bf = font.getFont();
581        int align = state.getTextAlign();
582        float textWidth = bf.getWidthPoint(text, fontSize);
583        float tx = 0;
584        float ty = 0;
585        float descender = bf.getFontDescriptor(BaseFont.DESCENT, fontSize);
586        float ury = bf.getFontDescriptor(BaseFont.BBOXURY, fontSize);
587        cb.saveState();
588        cb.concatCTM(cos, sin, -sin, cos, refX, refY);
589        if ((align & MetaState.TA_CENTER) == MetaState.TA_CENTER)
590            tx = -textWidth / 2;
591        else if ((align & MetaState.TA_RIGHT) == MetaState.TA_RIGHT)
592            tx = -textWidth;
593        if ((align & MetaState.TA_BASELINE) == MetaState.TA_BASELINE)
594            ty = 0;
595        else if ((align & MetaState.TA_BOTTOM) == MetaState.TA_BOTTOM)
596            ty = -descender;
597        else
598            ty = -ury;
599        BaseColor textColor;
600        if (state.getBackgroundMode() == MetaState.OPAQUE) {
601            textColor = state.getCurrentBackgroundColor();
602            cb.setColorFill(textColor);
603            cb.rectangle(tx, ty + descender, textWidth, ury - descender);
604            cb.fill();
605        }
606        textColor = state.getCurrentTextColor();
607        cb.setColorFill(textColor);
608        cb.beginText();
609        cb.setFontAndSize(bf, fontSize);
610        cb.setTextMatrix(tx, ty);
611        cb.showText(text);
612        cb.endText();
613        if (font.isUnderline()) {
614            cb.rectangle(tx, ty - fontSize / 4, textWidth, fontSize / 15);
615            cb.fill();
616        }
617        if (font.isStrikeout()) {
618            cb.rectangle(tx, ty + fontSize / 3, textWidth, fontSize / 15);
619            cb.fill();
620        }
621        cb.restoreState();
622    }
623
624    public boolean isNullStrokeFill(boolean isRectangle) {
625        MetaPen pen = state.getCurrentPen();
626        MetaBrush brush = state.getCurrentBrush();
627        boolean noPen = pen.getStyle() == MetaPen.PS_NULL;
628        int style = brush.getStyle();
629        boolean isBrush = style == MetaBrush.BS_SOLID || style == MetaBrush.BS_HATCHED && state.getBackgroundMode() == MetaState.OPAQUE;
630        boolean result = noPen && !isBrush;
631        if (!noPen) {
632            if (isRectangle)
633                state.setLineJoinRectangle(cb);
634            else
635                state.setLineJoinPolygon(cb);
636        }
637        return result;
638    }
639
640    public void strokeAndFill(){
641        MetaPen pen = state.getCurrentPen();
642        MetaBrush brush = state.getCurrentBrush();
643        int penStyle = pen.getStyle();
644        int brushStyle = brush.getStyle();
645        if (penStyle == MetaPen.PS_NULL) {
646            cb.closePath();
647            if (state.getPolyFillMode() == MetaState.ALTERNATE) {
648                cb.eoFill();
649            }
650            else {
651                cb.fill();
652            }
653        }
654        else {
655            boolean isBrush = brushStyle == MetaBrush.BS_SOLID || brushStyle == MetaBrush.BS_HATCHED && state.getBackgroundMode() == MetaState.OPAQUE;
656            if (isBrush) {
657                if (state.getPolyFillMode() == MetaState.ALTERNATE)
658                    cb.closePathEoFillStroke();
659                else
660                    cb.closePathFillStroke();
661            }
662            else {
663                cb.closePathStroke();
664            }
665        }
666    }
667
668    static float getArc(float xCenter, float yCenter, float xDot, float yDot) {
669        double s = Math.atan2(yDot - yCenter, xDot - xCenter);
670        if (s < 0)
671            s += Math.PI * 2;
672        return (float)(s / Math.PI * 180);
673    }
674
675    public static byte[] wrapBMP(Image image) throws IOException {
676        if (image.getOriginalType() != Image.ORIGINAL_BMP)
677            throw new IOException(MessageLocalization.getComposedMessage("only.bmp.can.be.wrapped.in.wmf"));
678        InputStream imgIn;
679        byte data[] = null;
680        if (image.getOriginalData() == null) {
681            imgIn = image.getUrl().openStream();
682            ByteArrayOutputStream out = new ByteArrayOutputStream();
683            int b = 0;
684            while ((b = imgIn.read()) != -1)
685                out.write(b);
686            imgIn.close();
687            data = out.toByteArray();
688        }
689        else
690            data = image.getOriginalData();
691        int sizeBmpWords = data.length - 14 + 1 >>> 1;
692        ByteArrayOutputStream os = new ByteArrayOutputStream();
693        // write metafile header
694        writeWord(os, 1);
695        writeWord(os, 9);
696        writeWord(os, 0x0300);
697        writeDWord(os, 9 + 4 + 5 + 5 + 13 + sizeBmpWords + 3); // total metafile size
698        writeWord(os, 1);
699        writeDWord(os, 14 + sizeBmpWords); // max record size
700        writeWord(os, 0);
701        // write records
702        writeDWord(os, 4);
703        writeWord(os, META_SETMAPMODE);
704        writeWord(os, 8);
705
706        writeDWord(os, 5);
707        writeWord(os, META_SETWINDOWORG);
708        writeWord(os, 0);
709        writeWord(os, 0);
710
711        writeDWord(os, 5);
712        writeWord(os, META_SETWINDOWEXT);
713        writeWord(os, (int)image.getHeight());
714        writeWord(os, (int)image.getWidth());
715
716        writeDWord(os, 13 + sizeBmpWords);
717        writeWord(os, META_DIBSTRETCHBLT);
718        writeDWord(os, 0x00cc0020);
719        writeWord(os, (int)image.getHeight());
720        writeWord(os, (int)image.getWidth());
721        writeWord(os, 0);
722        writeWord(os, 0);
723        writeWord(os, (int)image.getHeight());
724        writeWord(os, (int)image.getWidth());
725        writeWord(os, 0);
726        writeWord(os, 0);
727        os.write(data, 14, data.length - 14);
728        if ((data.length & 1) == 1)
729            os.write(0);
730//        writeDWord(os, 14 + sizeBmpWords);
731//        writeWord(os, META_STRETCHDIB);
732//        writeDWord(os, 0x00cc0020);
733//        writeWord(os, 0);
734//        writeWord(os, (int)image.height());
735//        writeWord(os, (int)image.width());
736//        writeWord(os, 0);
737//        writeWord(os, 0);
738//        writeWord(os, (int)image.height());
739//        writeWord(os, (int)image.width());
740//        writeWord(os, 0);
741//        writeWord(os, 0);
742//        os.write(data, 14, data.length - 14);
743//        if ((data.length & 1) == 1)
744//            os.write(0);
745
746        writeDWord(os, 3);
747        writeWord(os, 0);
748        os.close();
749        return os.toByteArray();
750    }
751
752    public static void writeWord(OutputStream os, int v) throws IOException {
753        os.write(v & 0xff);
754        os.write(v >>> 8 & 0xff);
755    }
756
757    public static void writeDWord(OutputStream os, int v) throws IOException {
758        writeWord(os, v & 0xffff);
759        writeWord(os, v >>> 16 & 0xffff);
760    }
761}