mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1829 lines
43 KiB
1829 lines
43 KiB
/*++
|
|
|
|
Copyright (c) 1995 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
textout.c
|
|
|
|
Abstract:
|
|
|
|
Implementation of DDI entry points DrvTextOut, DrvGetGlyphMode,
|
|
and their related functions.
|
|
|
|
[Environment:]
|
|
|
|
Win32 subsystem, PostScript driver
|
|
|
|
Revision History:
|
|
|
|
02/12/91 -kentse-
|
|
Created it.
|
|
|
|
08/08/95 -davidx-
|
|
Move hardcoded PS code into the resource.
|
|
Clean up.
|
|
|
|
08/18/95 -davidx-
|
|
Option to do more accurate font substitution.
|
|
Fast text output when characters are individually placed.
|
|
|
|
mm/dd/yy -author-
|
|
description
|
|
|
|
--*/
|
|
|
|
#include "pscript.h"
|
|
#include "type1.h"
|
|
|
|
BOOL DrvCommonPath(PDEVDATA, PATHOBJ *, BOOL, BOOL *, XFORMOBJ *,
|
|
BRUSHOBJ *, PPOINTL, PLINEATTRS);
|
|
|
|
BOOL DrawGlyphs(PDEVDATA,DWORD,GLYPHPOS*,FONTOBJ*,STROBJ*,TEXTDATA*,PWSTR);
|
|
VOID CharBitmap(PDEVDATA, GLYPHPOS *);
|
|
BOOL SetFontRemap(PDEVDATA, DWORD);
|
|
BOOL QueryFontRemap(PDEVDATA, DWORD);
|
|
BOOL ShouldWeRemap(PDEVDATA, ULONG, GLYPHPOS *, PWSTR, BOOL);
|
|
VOID Add1Font(PDEVDATA, FONTOBJ *);
|
|
BOOL SelectFont(PDEVDATA, FONTOBJ *, STROBJ *, TEXTDATA *);
|
|
VOID OutputGlyphBitmap(PDEVDATA, GLYPHBITS *);
|
|
BOOL CanBeSubstituted(STROBJ *);
|
|
DWORD SubstituteIFace(PDEVDATA, FONTOBJ *);
|
|
|
|
|
|
|
|
BOOL
|
|
DrvTextOut(
|
|
SURFOBJ *pso,
|
|
STROBJ *pstro,
|
|
FONTOBJ *pfo,
|
|
CLIPOBJ *pco,
|
|
PRECTL prclExtra,
|
|
PRECTL prclOpaque,
|
|
BRUSHOBJ *pboFore,
|
|
BRUSHOBJ *pboOpaque,
|
|
PPOINTL pptlOrg,
|
|
MIX mix
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Implementation of DDI entry point DrvTextOut.
|
|
Please refer to DDK documentation for more details.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVDATA pdev;
|
|
DWORD cGlyphs;
|
|
BOOL gsaved;
|
|
TEXTDATA tdata;
|
|
|
|
TRACEDDIENTRY("DrvTextOut");
|
|
|
|
// Validate input parameters
|
|
|
|
if (pso == NULL || pstro == NULL || pfo == NULL) {
|
|
DBGMSG(DBG_LEVEL_ERROR, "Invalid parameters.\n");
|
|
SETLASTERROR(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
// Get the pointer to our DEVDATA structure and validate it
|
|
|
|
pdev = (PDEVDATA) pso->dhpdev;
|
|
|
|
if (! bValidatePDEV(pdev)) {
|
|
DBGERRMSG("bValidatePDEV");
|
|
SETLASTERROR(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
if (pdev->dwFlags & PDEV_CANCELDOC)
|
|
return FALSE;
|
|
|
|
if (pdev->dwFlags & PDEV_IGNORE_GDI)
|
|
return TRUE;
|
|
|
|
// Find out if we ever get any bitmap fonts
|
|
|
|
if (! (pfo->flFontType & (DEVICE_FONTTYPE | TRUETYPE_FONTTYPE))) {
|
|
DBGMSG(DBG_LEVEL_WARNING, "Bitmap font type\n");
|
|
}
|
|
|
|
// Make sure we have been given a valid font.
|
|
|
|
if ((pfo->flFontType & DEVICE_FONTTYPE) &&
|
|
! ValidPsFontIndex(pdev, pfo->iFace))
|
|
{
|
|
DBGERRMSG("ValidPsFontIndex");
|
|
SETLASTERROR(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
// Initialize our TEXTDATA structure.
|
|
|
|
tdata.flAccel = pstro->flAccel;
|
|
tdata.bFontSubstitution = FALSE;
|
|
tdata.iFace = pfo->iFace;
|
|
|
|
// Check if we need to substitute TrueType font with device font
|
|
|
|
if ((pfo->flFontType & TRUETYPE_FONTTYPE) &&
|
|
(pdev->cDeviceFonts > 0) &&
|
|
(pdev->dm.dmPrivate.dwFlags & PSDEVMODE_FONTSUBST) &&
|
|
CanBeSubstituted(pstro))
|
|
{
|
|
DWORD newface = SubstituteIFace(pdev, pfo);
|
|
|
|
if (newface > 0 && newface <= pdev->cDeviceFonts) {
|
|
tdata.bFontSubstitution = TRUE;
|
|
tdata.iFace = newface;
|
|
}
|
|
}
|
|
|
|
tdata.bDeviceFont = tdata.bFontSubstitution || (pfo->flFontType & DEVICE_FONTTYPE);
|
|
tdata.bSimItalic = tdata.bDeviceFont && (pfo->flFontType & FO_SIM_ITALIC);
|
|
tdata.doReencode = FALSE;
|
|
|
|
// Select the current font in the printer from the given FONTOBJ.
|
|
|
|
if (! SelectFont(pdev, pfo, pstro, &tdata)) {
|
|
DBGERRMSG("SelectFont");
|
|
return FALSE;
|
|
}
|
|
|
|
// Handle the clip object passed in.
|
|
|
|
gsaved = bDoClipObj(pdev, pco, NULL, NULL);
|
|
|
|
// Output the opaque rectangle if necessary. This is a background
|
|
// rectangle that goes behind the foreground text, therefore, send
|
|
// it to the printer before the text.
|
|
|
|
if (prclOpaque != NULL) {
|
|
|
|
ps_newpath(pdev);
|
|
ps_box(pdev, prclOpaque, FALSE);
|
|
|
|
if (! ps_patfill(pdev, pso, (FLONG) FP_WINDINGMODE, pboOpaque,
|
|
pptlOrg, MixToRop4(mix), prclOpaque, FALSE, FALSE))
|
|
{
|
|
DBGERRMSG("ps_patfill");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// Output the text color to draw with.
|
|
|
|
if (pboFore->iSolidColor == NOT_SOLID_COLOR) {
|
|
|
|
ULONG ulColor;
|
|
|
|
// This is not a solid brush, so get a pointer to the
|
|
// realized brush.
|
|
|
|
DBGMSG(DBG_LEVEL_WARNING,
|
|
"Non-solid text brush, defaulting to black.\n");
|
|
|
|
ulColor = RGB_BLACK;
|
|
ps_setrgbcolor(pdev, (PSRGB *)&ulColor);
|
|
} else
|
|
// We have a solid brush, so simply output its color.
|
|
ps_setrgbcolor(pdev, (PSRGB *)&pboFore->iSolidColor);
|
|
|
|
if (pstro->pgp) {
|
|
|
|
// If the GLYPHPOS's are provided, then use it
|
|
|
|
if (! DrawGlyphs(pdev, pstro->cGlyphs, pstro->pgp,
|
|
pfo, pstro, &tdata, pstro->pwszOrg))
|
|
{
|
|
DBGERRMSG("DrawGlyphs");
|
|
return FALSE;
|
|
}
|
|
|
|
} else {
|
|
|
|
BOOL bMore;
|
|
GLYPHPOS *pGlyphPos;
|
|
PWSTR pwstr = pstro->pwszOrg;
|
|
|
|
// If the GLYPHPOS's are not provided, then we
|
|
// have to enumerate on the STROBJ.
|
|
|
|
STROBJ_vEnumStart(pstro);
|
|
|
|
do {
|
|
bMore = STROBJ_bEnum(pstro, &cGlyphs, &pGlyphPos);
|
|
|
|
if (! DrawGlyphs(pdev, cGlyphs, pGlyphPos,
|
|
pfo, pstro, &tdata, pwstr))
|
|
{
|
|
DBGERRMSG("DrawGlyphs");
|
|
return FALSE;
|
|
}
|
|
|
|
if (pwstr != NULL)
|
|
pwstr += cGlyphs;
|
|
} while (bMore);
|
|
}
|
|
|
|
// Output the extra rectangles if necessary. These rectangles are
|
|
// bottom right exclusive. The pels of the rectangles are to be
|
|
// combined with the pixels of the glyphs to produce the foreground
|
|
// pels. The extra rectangles are used to simulate underlining or
|
|
// strikeout.
|
|
|
|
if (prclExtra != NULL) {
|
|
|
|
RECTL rclBounds;
|
|
|
|
// output a newpath command to the printer.
|
|
|
|
ps_newpath(pdev);
|
|
|
|
// Set up bounding rectangle.
|
|
|
|
rclBounds = *prclExtra;
|
|
|
|
// Output each extra rectangle until we find the terminating
|
|
// retangle with all NULL coordinates.
|
|
|
|
while ((prclExtra->right != prclExtra->left) ||
|
|
(prclExtra->top != prclExtra->bottom) ||
|
|
(prclExtra->right != 0L) ||
|
|
(prclExtra->top != 0L))
|
|
{
|
|
ps_box(pdev, prclExtra, FALSE);
|
|
|
|
// Update the bounding rectangle if necessary.
|
|
|
|
if (prclExtra->left < rclBounds.left)
|
|
rclBounds.left = prclExtra->left;
|
|
if (prclExtra->right > rclBounds.right)
|
|
rclBounds.right = prclExtra->right;
|
|
if (prclExtra->top < rclBounds.top)
|
|
rclBounds.top = prclExtra->top;
|
|
if (prclExtra->bottom > rclBounds.bottom)
|
|
rclBounds.bottom = prclExtra->bottom;
|
|
|
|
prclExtra++;
|
|
}
|
|
|
|
// Call the driver's filling routine. This routine will do the
|
|
// right thing with the brush.
|
|
|
|
if (!ps_patfill(pdev, pso, (FLONG)FP_WINDINGMODE, pboFore, pptlOrg,
|
|
MixToRop4(mix), &rclBounds, FALSE, FALSE))
|
|
{
|
|
DBGERRMSG("ps_patfill");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// If a gsave was performed earlier, do a grestore here
|
|
|
|
if (gsaved)
|
|
ps_restore(pdev, TRUE, FALSE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
ULONG
|
|
DrvGetGlyphMode(
|
|
DHPDEV dhpdev,
|
|
FONTOBJ *pfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Implementation of DDI entry point DrvGetGlyphMode.
|
|
Please refer to DDK documentation for more details.
|
|
|
|
--*/
|
|
|
|
{
|
|
return FO_GLYPHBITS;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
bDoClipObj(
|
|
PDEVDATA pdev,
|
|
CLIPOBJ *pco,
|
|
RECTL *prclClipBound,
|
|
RECTL *prclTarget
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Send PS code to the printer to set up the clippath as
|
|
specified by CLIPOBJ pco.
|
|
|
|
Arguments:
|
|
|
|
pdev Pointer to DEVDATA structure
|
|
pco Pointer to CLIPOBJ
|
|
prclClipBound
|
|
Pointer to a RECTL variable for returning the
|
|
bounding rectangle of the clippath.
|
|
NULL if the caller is not interested in such.
|
|
prclTarget
|
|
Bounding rectangle for the figure to be drawn.
|
|
If this is specified and it's entirely contained
|
|
within the CLIPOBJ, then no clipping is done.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the clipping path is set up in the printer and
|
|
a gsaved was performed. FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
PATHOBJ *ppo;
|
|
|
|
if (pco == NULL)
|
|
return FALSE;
|
|
|
|
switch(pco->iDComplexity) {
|
|
|
|
case DC_TRIVIAL:
|
|
|
|
// In this case, there is no clipping. Therefore, we have
|
|
// no commands to send to the printer.
|
|
|
|
return FALSE;
|
|
|
|
case DC_RECT:
|
|
|
|
// Check to see if the target rectangle fits inside the clip
|
|
// rectangle. If it does, don't do clipping.
|
|
|
|
if (prclTarget != NULL) {
|
|
|
|
if ((pco->rclBounds.left <= prclTarget->left) &&
|
|
(pco->rclBounds.top <= prclTarget->top) &&
|
|
(pco->rclBounds.right >= prclTarget->right) &&
|
|
(pco->rclBounds.bottom >= prclTarget->bottom))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// In this case, we are clipping to a single rectangle.
|
|
// get it from the CLIPOBJ, then send it to the printer.
|
|
|
|
if (! ps_save(pdev, TRUE, FALSE))
|
|
return FALSE;
|
|
|
|
ps_newpath(pdev);
|
|
ps_box(pdev, &pco->rclBounds, TRUE);
|
|
|
|
if (prclClipBound != NULL)
|
|
*prclClipBound = pco->rclBounds;
|
|
|
|
// ps_box above has already done a clip.
|
|
// Simply return success.
|
|
|
|
return TRUE;
|
|
|
|
case DC_COMPLEX:
|
|
|
|
// In this case, we are clipping to a complex clip region.
|
|
// Enumerate the clip region from the CLIPOBJ, and send the
|
|
// entire clip region to the printer.
|
|
|
|
if (! ps_save(pdev, TRUE, FALSE)) {
|
|
DBGERRMSG("ps_save");
|
|
return FALSE;
|
|
}
|
|
|
|
// Call the engine to get the clippath.
|
|
|
|
if ((ppo = CLIPOBJ_ppoGetPath(pco)) == NULL)
|
|
{
|
|
DBGERRMSG("CLIPOBJ_ppoGetPath");
|
|
ps_restore(pdev, TRUE, FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
// Send the path to the printer.
|
|
|
|
if (! DrvCommonPath(pdev, ppo, FALSE, NULL, NULL, NULL, NULL, NULL))
|
|
{
|
|
DBGERRMSG("DrvCommonPath");
|
|
ps_restore(pdev, TRUE, FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
// Update the clipping bound rectangle if necessary.
|
|
|
|
if (prclClipBound != NULL)
|
|
{
|
|
RECTFX rcfx;
|
|
RECTL rclClipBound;
|
|
|
|
PATHOBJ_vGetBounds(ppo, &rcfx);
|
|
|
|
rclClipBound.top = FXTOL(rcfx.yTop);
|
|
rclClipBound.left = FXTOL(rcfx.xLeft);
|
|
rclClipBound.right = FXTOL(rcfx.xRight) + 1;
|
|
rclClipBound.bottom = FXTOL(rcfx.yBottom) + 1;
|
|
|
|
*prclClipBound = rclClipBound;
|
|
}
|
|
|
|
// Free up the path resources.
|
|
|
|
EngDeletePath(ppo);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
DBGMSG(DBG_LEVEL_ERROR, "Invalid pco->iDComplexity.\n");
|
|
return FALSE;
|
|
}
|
|
|
|
ps_clip(pdev, FALSE);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
Add1Font(
|
|
PDEVDATA pdev,
|
|
FONTOBJ *pfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Add a font to the list of downloaded fonts
|
|
|
|
Arguments:
|
|
|
|
pdev Pointer to DEVDATA structure
|
|
pfo Pointer to GDI FONTOBJ to be downloaded
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
--*/
|
|
|
|
{
|
|
if (pdev->cDownloadedFonts >= pdev->maxDLFonts) {
|
|
|
|
ps_restore(pdev, FALSE, TRUE);
|
|
ps_save(pdev, FALSE, TRUE);
|
|
|
|
// The preceding restore/save sequence wipes out the current
|
|
// font size information. We have to recalculate the point
|
|
// size here otherwise the text drawn using the current
|
|
// font will have incorrect size.
|
|
//
|
|
// NOTE!!! Other information in CGS also gets wiped out by the
|
|
// restore/save sequence. If we set their current value before
|
|
// this function is called, they will be lost.
|
|
|
|
pdev->cgs.psfxScaleFactor =
|
|
GetPointSize(pdev, pfo, &pdev->cgs.FontXform);
|
|
|
|
} else if (pdev->cDownloadedFonts == 0) {
|
|
|
|
ps_save(pdev, FALSE, TRUE);
|
|
}
|
|
|
|
pdev->cDownloadedFonts++;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
PsMakeFont(
|
|
PDEVDATA pdev,
|
|
BOOL bReencoded,
|
|
BOOL bSimItalic,
|
|
BOOL bBitmap
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Send PS code to select an appropriately sized font in the printer
|
|
|
|
Arguments:
|
|
|
|
pdev Pointer to DEVDATA structure
|
|
bReencoded Whether the font has been reencoded
|
|
bSimItalic Whether we're simulating italic font
|
|
bBitmap Whether the font is a downloaded bitmap font
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
--*/
|
|
|
|
{
|
|
psputs(pdev, "[");
|
|
|
|
if (bBitmap) {
|
|
|
|
LONG fontsize;
|
|
|
|
// All scaling done already by TT driver for bitmap fonts
|
|
|
|
fontsize = (pdev->cgs.psfxScaleFactor *
|
|
pdev->dm.dmPublic.dmPrintQuality) / PS_RESOLUTION;
|
|
|
|
psputfix(pdev, 6, fontsize, 0, 0, -fontsize, 0, 0);
|
|
|
|
} else {
|
|
|
|
FLOATOBJ m[4], fo;
|
|
INT index;
|
|
|
|
// Get font transformation matrix
|
|
|
|
FLOATOBJ_SetFloat(&m[0], pdev->cgs.FontXform.eM11);
|
|
FLOATOBJ_SetFloat(&m[1], pdev->cgs.FontXform.eM12);
|
|
FLOATOBJ_SetFloat(&m[2], pdev->cgs.FontXform.eM21);
|
|
FLOATOBJ_SetFloat(&m[3], pdev->cgs.FontXform.eM22);
|
|
|
|
// If we're simulating an italic font, then
|
|
// shear the font to the right by 18 degree:
|
|
//
|
|
// | 1 0 0 | * m
|
|
// | b 1 0 |
|
|
// | 0 0 1 |
|
|
//
|
|
// where b = -sin(18) = -0.309017
|
|
|
|
#define ITALIC_SHEAR (FLOAT) -0.309017
|
|
|
|
if (bSimItalic) {
|
|
|
|
// m[2] += b * m[0]
|
|
|
|
FLOATOBJ_SetFloat(&fo, ITALIC_SHEAR);
|
|
FLOATOBJ_Mul(&fo, &m[0]);
|
|
FLOATOBJ_Add(&m[2], &fo);
|
|
|
|
// m[3] += b * m[1]
|
|
|
|
FLOATOBJ_SetFloat(&fo, ITALIC_SHEAR);
|
|
FLOATOBJ_Mul(&fo, &m[1]);
|
|
FLOATOBJ_Add(&m[3], &fo);
|
|
}
|
|
|
|
// Multiply font transformation matrix by the emheight
|
|
|
|
for (index=0; index < 4; index++) {
|
|
|
|
FLOATOBJ_MulLong(&m[index], LTOPSFX(pdev->cgs.fwdEmHeight));
|
|
}
|
|
|
|
psputfix(pdev, 6,
|
|
FLOATOBJ_GetLong(&m[0]), FLOATOBJ_GetLong(&m[1]),
|
|
-FLOATOBJ_GetLong(&m[2]), -FLOATOBJ_GetLong(&m[3]),
|
|
0, 0);
|
|
}
|
|
|
|
psprintf(pdev, "]/%s%s MF\n",
|
|
bReencoded ? "_" : "", pdev->cgs.szFont);
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
SelectFont(
|
|
PDEVDATA pdev,
|
|
FONTOBJ *pfo,
|
|
STROBJ *pstro,
|
|
TEXTDATA *pdata
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Select the specified font as the current font in the printer.
|
|
Download the font to the printer first if it's not already there.
|
|
|
|
Arguments:
|
|
|
|
pdev Pointer to DEVDATA structure
|
|
pfo Pointer to FONTOBJ
|
|
pstro Pointer to STROBJ
|
|
pdata Pointer to TEXTDATA structure
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
PNTFM pntfm;
|
|
DLFONT *pDLFont;
|
|
BOOL boutline;
|
|
BOOL currentFontInSync;
|
|
|
|
// Get the point size, and fill in the font xform.
|
|
|
|
pdev->cgs.psfxScaleFactor = GetPointSize(pdev, pfo, &pdev->cgs.FontXform);
|
|
|
|
currentFontInSync = (pdev->cgs.lidFont == pfo->iUniq) &&
|
|
(pdev->cgs.fontsubFlag == pdata->bFontSubstitution);
|
|
|
|
if (pdata->bDeviceFont) {
|
|
|
|
// Get the font metrics for the specified font.
|
|
|
|
ASSERT(ValidPsFontIndex(pdev, pdata->iFace));
|
|
pntfm = GetPsFontNtfm(pdev, pdata->iFace);
|
|
|
|
if (! currentFontInSync) {
|
|
|
|
DWORD iFace = pdata->iFace - 1;
|
|
PSTR pFontName = (PSTR) pntfm + pntfm->ntfmsz.loszFontName;
|
|
|
|
if (! BitArrayGetBit(pdev->cgs.pFontFlags, iFace)) {
|
|
|
|
if (iFace < pdev->cDeviceFonts) {
|
|
|
|
// First time in current save context that a device font
|
|
// has been used, generate %%IncludeFont: DSC comment
|
|
|
|
DscIncludeFont(pdev, pFontName);
|
|
|
|
} else {
|
|
|
|
// If a soft font is requested and it hasn't been
|
|
// downloaded yet, then download the soft font to
|
|
// the printer and convert PFB to ASCII on the fly.
|
|
// Remember DownloadSoftFont takes a zero-based index.
|
|
|
|
Add1Font(pdev, pfo);
|
|
|
|
DscBeginFont(pdev, pFontName);
|
|
|
|
if (! DownloadSoftFont(pdev, iFace - pdev->cDeviceFonts)) {
|
|
|
|
DBGERRMSG("DownloadSoftFont");
|
|
return FALSE;
|
|
}
|
|
|
|
DscEndFont(pdev);
|
|
|
|
pDLFont = pdev->pDLFonts + pdev->cDownloadedFonts - 1;
|
|
pDLFont->iUniq = pfo->iUniq;
|
|
pDLFont->iTTUniq = 0;
|
|
pDLFont->cGlyphs = 0;
|
|
pDLFont->phgVector = NULL;
|
|
}
|
|
|
|
BitArraySetBit(pdev->cgs.pFontFlags, iFace);
|
|
}
|
|
|
|
// Indicate we have seen the font before
|
|
|
|
BitArraySetBit(pdev->pFontFlags, iFace);
|
|
|
|
// Select the font in the printer.
|
|
|
|
strcpy(pdev->cgs.szFont, pFontName);
|
|
}
|
|
|
|
// Reencode by default if not a symbol font
|
|
|
|
pdata->doReencode = TRUE;
|
|
|
|
if (pdata->iFace > pdev->cDeviceFonts) {
|
|
|
|
// Soft font
|
|
|
|
if (pntfm->flNTFM & FL_NTFM_NO_TRANSLATE_CHARSET)
|
|
pdata->doReencode= FALSE;
|
|
|
|
} else {
|
|
|
|
// Device font
|
|
|
|
PIFIMETRICS pifi;
|
|
|
|
pifi = (PIFIMETRICS) ((PBYTE) pntfm + pntfm->ntfmsz.loIFIMETRICS);
|
|
|
|
if ((pifi->jWinPitchAndFamily & 0x0f0) == FF_DECORATIVE ||
|
|
strcmp((PSTR)pntfm + pntfm->ntfmsz.loszFontName, "Symbol") == EQUAL_STRING)
|
|
{
|
|
pdata->doReencode= FALSE;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
// Must be a GDI font which will be downloaded
|
|
|
|
boutline = DownloadedAsOutline(pdev, pfo);
|
|
|
|
if (! DownloadFont(pdev, pfo, NULL, pstro, boutline)) {
|
|
DBGERRMSG("DownloadFont");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// Do setfont if different from current font
|
|
|
|
if (! currentFontInSync) {
|
|
|
|
PsMakeFont(pdev,
|
|
pdata->bDeviceFont && QueryFontRemap(pdev, pfo->iUniq),
|
|
pdata->bSimItalic,
|
|
! pdata->bDeviceFont && ! boutline);
|
|
|
|
pdev->cgs.lidFont = pfo->iUniq;
|
|
pdev->cgs.fontsubFlag = pdata->bFontSubstitution;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
ReencodeFont(
|
|
PDEVDATA pdev,
|
|
FONTOBJ *pfo,
|
|
GLYPHPOS *pgp,
|
|
ULONG cGlyphs,
|
|
TEXTDATA *pdata,
|
|
PWSTR pwstr
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Rencode a font when we have characters which don't have
|
|
standard PostScript character code. If necessary, this
|
|
routine will download a new encoding vector. It will then
|
|
reencode the current font to the new encoding vector.
|
|
|
|
Arguments:
|
|
|
|
pdev Pointer to DEVDATA structure
|
|
pfo Pointer to GDI FONTOBJ
|
|
pgp Pointer to an array of glyphs to be drawn
|
|
cGlyphs Number of glyphs in pgp or number of characters in pwstr
|
|
pdata Pointer to TEXTDATA structure
|
|
pwstr Pointer to a character string to be drawn
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
--*/
|
|
|
|
{
|
|
// Does the current font need to be remapped for this string?
|
|
|
|
if (! ShouldWeRemap(pdev, cGlyphs, pgp, pwstr, pdata->bFontSubstitution))
|
|
return;
|
|
|
|
// If the font remapping header and latin encoding vector have not
|
|
// been downloaded to the printer, do it now.
|
|
|
|
if (! (pdev->cgs.dwFlags & CGS_LATINENCODED)) {
|
|
|
|
bSendPSProcSet(pdev, PSPROC_REENCODE);
|
|
pdev->cgs.dwFlags |= CGS_LATINENCODED;
|
|
}
|
|
|
|
// Output the PostScript commands to reencode the current font
|
|
// using the proper encoding vector, if the current font has
|
|
// not already been reencoded.
|
|
|
|
if (QueryFontRemap(pdev, pdev->cgs.lidFont))
|
|
return;
|
|
|
|
psprintf(pdev, "LATENC /_%s /%s reencode\n",
|
|
pdev->cgs.szFont, pdev->cgs.szFont);
|
|
|
|
// Select the newly reencoded font.
|
|
|
|
pdev->cgs.psfxScaleFactor = GetPointSize(pdev, pfo, &pdev->cgs.FontXform);
|
|
|
|
PsMakeFont(pdev, TRUE,
|
|
pdata->bSimItalic,
|
|
(pfo->flFontType & TRUETYPE_FONTTYPE) &&
|
|
! pdata->bFontSubstitution &&
|
|
! DownloadedAsOutline(pdev, pfo));
|
|
|
|
// Indicate that the current font has been reencoded
|
|
|
|
SetFontRemap(pdev, pdev->cgs.lidFont);
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
RemapDeviceChars(
|
|
PDEVDATA pdev,
|
|
DWORD cChars,
|
|
CHAR *pch
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Display a character on the printer
|
|
|
|
Arguments:
|
|
|
|
pdev Pointer to DEVDATA structure
|
|
cChars Number of characters
|
|
pch Pointer to array of character to be drawn
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
CHAR ch, buffer[4];
|
|
DWORD nchar;
|
|
|
|
while (cChars--) {
|
|
|
|
ch = *pch++;
|
|
|
|
if (ch == '(' || ch == ')' || ch == '\\') {
|
|
|
|
// Precede each of these special characters with a backslash
|
|
|
|
buffer[0] = '\\';
|
|
buffer[1] = ch;
|
|
nchar = 2;
|
|
|
|
} else if (B_PRINTABLE(ch)) {
|
|
|
|
// Output printable characters directly
|
|
|
|
buffer[0] = ch;
|
|
nchar = 1;
|
|
|
|
} else {
|
|
|
|
// Convert non-printable character to octal representation
|
|
|
|
buffer[0] = '\\';
|
|
buffer[1] = ((ch >> 6) & 3) + '0';
|
|
buffer[2] = ((ch >> 3) & 7) + '0';
|
|
buffer[3] = (ch & 7) + '0';
|
|
nchar = 4;
|
|
}
|
|
|
|
if (! pswrite(pdev, buffer, nchar))
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
FindDownloadedGlyph(
|
|
DLFONT *pDLFont,
|
|
HGLYPH hGlyph
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert a glyph handle of a downloaded GDI font to its
|
|
corresponding character code
|
|
|
|
Arguments:
|
|
|
|
pDLFont Pointer to DLFONT structure
|
|
hGlyph Glyph handle
|
|
|
|
Return Value:
|
|
|
|
Index of the specified glyph handle within the array of downloaded
|
|
glyphs. Otherwise, INVALID_GLYPH_INDEX is returned if the specified
|
|
glyph hasn't been downloaded.
|
|
|
|
--*/
|
|
|
|
#define INVALID_GLYPH_INDEX 0xffff
|
|
|
|
{
|
|
DWORD index;
|
|
HGLYPH *phGlyph;
|
|
|
|
ASSERT(pDLFont != NULL && pDLFont->phgVector != NULL);
|
|
|
|
for (index=0, phGlyph = pDLFont->phgVector;
|
|
index < pDLFont->cGlyphs && *phGlyph != hGlyph;
|
|
index++, phGlyph++)
|
|
{
|
|
}
|
|
|
|
return (index < pDLFont->cGlyphs) ? index : INVALID_GLYPH_INDEX;
|
|
}
|
|
|
|
|
|
|
|
FIX
|
|
GetGlyphAdvanceWidth(
|
|
FONTOBJ *pfo,
|
|
HGLYPH hGlyph
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Calculate the advance width of a specified glyph
|
|
|
|
Arguments:
|
|
|
|
pfo Pointer to FONTOBJ
|
|
hGlyph Handle to the specified glyph
|
|
|
|
Return Value:
|
|
|
|
Glyph advance width
|
|
|
|
--*/
|
|
|
|
{
|
|
GLYPHDATA *pGlyphData;
|
|
|
|
// NOTE!!! Even though we have no interest in the bitmap here,
|
|
// we must get it in order to retrieve glyph metrics info.
|
|
|
|
if (! FONTOBJ_cGetGlyphs(pfo, FO_GLYPHBITS, 1, &hGlyph, &pGlyphData)) {
|
|
DBGERRMSG("FONTOBJ_cGetGlyphs");
|
|
return 0;
|
|
}
|
|
|
|
// We're not prepared to deal with negative advance width
|
|
|
|
if (pGlyphData->fxD < 0) {
|
|
DBGMSG(DBG_LEVEL_ERROR, "Invalid character increment.\n");
|
|
}
|
|
|
|
return pGlyphData->fxD;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
DrawGDIGlyphs(
|
|
PDEVDATA pdev,
|
|
FONTOBJ *pfo,
|
|
DLFONT *pDLFont,
|
|
DWORD cGlyphs,
|
|
GLYPHPOS *pGlyphPos,
|
|
PSTR charStr,
|
|
BOOL bPositionEachChar
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Display a number of glyphs on the printer using a downloaded GDI font.
|
|
This is only used when some of the glyphs must be displayed by
|
|
drawing the bitmap on the printer.
|
|
|
|
Arguments:
|
|
|
|
pdev Pointer to DEVDATA structure
|
|
pfo Pointer to FONTOBJ
|
|
pDLFont Pointer to DLFONT structure corresponding to pfo
|
|
cGlyphs Number of glyphs to display
|
|
pGlyphPos Pointer to array of GLYPHPOS's
|
|
charStr Pointer to array of character codes
|
|
bPositionEachChar Whether characters are individually positioned
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD count;
|
|
|
|
for (count=1; count <= cGlyphs; count++, pGlyphPos++, charStr++) {
|
|
|
|
// Position each character separately if needed
|
|
// Or if we're display the very last glyph
|
|
|
|
if (bPositionEachChar || count == 1) {
|
|
ps_moveto(pdev, &pGlyphPos->ptl);
|
|
psputs(pdev, "(");
|
|
}
|
|
|
|
// If the character is NOT part of the downloaded font,
|
|
// then we have to draw the character bitmap manually.
|
|
|
|
if (*charStr == NUL &&
|
|
FindDownloadedGlyph(pDLFont, pGlyphPos->hg) == INVALID_GLYPH_INDEX)
|
|
{
|
|
// Make sure we have a valid glyph handle
|
|
|
|
if (pGlyphPos->hg == HGLYPH_INVALID) {
|
|
DBGMSG(DBG_LEVEL_ERROR, "Invalid glyph handle.\n");
|
|
return FALSE;
|
|
}
|
|
|
|
// Force any characters in the current string to be shown
|
|
|
|
psputs(pdev, ")t\n");
|
|
|
|
// Draw the character - only bitmap fonts are supported.
|
|
|
|
CharBitmap(pdev, pGlyphPos);
|
|
|
|
// Advance the current point and start a new string
|
|
|
|
if (! bPositionEachChar) {
|
|
|
|
// !!! We assume ptl field of every GLYPHPOS is valid
|
|
// no matter SO_FLAG_DEFAULT_PLACEMENT bit of flAccel
|
|
// is set or not set. This assumption is currently
|
|
// valid and gives us SIGNIFICANT performance gain over
|
|
// calling FONTOBJ_cGetGlyphs.
|
|
|
|
if (count >= cGlyphs) {
|
|
|
|
psputs(pdev, "0 0 rm");
|
|
|
|
} else {
|
|
|
|
psprintf(pdev, "%d %d rm",
|
|
pGlyphPos[1].ptl.x - pGlyphPos[0].ptl.x,
|
|
pGlyphPos[1].ptl.y - pGlyphPos[0].ptl.y);
|
|
}
|
|
}
|
|
|
|
psputs(pdev, "(");
|
|
|
|
} else {
|
|
|
|
// If the character is part of the downloaded font, then
|
|
// use its glyph index as the device character code.
|
|
|
|
if (! RemapDeviceChars(pdev, 1, charStr)) {
|
|
DBGERRMSG("RemapDeviceChars");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// Show each character separately if needed
|
|
// Or if we're display the very last glyph
|
|
|
|
if (bPositionEachChar || count == cGlyphs)
|
|
psputs(pdev, ")t\n");
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
DrawGlyphs(
|
|
PDEVDATA pdev,
|
|
DWORD cGlyphs,
|
|
GLYPHPOS *pGlyphPos,
|
|
FONTOBJ *pfo,
|
|
STROBJ *pstro,
|
|
TEXTDATA *pdata,
|
|
PWSTR pwstr
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Display a set of glyphs on the printer
|
|
|
|
Arguments:
|
|
|
|
pdev Pointer to DEVDATA structure
|
|
cGlyphs Number of glyphs to drawn
|
|
pGlyphPos Pointer to an array of GLYPHPOS's
|
|
pfo Pointer to FONTOBJ
|
|
pstro Pointer to STROBJ
|
|
pdata Pointer to TEXTDATA structure
|
|
pwstr Pointer to the string to be drawn (if using substituted font)
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD count;
|
|
GLYPHPOS *pgp;
|
|
PSTR charStr, pch;
|
|
BOOL bPositionEachChar, bCharBitmap;
|
|
BOOL bResult = TRUE;
|
|
DLFONT *pDLFont;
|
|
|
|
// Quick exit if there is nothing to do
|
|
|
|
if (cGlyphs == 0)
|
|
return TRUE;
|
|
|
|
// Reencode the font if necessary
|
|
|
|
if (pdata->bDeviceFont && pdata->doReencode)
|
|
ReencodeFont(pdev, pfo, pGlyphPos, cGlyphs, pdata, pwstr);
|
|
|
|
// Decide whether each character in the string is positioned
|
|
// individually. If not, we only need to position once and
|
|
// let the printer take care of the rest.
|
|
|
|
bPositionEachChar =
|
|
(cGlyphs > 1) && ! (pdata->flAccel & SO_FLAG_DEFAULT_PLACEMENT);
|
|
|
|
// Assume we don't have to download character bitmaps
|
|
|
|
bCharBitmap = FALSE;
|
|
|
|
// Allocate memory to hold character codes
|
|
|
|
if ((pch = charStr = MEMALLOC(cGlyphs * sizeof(CHAR))) == NULL) {
|
|
DBGERRMSG("MEMALLOC");
|
|
return FALSE;
|
|
}
|
|
|
|
// Convert glyph handles to character codes
|
|
|
|
if (! pdata->bDeviceFont) {
|
|
|
|
DWORD glyphIndex;
|
|
|
|
// For downloaded GDI font, to convert a glyph handle
|
|
// to character code, we search the array of downloaded
|
|
// glyph handles. If the given handle is found, its index
|
|
// in the array is used as the character code. Otherwise,
|
|
// we have to send the glyph to the printer as bitmap.
|
|
|
|
// Find the DLFont structure corresponding to the GDI font
|
|
|
|
pDLFont = FindDownloadedFont(pdev, pfo, DownloadedAsOutline(pdev,pfo));
|
|
ASSERT(pDLFont != NULL);
|
|
|
|
for (count=0, pgp = pGlyphPos; count < cGlyphs; count++, pgp++) {
|
|
|
|
glyphIndex = FindDownloadedGlyph(pDLFont, pgp->hg);
|
|
|
|
if (glyphIndex == INVALID_GLYPH_INDEX) {
|
|
*pch++ = NUL;
|
|
bCharBitmap = TRUE;
|
|
} else
|
|
*pch++ = (CHAR) glyphIndex;
|
|
}
|
|
|
|
} else if (pdata->bFontSubstitution) {
|
|
|
|
// For substituted TrueType fonts, character codes
|
|
// are converted from Unicode string
|
|
|
|
UNICODETOMULTIBYTE(pch, cGlyphs*sizeof(CHAR), NULL,
|
|
pwstr, cGlyphs*sizeof(WCHAR));
|
|
} else {
|
|
|
|
// For device font, glyph handles are used directly
|
|
// as character codes
|
|
|
|
for (count=0, pgp = pGlyphPos; count < cGlyphs; count++, pgp++)
|
|
*pch++ = (CHAR) pgp->hg;
|
|
}
|
|
|
|
if (bCharBitmap) {
|
|
|
|
// If we're using a previously downloaded bitmap font
|
|
// and we need to download character bitmaps, then call
|
|
// another function to do the dirty work.
|
|
|
|
bResult = DrawGDIGlyphs(pdev, pfo, pDLFont, cGlyphs, pGlyphPos,
|
|
charStr, bPositionEachChar);
|
|
|
|
} else if (bPositionEachChar ||
|
|
(pdata->bFontSubstitution &&
|
|
(pdev->pPrinterData->dwFlags & PSDEV_SLOW_FONTSUBST)))
|
|
{
|
|
// We're told to position each character individually, or
|
|
// if we're substituting a TrueType font with a device font.
|
|
//
|
|
// In the latter case, we must adjust the inter-character
|
|
// spacing to compensate the difference between TrueType
|
|
// and device font metrics. Even though we'd like to use
|
|
// ashow here, but it's difficult to calculate the string
|
|
// width on the printer. So we position each character
|
|
// individually instead.
|
|
//
|
|
// !!! We assume ptl field of every GLYPHPOS is valid no matter
|
|
// SO_FLAG_DEFAULT_PLACEMENT bit of flAccel is set or not set.
|
|
|
|
// Intentionally disable level 2 features
|
|
|
|
if (FALSE && Level2Device(pdev->hppd)) {
|
|
|
|
LONG y0;
|
|
|
|
// On level 2 devices, output x- and/or y- displacements
|
|
// as a numarray and use the xyshow/xshow operator.
|
|
|
|
// Move to the starting position of the first character
|
|
|
|
ps_moveto(pdev, &pGlyphPos->ptl);
|
|
|
|
// Output the entire character string
|
|
|
|
psputs(pdev, "(");
|
|
bResult = RemapDeviceChars(pdev, cGlyphs, charStr);
|
|
psputs(pdev, ")\n[");
|
|
|
|
// Decide whether we should use xyshow or xshow
|
|
|
|
for (count=1, y0 = pGlyphPos[0].ptl.y;
|
|
count < cGlyphs && pGlyphPos[count].ptl.y == y0;
|
|
count++)
|
|
{
|
|
}
|
|
|
|
if (count >= cGlyphs) {
|
|
|
|
// All y-coordinates are the same - Use xshow.
|
|
|
|
for (count=1; count < cGlyphs; count++, pGlyphPos++) {
|
|
|
|
psprintf(pdev, "%d%c",
|
|
pGlyphPos[1].ptl.x - pGlyphPos[0].ptl.x,
|
|
(count & 31) ? ' ' : '\n');
|
|
}
|
|
|
|
psputs(pdev, "1]X\n");
|
|
|
|
} else {
|
|
|
|
// Y-coordinates are not all the same - Use xyshow.
|
|
|
|
for (count=1; count < cGlyphs; count++, pGlyphPos++) {
|
|
|
|
psprintf(pdev, "%d %d%c",
|
|
pGlyphPos[1].ptl.x - pGlyphPos[0].ptl.x,
|
|
pGlyphPos[1].ptl.y - pGlyphPos[0].ptl.y,
|
|
(count & 15) ? ' ' : '\n');
|
|
}
|
|
|
|
psputs(pdev, "1 0]XY\n");
|
|
}
|
|
|
|
} else {
|
|
|
|
// On level 1 devices, use moveto-show combination
|
|
// to individually position and display each character
|
|
|
|
for (count = 1, pch = charStr;
|
|
count <= cGlyphs && bResult;
|
|
count++, pGlyphPos++)
|
|
{
|
|
psputs(pdev, "(");
|
|
bResult = RemapDeviceChars(pdev, 1, pch++);
|
|
psprintf(pdev, ")%d %d MS%c",
|
|
pGlyphPos->ptl.x, pGlyphPos->ptl.y,
|
|
((count & 15) && count != cGlyphs) ? ' ' : '\n');
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
// The optimal case: We're using a device font and don't
|
|
// have to position individual characters. Output the entire
|
|
// character string and simply "show" it.
|
|
|
|
psputs(pdev, "(");
|
|
bResult = RemapDeviceChars(pdev, cGlyphs, charStr);
|
|
psputs(pdev, ")");
|
|
|
|
psprintf(pdev, "%d %d MS\n", pGlyphPos->ptl.x, pGlyphPos->ptl.y);
|
|
}
|
|
|
|
MEMFREE(charStr);
|
|
return bResult;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
ShouldWeRemap(
|
|
PDEVDATA pdev,
|
|
ULONG cGlyphs,
|
|
GLYPHPOS *pgp,
|
|
PWSTR pwstr,
|
|
BOOL bFontSubstitution
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determine if the current font needs to be reencoded in order
|
|
to output a given string or a set of glyphs
|
|
|
|
Arguments:
|
|
|
|
pdev Pointer to DEVDATA structure
|
|
cGlyphs Number of characters or glyphs to be drawn
|
|
pgp Pointer to GLYPHPOS for specifying the set of glyphs
|
|
pwstr Pointer to the string to be drawn
|
|
bFontSubstition Whether this is a device font or a substituted font
|
|
|
|
Return Value:
|
|
|
|
TRUE if reencoding is needed, FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
BYTE j;
|
|
|
|
if (bFontSubstitution)
|
|
{
|
|
// For a substituted font, pwstr parameter points to
|
|
// the character string to be drawn
|
|
|
|
while (cGlyphs--)
|
|
{
|
|
UNICODETOMULTIBYTE(&j, sizeof(BYTE), NULL, pwstr, sizeof(WCHAR));
|
|
|
|
if (!B_PRINTABLE(j))
|
|
return TRUE;
|
|
|
|
pwstr++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// For a device font, pgp parameter specifies the
|
|
// set of glyphs to be drawn.
|
|
|
|
while (cGlyphs--)
|
|
{
|
|
j = (BYTE)pgp->hg;
|
|
if (!B_PRINTABLE(j))
|
|
return TRUE;
|
|
pgp++;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
CharBitmap(
|
|
PDEVDATA pdev,
|
|
GLYPHPOS *pgp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Download a character bitmap to the printer
|
|
|
|
Arguments:
|
|
|
|
pdev Pointer to DEVDATA structure
|
|
pgp Pointer to GLYPHPOS structure for the downloaded character
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
--*/
|
|
|
|
{
|
|
GLYPHBITS *pgb;
|
|
LONG cx, cy;
|
|
|
|
// Figure out the bitmap origin and size
|
|
|
|
pgb = pgp->pgdf->pgb;
|
|
psprintf(pdev,
|
|
"gsave %d %d rm a translate\n", pgb->ptlOrigin.x, pgb->ptlOrigin.y);
|
|
|
|
cx = pgb->sizlBitmap.cx;
|
|
cy = pgb->sizlBitmap.cy;
|
|
|
|
psprintf(pdev, "%d %d scale\n", cx, cy);
|
|
|
|
// Output the image operator and the scan data. true means to
|
|
// paint the '1' bits with the foreground color.
|
|
|
|
psprintf(pdev, "%d %d true [%d 0 0 %d 0 0]\n", cx, cy, cx, cy);
|
|
|
|
// Output glyph bitmaps
|
|
|
|
OutputGlyphBitmap(pdev, pgb);
|
|
|
|
psputs(pdev, "im grestore\n");
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
SetFontRemap(
|
|
PDEVDATA pdev,
|
|
DWORD iFontID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Add the specified font to the list of reencoded font
|
|
|
|
Arguments:
|
|
|
|
pdev Pointer to DEVDATA structure
|
|
iFontID Index of the specified font
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
FREMAP *pfremap = &pdev->cgs.FontRemap;
|
|
|
|
if (pfremap->pNext)
|
|
{
|
|
// Find the end of the list
|
|
|
|
while (pfremap->pNext)
|
|
pfremap = (PFREMAP)pfremap->pNext;
|
|
|
|
// Allocate memory for the new list item
|
|
|
|
if ((pfremap->pNext = HEAPALLOC(pdev->hheap, sizeof(FREMAP))) == NULL)
|
|
{
|
|
DBGERRMSG("HEAPALLOC");
|
|
return FALSE;
|
|
}
|
|
|
|
pfremap = (PFREMAP)pfremap->pNext;
|
|
}
|
|
|
|
// Fill in the fields of the new list item
|
|
|
|
pfremap->iFontID = iFontID;
|
|
pfremap->pNext = NULL;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
QueryFontRemap(
|
|
PDEVDATA pdev,
|
|
DWORD iFontID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Find out if the specified font has been reencoded
|
|
|
|
Arguments:
|
|
|
|
pdev Pointer to DEVDATA structure
|
|
iFontID Index of the font in question
|
|
|
|
Return Value:
|
|
|
|
TRUE if the font has been reencoded, FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
FREMAP *pfremap = &pdev->cgs.FontRemap;
|
|
|
|
do {
|
|
if (pfremap->iFontID == iFontID)
|
|
return TRUE;
|
|
|
|
pfremap = (PFREMAP) pfremap->pNext;
|
|
|
|
} while (pfremap != NULL);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
CanBeSubstituted(
|
|
STROBJ *pstro
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determine if a STROBJ can be substituted using device font
|
|
|
|
Arguments:
|
|
|
|
pstro - Specifies the STROBJ in question
|
|
|
|
Return Value:
|
|
|
|
TRUE if the STROBJ can be substituted, FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
PWSTR pwch;
|
|
DWORD cch, codePage;
|
|
|
|
//
|
|
// Unicode values corresponding to ANSI characters 80-9f
|
|
//
|
|
|
|
static WCHAR MyCodeTable[32] = {
|
|
|
|
/* 0x80 */ 0x0080,
|
|
/* 0x81 */ 0x0081,
|
|
/* 0x8d */ 0x008d,
|
|
/* 0x8e */ 0x008e,
|
|
/* 0x8f */ 0x008f,
|
|
/* 0x90 */ 0x0090,
|
|
/* 0x9d */ 0x009d,
|
|
/* 0x9e */ 0x009e,
|
|
/* 0x8c */ 0x0152, // Latin Capital Ligature Oe
|
|
/* 0x9c */ 0x0153, // Latin Small Ligature Oe
|
|
/* 0x8a */ 0x0160, // Latin Capital Letter S With Caron
|
|
/* 0x9a */ 0x0161, // Latin Small Letter S With Caron
|
|
/* 0x9f */ 0x0178, // Latin Capital Letter Y With Diaeresis
|
|
/* 0x83 */ 0x0192, // Latin Small Letter F With Hook
|
|
/* 0x88 */ 0x02c6, // Modifier Letter Circumflex Accent
|
|
/* 0x98 */ 0x02dc, // Small Tilde
|
|
/* 0x96 */ 0x2013, // En Dash
|
|
/* 0x97 */ 0x2014, // Em Dash
|
|
/* 0x91 */ 0x2018, // Left Single Quotation Mark
|
|
/* 0x92 */ 0x2019, // Right Single Quotation Mark
|
|
/* 0x82 */ 0x201a, // Single Low-9 Quotation Mark
|
|
/* 0x93 */ 0x201c, // Left Double Quotation Mark
|
|
/* 0x94 */ 0x201d, // Right Double Quotation Mark
|
|
/* 0x84 */ 0x201e, // Double Low-9 Quotation Mark
|
|
/* 0x86 */ 0x2020, // Dagger
|
|
/* 0x87 */ 0x2021, // Double Dagger
|
|
/* 0x95 */ 0x2022, // Bullet
|
|
/* 0x85 */ 0x2026, // Horizontal Ellipsis
|
|
/* 0x89 */ 0x2030, // Per Mille Sign
|
|
/* 0x8b */ 0x2039, // Single Left-Pointing Angle Quotation Mark
|
|
/* 0x9b */ 0x203a, // Single Right-Pointing Angle Quotation Mark
|
|
/* 0x99 */ 0x2122, // Trade Mark Sign
|
|
};
|
|
|
|
//
|
|
// Go through the Unicode characters and find out if
|
|
// all of them belong to code page 1252.
|
|
//
|
|
|
|
if ((codePage = STROBJ_dwGetCodePage(pstro)) == 0) {
|
|
|
|
if (pwch = pstro->pwszOrg) {
|
|
|
|
//
|
|
// Go through each Unicode character and
|
|
// see if it belong to code page 1252
|
|
//
|
|
|
|
for (cch=pstro->cGlyphs; cch--; pwch++) {
|
|
|
|
//
|
|
// If the Unicode value is between 00 - 7f and a0-ff,
|
|
// they belong to code page 1252. Otherwise, we have
|
|
// to do a slower search.
|
|
//
|
|
|
|
if ((*pwch & 0xff00) || (*pwch & 0xe0) == 0x80) {
|
|
|
|
INT low, high, mid;
|
|
|
|
low = 0;
|
|
high = 31;
|
|
|
|
while (low <= high) {
|
|
|
|
mid = (low + high) >> 1;
|
|
|
|
if (*pwch == MyCodeTable[mid])
|
|
break;
|
|
else if (*pwch < MyCodeTable[mid])
|
|
high = mid - 1;
|
|
else
|
|
low = mid + 1;
|
|
}
|
|
|
|
if (low > high)
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return codePage == 1252;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
SubstituteIFace(
|
|
PDEVDATA pdev,
|
|
FONTOBJ *pfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Substitute a TrueType font with a device font or soft font
|
|
|
|
Arguments:
|
|
|
|
pdev Pointer to DEVDATA structure
|
|
pfo Pointer to TrueType FONTOBJ to be substituted
|
|
|
|
Return Value:
|
|
|
|
one-based device or soft font index if there is a substitution
|
|
for the TrueType font. 0 otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
CHAR strDevFont[MAX_FONT_NAME];
|
|
IFIMETRICS *pifiTT;
|
|
PWSTR pwstr, pwstrTT;
|
|
WORD index;
|
|
PDEVFONT pDevFont;
|
|
|
|
// Get the TrueType font name from the IFIMETRICS structure.
|
|
|
|
if ((pifiTT = FONTOBJ_pifi(pfo)) == NULL) {
|
|
DBGERRMSG("FONTOBJ_pifi");
|
|
return 0;
|
|
}
|
|
|
|
pwstrTT = (PWSTR)((BYTE *)pifiTT + pifiTT->dpwszFaceName);
|
|
|
|
// Search the font substitution table and find out if there is
|
|
// a mapping for the TrueType font in question
|
|
|
|
pwstr = FindTrueTypeSubst(pdev->pTTSubstTable, pwstrTT);
|
|
|
|
// If there is no mapping found, return 0
|
|
|
|
if (pwstr == NULL)
|
|
return 0;
|
|
|
|
// Convert device font name from Unicode to ANSI
|
|
|
|
CopyUnicode2Str(strDevFont, pwstr, MAX_FONT_NAME);
|
|
|
|
// At this point we have a mapping between a TrueType font name,
|
|
// and a device font name. We need to map the device font name
|
|
// to a device font index.
|
|
|
|
pDevFont = (PDEVFONT)
|
|
PpdFindUiOptionWithXlation(
|
|
(PUIOPTION) pdev->hppd->pFontList, strDevFont, &index);
|
|
|
|
if (pDevFont != NULL) {
|
|
|
|
// If we found a font whose name matches the substituted
|
|
// font name, then select it in the current graphics state
|
|
// and return its index.
|
|
|
|
CopyStringA(pdev->cgs.szFont, pDevFont->pName, MAX_FONT_NAME);
|
|
|
|
// Remember the index returned by PpdFindUiOptionWithXlation
|
|
// is zero-based, while the device font index is one-based.
|
|
|
|
return index + 1;
|
|
}
|
|
|
|
// The substituted font name is not a known device font. We'll
|
|
// ignore it and act as if there is no substitution.
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
LONG
|
|
iHipot(
|
|
LONG x,
|
|
LONG y
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Computes the hypoteneous of a right triangle
|
|
|
|
Arguments:
|
|
|
|
x, y Edges of the right triangle
|
|
|
|
[Note:]
|
|
|
|
Solve sq(x) + sq(y) = sq(hypo)
|
|
Start with MAX(x, y),
|
|
Use sq(x + 1) = sq(x) + 2x + 1 to incrementally
|
|
get to the target hypotenouse.
|
|
|
|
Return Value:
|
|
|
|
Hypoteneous of a right triangle
|
|
|
|
--*/
|
|
|
|
{
|
|
INT hypo; // Value to calculate
|
|
INT delta; // Used in the calculation loop
|
|
INT target; // Loop limit factor
|
|
|
|
// Quick exit for frequent trivial cases [bodind]
|
|
|
|
if (x == 0)
|
|
return abs(y);
|
|
|
|
if (y == 0)
|
|
return abs(x);
|
|
|
|
if (x < 0)
|
|
x = -x;
|
|
|
|
if (y < 0)
|
|
y = -y;
|
|
|
|
if(x > y)
|
|
{
|
|
hypo = x;
|
|
target = y * y;
|
|
}
|
|
else
|
|
{
|
|
hypo = y;
|
|
target = x * x;
|
|
}
|
|
|
|
for (delta = 0; delta < target; hypo++)
|
|
delta += (hypo << 1) + 1;
|
|
|
|
return hypo;
|
|
}
|