diff options
author | Dimitri van Heesch <doxygen@gmail.com> | 2019-06-23 18:15:44 (GMT) |
---|---|---|
committer | Dimitri van Heesch <doxygen@gmail.com> | 2019-06-23 18:50:56 (GMT) |
commit | 6fb61b2c6561149c1a318df26fd1acac619b3519 (patch) | |
tree | ad73d56e94aebbd888db4077bc367895fbb8ad83 /libmscgen/mscgen_ps_out.c | |
parent | 15678e7ccb524af8c90a4018f3b882ba184e6a39 (diff) | |
download | Doxygen-6fb61b2c6561149c1a318df26fd1acac619b3519.zip Doxygen-6fb61b2c6561149c1a318df26fd1acac619b3519.tar.gz Doxygen-6fb61b2c6561149c1a318df26fd1acac619b3519.tar.bz2 |
issue #6880 mscgen links placed in the wrong place when using SVG output
Diffstat (limited to 'libmscgen/mscgen_ps_out.c')
-rw-r--r-- | libmscgen/mscgen_ps_out.c | 595 |
1 files changed, 595 insertions, 0 deletions
diff --git a/libmscgen/mscgen_ps_out.c b/libmscgen/mscgen_ps_out.c new file mode 100644 index 0000000..32f9eae --- /dev/null +++ b/libmscgen/mscgen_ps_out.c @@ -0,0 +1,595 @@ +/*************************************************************************** + * + * $Id: ps_out.c 186 2011-03-01 21:18:19Z Michael.McTernan $ + * + * This file is part of mscgen, a message sequence chart renderer. + * Copyright (C) 2005 Michael C McTernan, Michael.McTernan.2001@cs.bris.ac.uk + * + * This program 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. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + **************************************************************************/ + +#include "mscgen_config.h" +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#include <assert.h> +#include <string.h> +#include "mscgen_adraw_int.h" +#include "mscgen_utf8.h" +#include "mscgen_safe.h" + +/*************************************************************************** + * Manifest Constants + ***************************************************************************/ + +/** Overall scaling of the Postscript output. + */ +#define PS_OUT_SCALE 0.7f + +/*************************************************************************** + * Local types + ***************************************************************************/ + +typedef struct PsContextTag +{ + /** Output file. */ + FILE *of; + + /** Point size of the current font. */ + int fontPoints; + + /** Current pen colour. */ + ADrawColour penColour; + + /** Background colour for the pen. */ + ADrawColour penBgColour; +} +PsContext; + +typedef struct +{ + int capheight, xheight, ascender, descender; + int widths[256]; +} +PsCharMetric; + +/** Helvetica character widths. + * This gives the width of each character is 1/1000ths of a point. + * The values are taken from the Adobe Font Metric file for Hevletica. + */ +static const PsCharMetric PsHelvetica = +{ + 718, 523, 718, -207, + { + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 278, 278, 355, 556, 556, 889, 667, 222, + 333, 333, 389, 584, 278, 333, 278, 278, + 556, 556, 556, 556, 556, 556, 556, 556, + 556, 556, 278, 278, 584, 584, 584, 556, + 1015, 667, 667, 722, 722, 667, 611, 778, + 722, 278, 500, 667, 556, 833, 722, 778, + 667, 778, 722, 667, 611, 722, 667, 944, + 667, 667, 611, 278, 278, 278, 469, 556, + 222, 556, 556, 500, 556, 556, 278, 556, + 556, 222, 222, 500, 222, 833, 556, 556, + 556, 556, 333, 500, 278, 556, 500, 722, + 500, 500, 500, 334, 260, 334, 584, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 333, 556, 556, 167, 556, 556, 556, + 556, 191, 333, 556, 333, 333, 500, 500, + -1, 556, 556, 556, 278, -1, 537, 350, + 222, 333, 333, 556, 1000, 1000, -1, 611, + -1, 333, 333, 333, 333, 333, 333, 333, + 333, -1, 333, 333, -1, 333, 333, 333, + 1000, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 1000, -1, 370, -1, -1, -1, -1, + 556, 778, 1000, 365, -1, -1, -1, -1, + -1, 889, -1, -1, -1, 278, -1, -1, + 222, 611, 944, 611, -1, -1, -1, -1 + } +}; + +/*************************************************************************** + * Helper functions + ***************************************************************************/ + + +/** Get the context pointer from an ADraw structure. + */ +static PsContext *getPsCtx(struct ADrawTag *ctx) +{ + return (PsContext *)ctx->internal; +} + +/** Get the context pointer from an ADraw structure. + */ +static FILE *getPsFile(struct ADrawTag *ctx) +{ + return getPsCtx(ctx)->of; +} + +/** Given a font metric measurement, return device dependent units. + * Font metric data is stored as 1/1000th of a point, and therefore + * needs to be multiplied by the font point size and divided by + * 1000 to give a value in device dependent units. + */ +static int getSpace(struct ADrawTag *ctx, long thousanths) +{ + return ((thousanths * getPsCtx(ctx)->fontPoints) + 500) / 1000; +} + +/** Write out a line of text, escaping special characters. + */ +static void writeEscaped(struct ADrawTag *ctx, const char *string) +{ + FILE *f = getPsFile(ctx); + + while(*string != '\0') + { + unsigned int code, bytes; + + switch(*string) + { + case '(': fprintf(f, "\\("); break; + case ')': fprintf(f, "\\)"); break; + default: + if(Utf8Decode(string, &code, &bytes)) + { + fprintf(f, "\\%o", code); + string += bytes - 1; + } + else + { + fputc(*string, f); + } + break; + } + + string++; + } +} + +static void setColour(struct ADrawTag *ctx, + ADrawColour col) +{ + float r, g, b; + + /* Extract RGB values */ + r = (float)((col & 0xff0000) >> 16); + g = (float)((col & 0x00ff00) >> 8); + b = (float)((col & 0x0000ff) >> 0); + + /* Normalise */ + r /= 255.0f; + g /= 255.0f; + b /= 255.0f; + + /* Generate output command */ + fprintf(getPsFile(ctx), "%f %f %f setrgbcolor\n", r ,g ,b); +} + +/*************************************************************************** + * API Functions + ***************************************************************************/ + +unsigned int PsTextWidth(struct ADrawTag *ctx, + const char *string) +{ + unsigned long width = 0; + + while(*string != '\0') + { + int i = *string & 0xff; + unsigned long w = PsHelvetica.widths[i]; + + /* Ignore undefined characters */ + width += w > 0 ? w : 0; + + string++; + } + + return getSpace(ctx, width); +} + + +int PsTextHeight(struct ADrawTag *ctx) +{ + return getSpace(ctx, PsHelvetica.ascender - PsHelvetica.descender); +} + + +void PsLine(struct ADrawTag *ctx, + unsigned int x1, + unsigned int y1, + unsigned int x2, + unsigned int y2) +{ + fprintf(getPsFile(ctx), + "newpath %d %d moveto %d %d lineto stroke\n", + x1, 0-y1, x2, 0-y2); + +} + + +void PsDottedLine(struct ADrawTag *ctx, + unsigned int x1, + unsigned int y1, + unsigned int x2, + unsigned int y2) +{ + fprintf(getPsFile(ctx), "[2] 0 setdash\n"); + PsLine(ctx, x1, y1, x2, y2); + fprintf(getPsFile(ctx), "[] 0 setdash\n"); +} + + +void PsFilledRectangle(struct ADrawTag *ctx, + unsigned int x1, + unsigned int y1, + unsigned int x2, + unsigned int y2) +{ + fprintf(getPsFile(ctx), + "newpath " + "%d %d moveto " + "%d %d lineto " + "%d %d lineto " + "%d %d lineto " + "closepath " + "fill\n", + x1, 0-y1, + x2, 0-y1, + x2, 0-y2, + x1, 0-y2); +} + + +void PsTextR(struct ADrawTag *ctx, + unsigned int x, + unsigned int y, + const char *string) +{ + PsContext *context = getPsCtx(ctx); + + /* Push the string and get its width */ + fprintf(getPsFile(ctx), "("); + writeEscaped(ctx, string); + fprintf(getPsFile(ctx), ") dup stringwidth\n"); + + /* Draw the background box */ + setColour(ctx, context->penBgColour); + fprintf(getPsFile(ctx), "pop " /* Ignore y-value */ + "dup " /* Duplicate string width */ + "newpath " + "%d %d moveto " /* Bottom left of the box */ + "0 rlineto " /* Move to bottom right of the box */ + "0 %d rlineto " /* To top right */ + "neg 0 rlineto " /* Back to bottom left */ + "closepath fill\n", /* Done */ + x, 0-y - getSpace(ctx, PsHelvetica.descender), + getSpace(ctx, PsHelvetica.ascender)); + + /* Restore pen and show the string */ + setColour(ctx, context->penColour); + fprintf(getPsFile(ctx), "%d %d moveto show\n", + x, 0-y - getSpace(ctx, PsHelvetica.descender)); +} + + +void PsTextL(struct ADrawTag *ctx, + unsigned int x, + unsigned int y, + const char *string) +{ + PsContext *context = getPsCtx(ctx); + + /* Draw the background box */ + setColour(ctx, context->penBgColour); + PsFilledRectangle(ctx, x, 0-y, x + 10, 0-y + 10); + setColour(ctx, context->penColour); + + fprintf(getPsFile(ctx), + "%d %d moveto " + "(", + x, 0-y - getSpace(ctx, PsHelvetica.descender)); + writeEscaped(ctx, string); + fprintf(getPsFile(ctx), + ") dup stringwidth " + "pop " /* Ignore y value */ + "neg " /* Invert x value */ + "0 " + "rmoveto " + "show\n"); +} + + +void PsTextC(struct ADrawTag *ctx, + unsigned int x, + unsigned int y, + const char *string) +{ + PsContext *context = getPsCtx(ctx); + + /* Push the string and get its width */ + fprintf(getPsFile(ctx), "("); + writeEscaped(ctx, string); + fprintf(getPsFile(ctx), ") dup stringwidth\n"); + + /* Draw the background box */ + setColour(ctx, context->penBgColour); + fprintf(getPsFile(ctx), "pop " /* Ignore y-value */ + "dup dup " /* Duplicate string width twice */ + "newpath " + "%d %d moveto " /* Starting point, centre bottom of box */ + "2 div neg 0 rmoveto " /* Move to bottom left */ + "0 rlineto " /* Move to bottom right of the box */ + "0 %d rlineto " /* To top right */ + "neg 0 rlineto " /* Back to bottom left */ + "closepath fill\n", /* Done */ + x, 0-y, + getSpace(ctx, PsHelvetica.ascender)); + + /* Restore pen and show the string */ + setColour(ctx, context->penColour); + fprintf(getPsFile(ctx), "%d %d moveto dup stringwidth pop 2 div neg 0 rmoveto show\n", + x, 0-y - getSpace(ctx, PsHelvetica.descender)); +} + + +void PsFilledTriangle(struct ADrawTag *ctx, + unsigned int x1, + unsigned int y1, + unsigned int x2, + unsigned int y2, + unsigned int x3, + unsigned int y3) +{ + fprintf(getPsFile(ctx), + "newpath " + "%d %d moveto " + "%d %d lineto " + "%d %d lineto " + "closepath " + "fill\n", + x1, 0-y1, + x2, 0-y2, + x3, 0-y3); +} + + +void PsFilledCircle(struct ADrawTag *ctx, + unsigned int x, + unsigned int y, + unsigned int r) +{ + fprintf(getPsFile(ctx), + "newpath " + "%d %d %d 0 360 arc " + "closepath " + "fill\n", + x, 0-y, r); +} + + +void PsArc(struct ADrawTag *ctx, + unsigned int cx, + unsigned int cy, + unsigned int w, + unsigned int h, + unsigned int s, + unsigned int e) +{ + fprintf(getPsFile(ctx), + "newpath " + "%d %d %d %d %d %d ellipse " + "stroke\n", + cx, 0-cy, w, h, s, e); +} + + +void PsDottedArc(struct ADrawTag *ctx, + unsigned int cx, + unsigned int cy, + unsigned int w, + unsigned int h, + unsigned int s, + unsigned int e) +{ + fprintf(getPsFile(ctx), "[2] 0 setdash\n"); + PsArc(ctx, cx, cy, w, h, s, e); + fprintf(getPsFile(ctx), "[] 0 setdash\n"); +} + + +void PsSetPen(struct ADrawTag *ctx, + ADrawColour col) +{ + PsContext *context = getPsCtx(ctx); + + assert(col != ADRAW_COL_INVALID); + + /* Check if the pen colour has changed */ + if(context->penColour != col) + { + setColour(ctx, col); + + /* Store the pen colour */ + context->penColour = col; + } +} + + +void PsSetBgPen(struct ADrawTag *ctx, + ADrawColour col) +{ + PsContext *context = getPsCtx(ctx); + + context->penBgColour = col; +} + + +void PsSetFontSize(struct ADrawTag *ctx, + ADrawFontSize size) +{ + PsContext *context = getPsCtx(ctx); + + switch(size) + { + case ADRAW_FONT_TINY: + getPsCtx(ctx)->fontPoints = 8; + break; + + case ADRAW_FONT_SMALL: + getPsCtx(ctx)->fontPoints = 12; + break; + + default: + assert(0); + } + + fprintf(context->of, "/Helvetica findfont\n"); + fprintf(context->of, "%d scalefont\n", getPsCtx(ctx)->fontPoints); + fprintf(context->of, "setfont\n"); +} + + +Boolean PsClose(struct ADrawTag *ctx) +{ + PsContext *context = getPsCtx(ctx); + + /* Close the output file */ + if(context->of != stdout) + { + fclose(context->of); + } + + /* Free and destroy context */ + free(context); + ctx->internal = NULL; + + return TRUE; +} + + + +Boolean PsInit(unsigned int w, + unsigned int h, + const char *file, + struct ADrawTag *outContext) +{ + PsContext *context; + + /* Create context */ + context = outContext->internal = malloc_s(sizeof(PsContext)); + if(context == NULL) + { + return FALSE; + } + + /* Open the output file */ + if(strcmp(file, "-") == 0) + { + context->of = stdout; + } + else + { + context->of = fopen(file, "wb"); + if(!context->of) + { + fprintf(stderr, "PsInit: Failed to open output file '%s': %s\n", file, strerror(errno)); + return FALSE; + } + } + + /* Write the header */ + fprintf(context->of, "%%!PS-Adobe-3.0 EPSF-2.0\n" + "%%%%BoundingBox: 0 0 %.0f %.0f\n", w * PS_OUT_SCALE, h * PS_OUT_SCALE); + fprintf(context->of, "%%%%Creator: mscgen %s\n", PACKAGE_VERSION); + fprintf(context->of, "%%%%EndComments\n"); + + /* Shrink everything by 70% */ + fprintf(context->of, "%f %f scale\n", PS_OUT_SCALE, PS_OUT_SCALE); + + /* Create clipping rectangle to constrain dimensions */ + fprintf(context->of, "0 0 moveto\n"); + fprintf(context->of, "0 %u lineto\n", h); + fprintf(context->of, "%u %u lineto\n", w, h); + fprintf(context->of, "%u 0 lineto\n", w); + fprintf(context->of, "closepath\n"); + fprintf(context->of, "clip\n"); + fprintf(context->of, "%%PageTrailer\n"); + fprintf(context->of, "%%Page: 1 1\n"); + + /* Set default font */ + fprintf(context->of, "/Helvetica findfont\n"); + fprintf(context->of, "10 scalefont\n"); + fprintf(context->of, "setfont\n"); + + /* Get the default font size */ + PsSetFontSize(outContext, ADRAW_FONT_SMALL); + + /* Translate up by the height, y-axis will be inverted */ + fprintf(context->of, "0 %d translate\n", h); + + /* Arc drawing function */ + fprintf(context->of, "/mtrx matrix def\n" + "/ellipse\n" + " { /endangle exch def\n" + " /startangle exch def\n" + " /ydia exch def\n" + " /xdia exch def\n" + " /y exch def\n" + " /x exch def\n" + " /savematrix mtrx currentmatrix def\n" + " x y translate\n" + " xdia 2 div ydia 2 div scale\n" + " 1 -1 scale\n" + " 0 0 1 startangle endangle arc\n" + " savematrix setmatrix\n" + "} def\n"); + + /* Set the current pen colours */ + context->penColour = ADRAW_COL_BLACK; + context->penBgColour = ADRAW_COL_WHITE; + + /* Now fill in the function pointers */ + outContext->line = PsLine; + outContext->dottedLine = PsDottedLine; + outContext->textL = PsTextL; + outContext->textC = PsTextC; + outContext->textR = PsTextR; + outContext->textWidth = PsTextWidth; + outContext->textHeight = PsTextHeight; + outContext->filledRectangle = PsFilledRectangle; + outContext->filledTriangle = PsFilledTriangle; + outContext->filledCircle = PsFilledCircle; + outContext->arc = PsArc; + outContext->dottedArc = PsDottedArc; + outContext->setPen = PsSetPen; + outContext->setBgPen = PsSetBgPen; + outContext->setFontSize = PsSetFontSize; + outContext->close = PsClose; + + return TRUE; +} + +/* END OF FILE */ |