#include "FontCID.h" //----- CIDTypeFontDef вспомогательные функции ------------------------------------------------------ void CIDFontDefFreeWidth (FontDef pFontDef) { CIDFontDefAttr pAttr = (CIDFontDefAttr)pFontDef->pAttr; for ( unsigned int nIndex = 0; nIndex < pAttr->pW->nCount; nIndex++) { CIDWidth *pW = (CIDWidth *)ListItemAt( pAttr->pW, nIndex); FreeMem( pFontDef->oMMgr, pW); } ListFree( pAttr->pW ); pAttr->pW = NULL; pFontDef->bValid = false; } void CIDFontDefFreeFunc (FontDef pFontDef) { CIDFontDefAttr pAttr = (CIDFontDefAttr)pFontDef->pAttr; if ( pAttr->pFontData ) { StreamFree( pAttr->pFontData ); pAttr->pFontData = NULL; } if ( pAttr->pCIDtoGIDMap ) { StreamFree( pAttr->pCIDtoGIDMap ); pAttr->pCIDtoGIDMap = NULL; } CIDFontDefFreeWidth( pFontDef ); FreeMem( pFontDef->oMMgr, pAttr); } //----- CIDTypeFontDef основные функции ------------------------------------------------------------- FontDef CIDFontDefNew (MMgr oMMgr, char *sName, FontDefInitFunc pInitFn) { if ( !oMMgr ) return NULL; FontDef pFontDef = (FontDef)GetMem( oMMgr, sizeof(FontDefRec) ); if ( !pFontDef ) return NULL; UtilsMemSet( pFontDef, 0, sizeof(FontDefRec) ); pFontDef->nSigBytes = FONTDEF_SIG_BYTES; UtilsStrCpy( pFontDef->sBaseFont, sName, pFontDef->sBaseFont + LIMIT_MAX_NAME_LEN); pFontDef->oMMgr = oMMgr; pFontDef->oError = oMMgr->oError; pFontDef->eType = FontDefCID; pFontDef->pFreeFn = CIDFontDefFreeFunc; pFontDef->pInitFn = pInitFn; pFontDef->bValid = false; CIDFontDefAttr pFontDefAttr = (CIDFontDefAttr)GetMem( oMMgr, sizeof(CIDFontDefAttrRec) ); if ( !pFontDefAttr ) { FreeMem( pFontDef->oMMgr, pFontDef ); return NULL; } pFontDef->pAttr = pFontDefAttr; UtilsMemSet( (BYTE *)pFontDefAttr, 0, sizeof(CIDFontDefAttrRec) ); pFontDefAttr->pW = ListNew( oMMgr, DEF_CHAR_WIDTHS_NUM ); if ( !pFontDefAttr->pW ) { FreeMem( pFontDef->oMMgr, pFontDef); FreeMem( pFontDef->oMMgr, pFontDefAttr); return NULL; } pFontDef->nMissingWidth = 500; pFontDefAttr->nDW = 1000; pFontDefAttr->anDW2[0] = 880; pFontDefAttr->anDW2[1] = -1000; pFontDefAttr->pW2 = ListNew( oMMgr, DEF_CHAR_WIDTHS_NUM ); if ( !pFontDefAttr->pW2 ) { ListFree( pFontDefAttr->pW ); FreeMem( pFontDef->oMMgr, pFontDef); FreeMem( pFontDef->oMMgr, pFontDefAttr); return NULL; } pFontDefAttr->nCIDtoGIDtype = -1; pFontDefAttr->pCIDtoGIDMap = NULL; return pFontDef; } unsigned long CIDFontDefLoadFontData(FontDef pFontDef, std::wstring wsFilePath, CIDFontFileType eCIDType) { if ( !pFontDef || FontDefCID != pFontDef->eType ) return OK; CIDFontDefAttr pAttr = (CIDFontDefAttr)pFontDef->pAttr; pAttr->eType = eCIDType; if ( wsFilePath.length() < 1 )return S_OK; CFile oFile; if (oFile.OpenFile(std_string2string(wsFilePath)) != S_OK) return AVS_OFFICEPDFWRITER_ERROR_FILE_OPEN_ERROR; ULONG64 fileSize = oFile.GetFileSize(); BYTE *fileContent = new BYTE[fileSize]; if (!fileContent) { oFile.CloseFile(); return AVS_OFFICEPDFWRITER_ERROR_FILE_OPEN_ERROR; } oFile.ReadFile(fileContent, fileSize); oFile.CloseFile(); if ( pAttr->pFontData ) StreamFree( pAttr->pFontData ); pAttr->pFontData = TempFileStreamNew( pFontDef->oMMgr ); if ( !pAttr->pFontData ) { delete []fileContent; return AVS_OFFICEPDFWRITER_ERROR_NOT_ENOUGH_MEMORY; } StreamWrite( pAttr->pFontData, fileContent, fileSize, true ); delete []fileContent; return S_OK; } void CIDFontDefSetInfoSys (FontDef pFontDef, char *sRegistry, char *sOrdering, int nSupplement) { if ( !pFontDef || FontDefCID != pFontDef->eType ) return; CIDFontDefAttr pAttr = (CIDFontDefAttr)pFontDef->pAttr; UtilsStrCpy( pAttr->sRegistry, sRegistry, pAttr->sRegistry + LIMIT_MAX_NAME_LEN ); UtilsStrCpy( pAttr->sOrdering, sOrdering, pAttr->sOrdering + LIMIT_MAX_NAME_LEN ); pAttr->nSupplement = nSupplement; } void CIDFontDefSetDW (FontDef pFontDef, int nDW) { if ( !pFontDef || FontDefCID != pFontDef->eType ) return; CIDFontDefAttr pAttr = (CIDFontDefAttr)pFontDef->pAttr; pAttr->nDW = nDW; } void CIDFontDefSetDW2 (FontDef pFontDef, int anDW2[2]) { if ( !pFontDef || FontDefCID != pFontDef->eType ) return; CIDFontDefAttr pAttr = (CIDFontDefAttr)pFontDef->pAttr; pAttr->anDW2[0] = anDW2[0]; pAttr->anDW2[1] = anDW2[1]; } short CIDFontDefGetCIDWidth (FontDef pFontDef, unsigned short nCid) { CIDFontDefAttr pAttr = (CIDFontDefAttr)pFontDef->pAttr; for ( unsigned int nIndex = 0; nIndex < pAttr->pW->nCount; nIndex++) { CIDWidth *pW = (CIDWidth *)ListItemAt( pAttr->pW, nIndex); if ( nCid >= pW->unCid_First && nCid <= pW->unCid_Last ) return pW->shWidth; } // Не найден в массиве PDF_CID return pAttr->nDW; } unsigned long CIDFontDefAddWidth (FontDef pFontDef, const CIDWidth *pWidths) { CIDFontDefAttr pAttr = (CIDFontDefAttr)pFontDef->pAttr; while ( 0xFFFF != pWidths->unCid_First ) { CIDWidth *pCurWidth = (CIDWidth*)GetMem( pFontDef->oMMgr, sizeof (CIDWidth) ); unsigned long nRet = OK; if ( !pCurWidth ) return pFontDef->oError->nErrorNo; pCurWidth->unCid_First = pWidths->unCid_First; pCurWidth->unCid_Last = pWidths->unCid_Last; pCurWidth->shWidth = pWidths->shWidth; if ( OK != ( nRet = ListAdd( pAttr->pW, pCurWidth ) ) ) { FreeMem( pFontDef->oMMgr, pCurWidth); return nRet; } pWidths++; } return OK; } unsigned long CIDFontDefAddWidth (FontDef pFontDef, CIDWidth oWidth) { CIDFontDefAttr pAttr = (CIDFontDefAttr)pFontDef->pAttr; CIDWidth *pCurWidth = (CIDWidth*)GetMem( pFontDef->oMMgr, sizeof (CIDWidth) ); unsigned long nRet = OK; if ( !pCurWidth ) return pFontDef->oError->nErrorNo; pCurWidth->unCid_First = oWidth.unCid_First; pCurWidth->unCid_Last = oWidth.unCid_Last; pCurWidth->shWidth = oWidth.shWidth; if ( OK != ( nRet = ListAdd( pAttr->pW, pCurWidth ) ) ) { FreeMem( pFontDef->oMMgr, pCurWidth); return nRet; } return OK; } unsigned long CIDFontDefAddWidth2 (FontDef pFontDef, const CIDWidth2 *pWidths) { CIDFontDefAttr pAttr = (CIDFontDefAttr)pFontDef->pAttr; while ( 0xFFFF != pWidths->unCid_First ) { CIDWidth2 *pCurWidth = (CIDWidth2*)GetMem( pFontDef->oMMgr, sizeof (CIDWidth2) ); unsigned long nRet = OK; if ( !pCurWidth ) return pFontDef->oError->nErrorNo; pCurWidth->unCid_First = pWidths->unCid_First; pCurWidth->unCid_Last = pWidths->unCid_Last; pCurWidth->shW = pWidths->shW; pCurWidth->shVx = pWidths->shVx; pCurWidth->shVy = pWidths->shVy; if ( OK != ( nRet = ListAdd( pAttr->pW2, pCurWidth ) ) ) { FreeMem( pFontDef->oMMgr, pCurWidth); return nRet; } pWidths++; } return OK; } unsigned long CIDFontDefAddWidth2 (FontDef pFontDef, CIDWidth2 oWidths2) { CIDFontDefAttr pAttr = (CIDFontDefAttr)pFontDef->pAttr; CIDWidth2 *pCurWidth = (CIDWidth2*)GetMem( pFontDef->oMMgr, sizeof (CIDWidth2) ); unsigned long nRet = OK; if ( !pCurWidth ) return pFontDef->oError->nErrorNo; pCurWidth->unCid_First = oWidths2.unCid_First; pCurWidth->unCid_Last = oWidths2.unCid_Last; pCurWidth->shW = oWidths2.shW; pCurWidth->shVx = oWidths2.shVx; pCurWidth->shVy = oWidths2.shVy; if ( OK != ( nRet = ListAdd( pAttr->pW2, pCurWidth ) ) ) { FreeMem( pFontDef->oMMgr, pCurWidth); return nRet; } return OK; } unsigned long CIDFontDefSetCIDtoGID (FontDef pFontDef, char *sName) { CIDFontDefAttr pAttr = (CIDFontDefAttr)pFontDef->pAttr; if ( !sName ) return OK; pAttr->nCIDtoGIDtype = 0; UtilsStrCpy( pAttr->sCIDtoGIDMap, sName, pAttr->sCIDtoGIDMap + LIMIT_MAX_NAME_LEN ); return OK; } unsigned long CIDFontDefSetCIDtoGID (FontDef pFontDef, BYTE *pBuffer, int nSize) { CIDFontDefAttr pAttr = (CIDFontDefAttr)pFontDef->pAttr; if ( !pBuffer || 0 == nSize ) return OK; if ( pAttr->pCIDtoGIDMap ) StreamFree( pAttr->pCIDtoGIDMap ); pAttr->nCIDtoGIDtype = 1; pAttr->pCIDtoGIDMap = TempFileStreamNew( pFontDef->oMMgr ); if ( !pAttr->pCIDtoGIDMap ) return AVS_OFFICEPDFWRITER_ERROR_NOT_ENOUGH_MEMORY; StreamSeek( pAttr->pCIDtoGIDMap, 0, SeekSet ); StreamWrite( pAttr->pCIDtoGIDMap, pBuffer, nSize, true ); return OK; } unsigned long CIDFontDefChangeStyle (FontDef pFontDef, bool bBold, bool bItalic) { if ( !pFontDef || !pFontDef->pAttr ) return AVS_OFFICEPDFWRITER_ERROR_INVALID_FONTDEF_DATA; if ( bBold ) { pFontDef->nStemV *= 2; pFontDef->nFlags |= FONT_FOURCE_BOLD; } if ( bItalic ) { pFontDef->nItalicAngle -= 11; pFontDef->nFlags |= FONT_ITALIC; } return OK; } //----- CIDTypeFont вспомогательные функции --------------------------------------------------------- static unsigned long CIDFontType2BeforeWriteFunc (Dict pObj) { FontAttr pFontAttr = (FontAttr)pObj->pAttr; FontDef pDef = pFontAttr->pFontDef; TTFontDefAttrRecPtr pDefAttr = (TTFontDefAttrRecPtr)pDef->pAttr; unsigned long nRet = OK; if ( pFontAttr->pMapStream ) pFontAttr->pMapStream->nFilter = pObj->nFilter; if ( pFontAttr->pCMapStream ) pFontAttr->pCMapStream->nFilter = pObj->nFilter; if ( !pFontAttr->pFontDef->pDescriptor ) { Dict pDescriptor = DictNew( pObj->oMMgr ); if ( !pDescriptor ) return ErrorGetCode( pObj->oError ); if ( pDefAttr->bEmbedding ) { Dict pFontData = DictStreamNew( pObj->oMMgr, pFontAttr->pXref ); if ( !pFontData ) return ErrorGetCode( pObj->oError ); if ( OK != TTFontDefSaveFontData( pFontAttr->pFontDef, pFontData->pStream ) ) return ErrorGetCode( pObj->oError ); nRet += DictAdd( pDescriptor, "FontFile2", pFontData ); nRet += DictAddNumber( pFontData, "Length1", pDefAttr->nLength1 ); nRet += DictAddNumber( pFontData, "Length2", 0); nRet += DictAddNumber( pFontData, "Length3", 0); pFontData->nFilter = pObj->nFilter; if ( OK != nRet ) return ErrorGetCode( pObj->oError ); } nRet += XrefAdd( pFontAttr->pXref, pDescriptor ); nRet += DictAddName( pDescriptor, "Type", "FontDescriptor" ); nRet += DictAddNumber( pDescriptor, "Ascent", pDef->nAscent ); nRet += DictAddNumber( pDescriptor, "Descent", pDef->nDescent ); nRet += DictAddNumber( pDescriptor, "Flags", pDef->nFlags ); Array oArray = BoxArrayNew( pObj->oMMgr, pDef->oFontBox ); nRet += DictAdd( pDescriptor, "FontBBox", oArray ); nRet += DictAddName( pDescriptor, "FontName", pDefAttr->sBaseFont ); nRet += DictAddNumber( pDescriptor, "ItalicAngle", pDef->nItalicAngle ); nRet += DictAddNumber( pDescriptor, "StemV", pDef->nStemV ); nRet += DictAddNumber( pDescriptor, "XHeight", pDef->nXHeight ); if ( OK != nRet ) return ErrorGetCode( pObj->oError ); pFontAttr->pFontDef->pDescriptor = pDescriptor; } if ( OK != ( nRet = DictAddName( pObj, "BaseFont", pDefAttr->sBaseFont ) ) ) return nRet; if ( OK != ( nRet = DictAddName( pFontAttr->pDescendantFont, "BaseFont", pDefAttr->sBaseFont ) ) ) return nRet; return DictAdd( pFontAttr->pDescendantFont, "FontDescriptor", pFontAttr->pFontDef->pDescriptor ); } static FontDict CIDFontTypeNew (FontDict pParent, Xref pXref) { unsigned long nRet = OK; FontAttr pAttr = (FontAttr)pParent->pAttr; FontDef pFontDef = pAttr->pFontDef; CIDFontDefAttr pFontDefAttr = (CIDFontDefAttr)pFontDef->pAttr; EncoderRecPtr pEncoder = pAttr->pEncoder; CMapEncoderStreamAttr pEncoderAttr = (CMapEncoderStreamAttr)pEncoder->pAttr; FontDict pFont = DictNew( pParent->oMMgr ); if ( !pFont ) return NULL; if ( OK != XrefAdd( pXref, pFont ) ) return NULL; nRet += DictAddName( pFont, "Type", "Font" ); if ( cidType0 == pFontDefAttr->eType || cidType0C == pFontDefAttr->eType || cidType0COT == pFontDefAttr->eType ) nRet += DictAddName( pFont, "Subtype", "CIDFontType0" ); else// if ( cidType2 == pFontDefAttr->eType || cidType2OT == pFontDefAttr->eType ) nRet += DictAddName( pFont, "Subtype", "CIDFontType2" ); nRet += DictAddName( pFont, "BaseFont", pFontDef->sBaseFont ); nRet += DictAddNumber( pFont, "DW", pFontDefAttr->nDW ); if ( OK != nRet ) return NULL; // Добавим элемент 'DW2'. Array oArray = ArrayNew( pParent->oMMgr ); if ( !oArray ) return NULL; if ( OK != DictAdd( pFont, "DW2", oArray ) ) return NULL; nRet += ArrayAddNumber( oArray, pFontDefAttr->anDW2[0]); nRet += ArrayAddNumber( oArray, pFontDefAttr->anDW2[1]); if ( OK != nRet ) return NULL; if ( pFontDefAttr->pW->nCount > 0 ) { // Добавим элемент 'W'. oArray = ArrayNew( pParent->oMMgr ); if ( !oArray ) return NULL; if ( OK != DictAdd( pFont, "W", oArray ) ) return NULL; unsigned short nSaveCid = 0; Array oSubArray = NULL; for ( unsigned int nIndex = 0; nIndex < pFontDefAttr->pW->nCount; nIndex++) { CIDWidth *pCurWidth = (CIDWidth *)ListItemAt( pFontDefAttr->pW, nIndex ); if ( pCurWidth->unCid_First != pCurWidth->unCid_Last ) { // Формат с1 ... с2 w nRet += ArrayAddNumber( oArray, pCurWidth->unCid_First ); nRet += ArrayAddNumber( oArray, pCurWidth->unCid_Last ); nRet += ArrayAddNumber( oArray, pCurWidth->shWidth ); continue; } if ( pCurWidth->unCid_First != nSaveCid + 1 || !oSubArray ) { oSubArray = ArrayNew( pParent->oMMgr ); if ( !oSubArray ) return NULL; nRet += ArrayAddNumber( oArray, pCurWidth->unCid_First ); nRet += ArrayAdd( oArray, oSubArray ); } nRet += ArrayAddNumber( oSubArray, pCurWidth->shWidth ); nSaveCid = pCurWidth->unCid_First; if ( OK != nRet ) return NULL; } } if ( pFontDefAttr->pW2->nCount > 0 ) { // Добавим элемент 'W2'. oArray = ArrayNew( pParent->oMMgr ); if ( !oArray ) return NULL; if ( OK != DictAdd( pFont, "W2", oArray ) ) return NULL; unsigned short nSaveCid = 0; Array oSubArray = NULL; for ( unsigned int nIndex = 0; nIndex < pFontDefAttr->pW2->nCount; nIndex++) { CIDWidth2 *pCurWidth = (CIDWidth2 *)ListItemAt( pFontDefAttr->pW2, nIndex ); if ( pCurWidth->unCid_First != pCurWidth->unCid_Last ) { // Формат с1 ... с2 W Vx Vy nRet += ArrayAddNumber( oArray, pCurWidth->unCid_First ); nRet += ArrayAddNumber( oArray, pCurWidth->unCid_Last ); nRet += ArrayAddNumber( oArray, pCurWidth->shW ); nRet += ArrayAddNumber( oArray, pCurWidth->shVx ); nRet += ArrayAddNumber( oArray, pCurWidth->shVy ); continue; } if ( pCurWidth->unCid_First != nSaveCid + 1 || !oSubArray ) { oSubArray = ArrayNew( pParent->oMMgr ); if ( !oSubArray ) return NULL; nRet += ArrayAddNumber( oArray, pCurWidth->unCid_First ); nRet += ArrayAdd( oArray, oSubArray ); } nRet += ArrayAddNumber( oSubArray, pCurWidth->shW ); nRet += ArrayAddNumber( oSubArray, pCurWidth->shVx ); nRet += ArrayAddNumber( oSubArray, pCurWidth->shVy ); nSaveCid = pCurWidth->unCid_First; if ( OK != nRet ) return NULL; } } Dict pDescriptor = DictNew( pParent->oMMgr ); if ( !pDescriptor ) return NULL; if ( OK != XrefAdd( pXref, pDescriptor ) ) return NULL; if ( OK != DictAdd( pFont, "FontDescriptor", pDescriptor ) ) return NULL; nRet += DictAddName( pDescriptor, "Type", "FontDescriptor"); nRet += DictAddName( pDescriptor, "FontName", pFontDef->sBaseFont ); nRet += DictAddNumber( pDescriptor, "Ascent", pFontDef->nAscent ); nRet += DictAddNumber( pDescriptor, "Descent", pFontDef->nDescent ); nRet += DictAddNumber( pDescriptor, "CapHeight", pFontDef->nCapHeight ); nRet += DictAddNumber( pDescriptor, "MissingWidth", pFontDef->nMissingWidth ); nRet += DictAddNumber( pDescriptor, "Flags", pFontDef->nFlags ); if ( OK != nRet ) return NULL; oArray = BoxArrayNew( pParent->oMMgr, pFontDef->oFontBox ); if ( !oArray ) return NULL; nRet += DictAdd( pDescriptor, "FontBBox", oArray ); nRet += DictAddNumber( pDescriptor, "ItalicAngle", pFontDef->nItalicAngle ); nRet += DictAddNumber( pDescriptor, "StemV", pFontDef->nStemV ); if ( pFontDefAttr->pFontData ) { Dict pFontData = DictStreamNew( pParent->oMMgr, pXref ); if ( !pFontData ) return NULL; if ( OK != StreamWriteToStream( pFontDefAttr->pFontData, pFontData->pStream, STREAM_FILTER_NONE, NULL ) ) return NULL; if ( cidType0 == pFontDefAttr->eType ) nRet += DictAdd( pDescriptor, "FontFile", pFontData ); else if ( cidType0C == pFontDefAttr->eType ) { nRet += DictAdd( pDescriptor, "FontFile3", pFontData ); nRet += DictAddName( pFontData, "Subtype", "CIDFontType0C" ); } else if ( cidType2 == pFontDefAttr->eType ) { nRet += DictAdd( pDescriptor, "FontFile2", pFontData ); } else // if ( cidType0COT == pFontDefAttr->eType || cidType2OT == pFontDefAttr->eType ) { nRet += DictAdd( pDescriptor, "FontFile3", pFontData ); nRet += DictAddName( pFontData, "Subtype", "OpenType" ); } pFontData->nFilter = pFont->nFilter; } if ( OK != nRet ) return NULL; if ( strlen( pFontDefAttr->sRegistry ) > 0 && strlen( pFontDefAttr->sOrdering ) > 0 ) { Dict pCIDSystemInfo = DictNew( pParent->oMMgr ); if ( !pCIDSystemInfo ) return NULL; if ( OK != DictAdd( pFont, "CIDSystemInfo", pCIDSystemInfo ) ) return NULL; nRet += DictAdd( pCIDSystemInfo, "Registry", StringNew( pParent->oMMgr, pFontDefAttr->sRegistry, NULL ) ); nRet += DictAdd( pCIDSystemInfo, "Ordering", StringNew( pParent->oMMgr, pFontDefAttr->sOrdering, NULL ) ); nRet += DictAddNumber( pCIDSystemInfo, "Supplement", pFontDefAttr->nSupplement ); if ( OK != nRet ) return NULL; } if ( 0 == pFontDefAttr->nCIDtoGIDtype ) { nRet += DictAddName( pFont, "CIDToGIDMap", pFontDefAttr->sCIDtoGIDMap ); } else if ( 1 == pFontDefAttr->nCIDtoGIDtype ) { Dict pCIDtoGID = DictStreamNew( pParent->oMMgr, pXref ); if ( !pCIDtoGID ) return NULL; if ( OK != DictAdd( pFont, "CIDToGIDMap", pCIDtoGID ) ) return NULL; if ( OK != StreamWriteToStream( pFontDefAttr->pCIDtoGIDMap, pCIDtoGID->pStream, STREAM_FILTER_NONE, NULL ) ) return NULL; #ifdef FILTER_FLATE_DECODE_ENABLED pCIDtoGID->nFilter |= STREAM_FILTER_FLATE_DECODE; #endif } return pFont; } static FontDict CIDFontType0New (FontDict pParent, Xref pXref) { unsigned long nRet = OK; FontAttr pAttr = (FontAttr)pParent->pAttr; FontDef pFontDef = pAttr->pFontDef; CIDFontDefAttr pFontDefAttr = (CIDFontDefAttr)pFontDef->pAttr; EncoderRecPtr pEncoder = pAttr->pEncoder; CMapEncoderAttr pEncoderAttr = (CMapEncoderAttr)pEncoder->pAttr; FontDict pFont = DictNew( pParent->oMMgr ); if ( !pFont ) return NULL; if ( OK != XrefAdd( pXref, pFont ) ) return NULL; nRet += DictAddName( pFont, "Type", "Font" ); nRet += DictAddName( pFont, "Subtype", "CIDFontType0" ); nRet += DictAddNumber( pFont, "DW", pFontDefAttr->nDW ); nRet += DictAddName( pFont, "BaseFont", pFontDef->sBaseFont ); if ( OK != nRet ) return NULL; // Добавим элемент 'DW2'. Array oArray = ArrayNew( pParent->oMMgr ); if ( !oArray ) return NULL; if ( OK != DictAdd( pFont, "DW2", oArray ) ) return NULL; nRet += ArrayAddNumber( oArray, pFontDefAttr->anDW2[0]); nRet += ArrayAddNumber( oArray, pFontDefAttr->anDW2[1]); if ( OK != nRet ) return NULL; // Добавим элемент 'W'. oArray = ArrayNew( pParent->oMMgr ); if ( !oArray ) return NULL; if ( OK != DictAdd( pFont, "W", oArray ) ) return NULL; unsigned short nSaveCid = 0; Array oSubArray = NULL; for ( unsigned int nIndex = 0; nIndex < pFontDefAttr->pW->nCount; nIndex++) { CIDWidth *pCurWidth = (CIDWidth *)ListItemAt( pFontDefAttr->pW, nIndex ); if ( pCurWidth->unCid_First != pCurWidth->unCid_Last ) { // Формат с1 ... с2 w nRet += ArrayAddNumber( oArray, pCurWidth->unCid_First ); nRet += ArrayAddNumber( oArray, pCurWidth->unCid_Last ); nRet += ArrayAddNumber( oArray, pCurWidth->shWidth ); } if ( pCurWidth->unCid_First != nSaveCid + 1 || !oSubArray ) { oSubArray = ArrayNew( pParent->oMMgr ); if ( !oSubArray ) return NULL; nRet += ArrayAddNumber( oArray, pCurWidth->unCid_First ); nRet += ArrayAdd( oArray, oSubArray ); } nRet += ArrayAddNumber( oSubArray, pCurWidth->shWidth ); nSaveCid = pCurWidth->unCid_First; if ( OK != nRet ) return NULL; } Dict pDescriptor = DictNew( pParent->oMMgr ); if ( !pDescriptor ) return NULL; if ( OK != XrefAdd( pXref, pDescriptor ) ) return NULL; if ( OK != DictAdd( pFont, "FontDescriptor", pDescriptor ) ) return NULL; nRet += DictAddName( pDescriptor, "Type", "FontDescriptor"); nRet += DictAddName( pDescriptor, "FontName", pFontDef->sBaseFont ); nRet += DictAddNumber( pDescriptor, "Ascent", pFontDef->nAscent ); nRet += DictAddNumber( pDescriptor, "Descent", pFontDef->nDescent ); nRet += DictAddNumber( pDescriptor, "CapHeight", pFontDef->nCapHeight ); nRet += DictAddNumber( pDescriptor, "MissingWidth", pFontDef->nMissingWidth ); nRet += DictAddNumber( pDescriptor, "Flags", pFontDef->nFlags ); if ( OK != nRet ) return NULL; oArray = BoxArrayNew( pParent->oMMgr, pFontDef->oFontBox ); if ( !oArray ) return NULL; nRet += DictAdd( pDescriptor, "FontBBox", oArray ); nRet += DictAddNumber( pDescriptor, "ItalicAngle", pFontDef->nItalicAngle ); nRet += DictAddNumber( pDescriptor, "StemV", pFontDef->nStemV ); if ( OK != nRet ) return NULL; Dict pCIDSystemInfo = DictNew( pParent->oMMgr ); if ( !pCIDSystemInfo ) return NULL; if ( OK != DictAdd( pFont, "CIDSystemInfo", pCIDSystemInfo ) ) return NULL; nRet += DictAdd( pCIDSystemInfo, "Registry", StringNew( pParent->oMMgr, pEncoderAttr->sRegistry, NULL ) ); nRet += DictAdd( pCIDSystemInfo, "Ordering", StringNew( pParent->oMMgr, pEncoderAttr->sOrdering, NULL ) ); nRet += DictAddNumber( pCIDSystemInfo, "Supplement", pEncoderAttr->nSuppliment ); if ( OK != nRet ) return NULL; return pFont; } static FontDict CIDFontType2New (FontDict pParent, Xref pXref) { unsigned long nRet = OK; FontAttr pAttr = (FontAttr)pParent->pAttr; FontDef pFontDef = pAttr->pFontDef; TTFontDefAttrRecPtr pFontDefAttr = (TTFontDefAttrRecPtr)pFontDef->pAttr; EncoderRecPtr pEncoder = pAttr->pEncoder; CMapEncoderAttr pEncoderAttr = (CMapEncoderAttr)pEncoder->pAttr; unsigned short anTempMap[65536]; unsigned short nMax = 0; FontDict pFont = DictNew( pParent->oMMgr ); if ( !pFont ) return NULL; if ( OK != XrefAdd( pXref, pFont ) ) return NULL; pParent->pBeforeWriteFn = CIDFontType2BeforeWriteFunc; nRet += DictAddName( pFont, "Type", "Font"); nRet += DictAddName( pFont, "Subtype", "CIDFontType2"); nRet += DictAddNumber( pFont, "DW", pFontDef->nMissingWidth ); if ( OK != nRet ) return NULL; // Добавим элемент 'DW2'. Array oArray = ArrayNew( pFont->oMMgr ); if ( !oArray ) return NULL; if ( OK != DictAdd( pFont, "DW2", oArray ) ) return NULL; nRet += ArrayAddNumber( oArray, pFontDef->oFontBox.fBottom ); nRet += ArrayAddNumber( oArray, pFontDef->oFontBox.fBottom - pFontDef->oFontBox.fTop ); UtilsMemSet( anTempMap, 0, sizeof(unsigned short) * 65536); if ( OK != nRet ) return NULL; for ( unsigned int nI = 0; nI < 256; nI++) { for ( unsigned int nJ = 0; nJ < 256; nJ++) { unsigned short nCid = pEncoderAttr->anCIDMap[nI][nJ]; if ( 0 != nCid ) { unsigned short nUnicode = pEncoderAttr->anUnicodeMap[nI][nJ]; unsigned short nGid = TTFontDefGetGlyphID( pFontDef, nUnicode ); anTempMap[nCid] = nGid; if ( nMax < nCid ) nMax = nCid; } } } if ( nMax > 0 ) { short nDW = pFontDef->nMissingWidth; unsigned short *pnTempMap = anTempMap; Array oTempArray = NULL; // Добавим элемент 'W'. oArray = ArrayNew( pFont->oMMgr ); if ( !oArray ) return NULL; if ( OK != DictAdd( pFont, "W", oArray ) ) return NULL; for ( unsigned int nIndex = 0; nIndex < nMax; nIndex++, pnTempMap++) { int nWidth = TTFontDefGetGidWidth( pFontDef, *pnTempMap); if ( nWidth != nDW ) { if ( !oTempArray ) { if ( OK != ( nRet = ArrayAddNumber( oArray, nIndex ) ) ) return NULL; oTempArray = ArrayNew( pFont->oMMgr ); if ( !oTempArray ) return NULL; if ( OK != ( nRet = ArrayAdd( oArray, oTempArray ) ) ) return NULL; } if ( OK != ( nRet = ArrayAddNumber( oTempArray, nWidth ) ) ) return NULL; } else oTempArray = NULL; } // Создаем данные "CIDToGIDMap". if ( pFontDefAttr->bEmbedding ) { pAttr->pMapStream = DictStreamNew( pFont->oMMgr, pXref ); if ( !pAttr->pMapStream ) return NULL; if ( OK != ( nRet = DictAdd( pFont, "CIDToGIDMap", pAttr->pMapStream ) ) ) return NULL; for ( unsigned int nIndex = 0; nIndex < nMax; nIndex++) { BYTE anUnicodeTemp[2]; unsigned short nGid = anTempMap[nIndex]; anUnicodeTemp[0] = nGid >> 8; anUnicodeTemp[1] = nGid; UtilsMemCpy( (BYTE *)(anTempMap + nIndex), anUnicodeTemp, 2 ); } if ( OK != ( nRet = StreamWrite( pAttr->pMapStream->pStream, (BYTE *)anTempMap, nMax * 2 ) ) ) return NULL; } } else { SetError( pFont->oError, AVS_OFFICEPDFWRITER_ERROR_INVALID_FONTDEF_DATA, 0); return 0; } Dict pCIDSystemInfo = DictNew( pParent->oMMgr ); if ( !pCIDSystemInfo ) return NULL; if ( OK != DictAdd( pFont, "CIDSystemInfo", pCIDSystemInfo ) ) return NULL; nRet += DictAdd( pCIDSystemInfo, "Registry", StringNew( pParent->oMMgr, pEncoderAttr->sRegistry, NULL ) ); nRet += DictAdd( pCIDSystemInfo, "Ordering", StringNew( pParent->oMMgr, pEncoderAttr->sOrdering, NULL ) ); nRet += DictAddNumber( pCIDSystemInfo, "Supplement", pEncoderAttr->nSuppliment ); if ( OK != nRet ) return NULL; return pFont; } static TextWidth CIDTextWidth (FontDict pFont, const BYTE *pText, unsigned int nLen, unsigned int *pCIDs, unsigned int nLenCID) { TextWidth oTextWidth = {0, 0, 0, 0}; return oTextWidth; FontAttr pAttr = (FontAttr)pFont->pAttr; ParseTextRec oParseState; EncoderRecPtr pEncoder = pAttr->pEncoder; unsigned int nIndex = 0; int nDW2 = 0; BYTE nChar = 0; if ( FontDefCID == pAttr->pFontDef->eType ) { CIDFontDefAttr pCIDFontDefAttr = (CIDFontDefAttr)pAttr->pFontDef->pAttr; nDW2 = pCIDFontDefAttr->anDW2[1]; } else { nDW2 = pAttr->pFontDef->oFontBox.fBottom - pAttr->pFontDef->oFontBox.fTop; } EncoderSetParseText( pEncoder, &oParseState, pText, nLen); while ( nIndex < nLen ) { ByteType eBType = CMapEncoderByteType( pEncoder, &oParseState ); unsigned short nCid = 0; unsigned short nUnicode = 0; unsigned int nWidth = 0; nChar = *pText++; unsigned short nCode = nChar; if ( ByteTypeLead == eBType ) { nCode <<= 8; nCode += *pText; } if ( ByteTypeTrial != eBType ) { if ( WModeHorizontal == pAttr->eWritingMode ) { if ( FontDefCID == pAttr->pFontDef->eType ) { // cid-based font nCid = CMapEncoderToCID( pEncoder, nCode ); nWidth = CIDFontDefGetCIDWidth( pAttr->pFontDef, nCid ); } else { // unicode-based font nUnicode = CMapEncoderToUnicode( pEncoder, nCode ); nWidth = TTFontDefGetCharWidth( pAttr->pFontDef, nUnicode ); } } else { nWidth = -nDW2; } oTextWidth.nNumchars++; } if ( IS_WHITE_SPACE(nCode) ) { oTextWidth.nNumwords++; oTextWidth.nNumspace++; } oTextWidth.nWidth += nWidth; nIndex++; } if ( IS_WHITE_SPACE(nChar) ) ; // ничего не делаем else oTextWidth.nNumwords++; return oTextWidth; } static unsigned int CIDMeasureText (FontDict pFont, const BYTE *pText, unsigned int nLen, unsigned int *pCIDs, unsigned int nLenCID, float fWidth, float fFontSize, float fCharSpace, float fWordSpace, bool bWordWrap, float *pfRealWidth) { float fTempWidth = 0; unsigned int nTempLen = 0; FontAttr pAttr = (FontAttr)pFont->pAttr; ByteType eLastBType = ByteTypeTrial; EncoderRecPtr pEncoder = pAttr->pEncoder; ParseTextRec oParseState; int nDW2 = 0; return 0; if ( FontDefCID == pAttr->pFontDef->eType ) { CIDFontDefAttr pCIDFontDefAttr = (CIDFontDefAttr)pAttr->pFontDef->pAttr; nDW2 = pCIDFontDefAttr->anDW2[1]; } else { nDW2 = pAttr->pFontDef->oFontBox.fBottom - pAttr->pFontDef->oFontBox.fTop; } EncoderSetParseText( pEncoder, &oParseState, pText, nLen); for ( unsigned int nIndex = 0; nIndex < nLen; nIndex++) { BYTE nByte = *pText++; BYTE nByte2 = *pText; // следующий байт ByteType eBType = EncoderByteType( pEncoder, &oParseState ); unsigned short nUnicode = 0; unsigned short nCode = nByte; unsigned short nTempWidth = 0; if ( ByteTypeLead == eBType ) { nCode <<= 8; nCode += nByte2; } if ( !bWordWrap ) { if ( IS_WHITE_SPACE(nByte) ) { nTempLen = nIndex + 1; if ( pfRealWidth ) *pfRealWidth = fTempWidth; } else if ( ByteTypeSingle == eBType || ByteTypeLead == eBType ) { nTempLen = nIndex; if ( pfRealWidth ) *pfRealWidth = fTempWidth; } } else { if ( IS_WHITE_SPACE(nByte) ) { nTempLen = nIndex + 1; if ( pfRealWidth ) *pfRealWidth = fTempWidth; } else if ( ByteTypeTrial == eLastBType || ( ByteTypeLead == eBType && ByteTypeSingle == eLastBType ) ) { if ( !EncoderCheckJWWLineHead( pEncoder, nCode ) ) { nTempLen = nIndex; if ( pfRealWidth ) *pfRealWidth = fTempWidth; } } } if ( IS_WHITE_SPACE(nByte) ) { fTempWidth += fWordSpace; } if ( ByteTypeTrial != eBType ) { if ( WModeHorizontal == pAttr->eWritingMode ) { if ( FontDefCID == pAttr->pFontDef->eType ) { // cid-based font unsigned short nCid = CMapEncoderToCID( pEncoder, nCode ); nTempWidth = CIDFontDefGetCIDWidth( pAttr->pFontDef, nCid ); } else { // unicode-based font nUnicode = CMapEncoderToUnicode( pEncoder, nCode ); nTempWidth = TTFontDefGetCharWidth( pAttr->pFontDef, nUnicode ); } } else { nTempWidth = -nDW2; } if ( nIndex > 0 ) fTempWidth += fCharSpace; } fTempWidth += (double)nTempWidth * fFontSize / 1000; if ( fTempWidth > fWidth || 0x0A == nByte ) return nTempLen; if ( IS_WHITE_SPACE(nByte) ) eLastBType = ByteTypeTrial; else eLastBType = eBType; } if ( pfRealWidth ) *pfRealWidth = fTempWidth; return nLen; } static TextWidth CIDTextWidth2 (FontDict pFont, const BYTE *pText, unsigned int nLen, unsigned int *pCIDs, unsigned int nLenCID) { TextWidth oTextWidth = {0, 0, 0, 0}; FontAttr pAttr = (FontAttr)pFont->pAttr; if ( 0 == nLenCID || NULL == pCIDs ) return oTextWidth; ParseTextRec oParseState; EncoderRecPtr pEncoder = pAttr->pEncoder; CIDFontDefAttr pCIDFontDefAttr = (CIDFontDefAttr)pAttr->pFontDef->pAttr; int nDW2 = pCIDFontDefAttr->anDW2[1]; int nIndex = 0; while ( nIndex < nLenCID ) { int nWidth = 0; if ( WModeHorizontal == pAttr->eWritingMode ) { nWidth = CIDFontDefGetCIDWidth( pAttr->pFontDef, pCIDs[nIndex] ); } else { nWidth = -nDW2; } oTextWidth.nNumchars++; if ( IS_WHITE_SPACE(pCIDs[nIndex]) ) { oTextWidth.nNumwords++; oTextWidth.nNumspace++; } oTextWidth.nWidth += nWidth; nIndex++; } return oTextWidth; } static unsigned int CIDMeasureText2 (FontDict pFont, const BYTE *pText, unsigned int nLen, unsigned int *pCIDs, unsigned int nLenCID, float fWidth, float fFontSize, float fCharSpace, float fWordSpace, bool bWordWrap, float *pfRealWidth) { // TO DO : Релизовать данную функцию return 0; //float fTempWidth = 0; //unsigned int nTempLen = 0; //FontAttr pAttr = (FontAttr)pFont->pAttr; //ByteType eLastBType = ByteTypeTrial; //EncoderRecPtr pEncoder = pAttr->pEncoder; //ParseTextRec oParseState; //int nDW2 = 0; //if ( FontDefCID == pAttr->pFontDef->eType ) //{ // CIDFontDefAttr pCIDFontDefAttr = (CIDFontDefAttr)pAttr->pFontDef->pAttr; // nDW2 = pCIDFontDefAttr->anDW2[1]; //} //else //{ // nDW2 = pAttr->pFontDef->oFontBox.fBottom - pAttr->pFontDef->oFontBox.fTop; //} //EncoderSetParseText( pEncoder, &oParseState, pText, nLen); //for ( unsigned int nIndex = 0; nIndex < nLen; nIndex++) //{ // BYTE nByte = *pText++; // BYTE nByte2 = *pText; // следующий байт // ByteType eBType = EncoderByteType( pEncoder, &oParseState ); // unsigned short nUnicode = 0; // unsigned short nCode = nByte; // unsigned short nTempWidth = 0; // if ( ByteTypeLead == eBType ) // { // nCode <<= 8; // nCode += nByte2; // } // if ( !bWordWrap ) // { // if ( IS_WHITE_SPACE(nByte) ) // { // nTempLen = nIndex + 1; // if ( pfRealWidth ) // *pfRealWidth = fTempWidth; // } // else if ( ByteTypeSingle == eBType || ByteTypeLead == eBType ) // { // nTempLen = nIndex; // if ( pfRealWidth ) // *pfRealWidth = fTempWidth; // } // } // else // { // if ( IS_WHITE_SPACE(nByte) ) // { // nTempLen = nIndex + 1; // if ( pfRealWidth ) // *pfRealWidth = fTempWidth; // } // else if ( ByteTypeTrial == eLastBType || ( ByteTypeLead == eBType && ByteTypeSingle == eLastBType ) ) // { // if ( !EncoderCheckJWWLineHead( pEncoder, nCode ) ) // { // nTempLen = nIndex; // if ( pfRealWidth ) // *pfRealWidth = fTempWidth; // } // } // } // if ( IS_WHITE_SPACE(nByte) ) // { // fTempWidth += fWordSpace; // } // if ( ByteTypeTrial != eBType ) // { // if ( WModeHorizontal == pAttr->eWritingMode ) // { // if ( FontDefCID == pAttr->pFontDef->eType ) // { // // cid-based font // unsigned short nCid = CMapEncoderToCID( pEncoder, nCode ); // nTempWidth = CIDFontDefGetCIDWidth( pAttr->pFontDef, nCid ); // } // else // { // // unicode-based font // nUnicode = CMapEncoderToUnicode( pEncoder, nCode ); // nTempWidth = TTFontDefGetCharWidth( pAttr->pFontDef, nUnicode ); // } // } // else // { // nTempWidth = -nDW2; // } // if ( nIndex > 0 ) // fTempWidth += fCharSpace; // } // fTempWidth += (double)nTempWidth * fFontSize / 1000; // if ( fTempWidth > fWidth || 0x0A == nByte ) // return nTempLen; // if ( IS_WHITE_SPACE(nByte) ) // eLastBType = ByteTypeTrial; // else // eLastBType = eBType; //} //if ( pfRealWidth ) // *pfRealWidth = fTempWidth; //return nLen; } static char* UINT16ToHex (char *sStr, unsigned short nValue, char *pEndPointer) { BYTE anTemp[2]; char nChar = 0; if (pEndPointer - sStr < 7) return sStr; UtilsMemCpy( anTemp, (BYTE *)&nValue, 2); unsigned short nTempValue = (unsigned short)((unsigned short)anTemp[0] << 8 | (unsigned short)anTemp[1]); UtilsMemCpy( anTemp, (BYTE *)&nTempValue, 2); *sStr++ = '<'; if ( 0 != anTemp[0] ) { nChar = anTemp[0] >> 4; if ( nChar <= 9 ) nChar += 0x30; else nChar += 0x41 - 10; *sStr++ = nChar; nChar = anTemp[0] & 0x0f; if ( nChar <= 9 ) nChar += 0x30; else nChar += 0x41 - 10; *sStr++ = nChar; } nChar = anTemp[1] >> 4; if ( nChar <= 9 ) nChar += 0x30; else nChar += 0x41 - 10; *sStr++ = nChar; nChar = anTemp[1] & 0x0f; if ( nChar <= 9 ) nChar += 0x30; else nChar += 0x41 - 10; *sStr++ = nChar; *sStr++ = '>'; *sStr = 0; return sStr; } static Dict CreateCMap (EncoderRecPtr pEncoder, Xref pXref) { unsigned long nRet = OK; Dict pCMap = DictStreamNew( pEncoder->oMMgr, pXref ); CMapEncoderAttr pAttr = (CMapEncoderAttr)pEncoder->pAttr; char sBuffer[TEMP_BUFFER_SIZE]; char *pBufPointer; char *pEndBufPointer = sBuffer + TEMP_BUFFER_SIZE - 1; if ( !pCMap ) return NULL; nRet += DictAddName( pCMap, "Type", "CMap"); nRet += DictAddName( pCMap, "CMapName", pEncoder->sName ); Dict pSysInfo = DictNew( pEncoder->oMMgr ); if ( !pSysInfo ) return NULL; if ( OK != DictAdd( pCMap, "CIDSystemInfo", pSysInfo ) ) return NULL; nRet += DictAdd( pSysInfo, "Registry", StringNew( pEncoder->oMMgr, pAttr->sRegistry, NULL ) ); nRet += DictAdd( pSysInfo, "Ordering", StringNew( pEncoder->oMMgr, pAttr->sOrdering, NULL ) ); nRet += DictAddNumber( pSysInfo, "Supplement", pAttr->nSuppliment ); nRet += DictAddNumber( pCMap, "WMode", (unsigned int)pAttr->eWritingMode ); // создаем CMap данные из закодированных данных nRet += StreamWriteStr( pCMap->pStream, "%!PS-Adobe-3.0 Resource-CMap\r\n" ); nRet += StreamWriteStr( pCMap->pStream, "%%DocumentNeededResources: ProcSet (CIDInit)\r\n" ); nRet += StreamWriteStr( pCMap->pStream, "%%IncludeResource: ProcSet (CIDInit)\r\n" ); pBufPointer = (char*)UtilsStrCpy( sBuffer, "%%BeginResource: CMap (", pEndBufPointer); pBufPointer = (char*)UtilsStrCpy( pBufPointer, pEncoder->sName, pEndBufPointer); UtilsStrCpy( pBufPointer, ")\r\n", pEndBufPointer); nRet += StreamWriteStr( pCMap->pStream, sBuffer ); pBufPointer = (char*)UtilsStrCpy( sBuffer, "%%Title: (", pEndBufPointer); pBufPointer = (char*)UtilsStrCpy( pBufPointer, pEncoder->sName, pEndBufPointer); *pBufPointer++ = ' '; pBufPointer = (char*)UtilsStrCpy( pBufPointer, pAttr->sRegistry, pEndBufPointer); *pBufPointer++ = ' '; pBufPointer = (char*)UtilsStrCpy( pBufPointer, pAttr->sOrdering, pEndBufPointer); *pBufPointer++ = ' '; pBufPointer = UtilsIToA( pBufPointer, pAttr->nSuppliment, pEndBufPointer); UtilsStrCpy( pBufPointer, ")\r\n", pEndBufPointer); nRet += StreamWriteStr( pCMap->pStream, sBuffer ); nRet += StreamWriteStr( pCMap->pStream, "%%Version: 1.0\r\n"); nRet += StreamWriteStr( pCMap->pStream, "%%EndComments\r\n"); nRet += StreamWriteStr( pCMap->pStream, "/CIDInit /ProcSet findresource begin\r\n\r\n"); // Спецификации файлов Adobe CMap и CIDFont рекомендуют выделить мето для // 5 дополнительных элементов в данной директории. nRet += StreamWriteStr( pCMap->pStream, "12 dict begin\r\n\r\n"); nRet += StreamWriteStr( pCMap->pStream, "begincmap\r\n\r\n"); nRet += StreamWriteStr( pCMap->pStream, "/CIDSystemInfo 3 dict dup begin\r\n"); pBufPointer = (char*)UtilsStrCpy( sBuffer, " /Registry (", pEndBufPointer); pBufPointer = (char*)UtilsStrCpy( pBufPointer, pAttr->sRegistry, pEndBufPointer); UtilsStrCpy( pBufPointer, ") def\r\n", pEndBufPointer); nRet += StreamWriteStr( pCMap->pStream, sBuffer ); pBufPointer = (char*)UtilsStrCpy( sBuffer, " /Ordering (", pEndBufPointer); pBufPointer = (char*)UtilsStrCpy( pBufPointer, pAttr->sOrdering, pEndBufPointer); UtilsStrCpy( pBufPointer, ") def\r\n", pEndBufPointer); nRet += StreamWriteStr( pCMap->pStream, sBuffer); pBufPointer = (char*)UtilsStrCpy( sBuffer, " /Supplement ", pEndBufPointer); pBufPointer = UtilsIToA( pBufPointer, pAttr->nSuppliment, pEndBufPointer); pBufPointer = (char*)UtilsStrCpy( pBufPointer, " def\r\n", pEndBufPointer); UtilsStrCpy( pBufPointer, "end def\r\n\r\n", pEndBufPointer); nRet += StreamWriteStr( pCMap->pStream, sBuffer); pBufPointer = (char*)UtilsStrCpy( sBuffer, "/CMapName /", pEndBufPointer); pBufPointer = (char*)UtilsStrCpy( pBufPointer, pEncoder->sName, pEndBufPointer); UtilsStrCpy( pBufPointer, " def\r\n", pEndBufPointer); nRet += StreamWriteStr( pCMap->pStream, sBuffer); nRet += StreamWriteStr( pCMap->pStream, "/CMapVersion 1.0 def\r\n"); nRet += StreamWriteStr( pCMap->pStream, "/CMapType 1 def\r\n\r\n"); if ( pAttr->nUidOffset >= 0 ) { pBufPointer = (char*)UtilsStrCpy( sBuffer, "/UIDOffset ", pEndBufPointer); pBufPointer = UtilsIToA( pBufPointer, pAttr->nUidOffset, pEndBufPointer); UtilsStrCpy( pBufPointer, " def\r\n\r\n", pEndBufPointer); nRet += StreamWriteStr( pCMap->pStream, sBuffer ); } pBufPointer = (char*)UtilsStrCpy( sBuffer, "/XUID [", pEndBufPointer); pBufPointer = UtilsIToA( pBufPointer, pAttr->anXUid[0], pEndBufPointer); *pBufPointer++ = ' '; pBufPointer = UtilsIToA( pBufPointer, pAttr->anXUid[1], pEndBufPointer); *pBufPointer++ = ' '; pBufPointer = UtilsIToA( pBufPointer, pAttr->anXUid[2], pEndBufPointer); UtilsStrCpy( pBufPointer, "] def\r\n\r\n", pEndBufPointer); nRet += StreamWriteStr( pCMap->pStream, sBuffer); pBufPointer = (char*)UtilsStrCpy( sBuffer, "/WMode ", pEndBufPointer); pBufPointer = UtilsIToA( pBufPointer, (unsigned int)pAttr->eWritingMode, pEndBufPointer); UtilsStrCpy( pBufPointer, " def\r\n\r\n", pEndBufPointer); nRet += StreamWriteStr( pCMap->pStream, sBuffer); // add code-space-range pBufPointer = UtilsIToA( sBuffer, pAttr->pCodeSpaceRange->nCount, pEndBufPointer); UtilsStrCpy( pBufPointer, " begincodespacerange\r\n", pEndBufPointer); nRet += StreamWriteStr( pCMap->pStream, sBuffer ); for ( unsigned int nIndex = 0; nIndex < pAttr->pCodeSpaceRange->nCount; nIndex++) { CIDRangeRec *pRange = (CIDRangeRec*)ListItemAt( pAttr->pCodeSpaceRange, nIndex); pBufPointer = UINT16ToHex( sBuffer, pRange->nFrom, pEndBufPointer); *pBufPointer++ = ' '; pBufPointer = UINT16ToHex( pBufPointer, pRange->nTo, pEndBufPointer); UtilsStrCpy( pBufPointer, "\r\n", pEndBufPointer); nRet += StreamWriteStr( pCMap->pStream, sBuffer ); if ( OK != nRet ) return NULL; } UtilsStrCpy( sBuffer, "endcodespacerange\r\n\r\n", pEndBufPointer); nRet += StreamWriteStr( pCMap->pStream, sBuffer ); if ( OK != nRet ) return NULL; // add not-def-range pBufPointer = UtilsIToA( sBuffer, pAttr->pNotDefRange->nCount, pEndBufPointer); UtilsStrCpy( pBufPointer, " beginnotdefrange\r\n", pEndBufPointer); nRet += StreamWriteStr( pCMap->pStream, sBuffer); for ( unsigned int nIndex = 0; nIndex < pAttr->pNotDefRange->nCount; nIndex++) { CIDRangeRec *pRange = (CIDRangeRec*)ListItemAt( pAttr->pNotDefRange, nIndex ); pBufPointer = UINT16ToHex( sBuffer, pRange->nFrom, pEndBufPointer); *pBufPointer++ = ' '; pBufPointer = UINT16ToHex( pBufPointer, pRange->nTo, pEndBufPointer); *pBufPointer++ = ' '; pBufPointer = UtilsIToA( pBufPointer, pRange->nCID, pEndBufPointer); UtilsStrCpy( pBufPointer, "\r\n", pEndBufPointer); nRet += StreamWriteStr( pCMap->pStream, sBuffer ); if ( OK != nRet ) return NULL; } UtilsStrCpy( sBuffer, "endnotdefrange\r\n\r\n", pEndBufPointer); nRet += StreamWriteStr( pCMap->pStream, sBuffer ); if ( OK != nRet ) return NULL; // add cid-range unsigned int nPhase = pAttr->pCMAPRange->nCount / 100; unsigned int nOdd = pAttr->pCMAPRange->nCount % 100; if ( nPhase > 0 ) pBufPointer = UtilsIToA( sBuffer, 100, pEndBufPointer); else pBufPointer = UtilsIToA( sBuffer, nOdd, pEndBufPointer); UtilsStrCpy( pBufPointer, " begincidrange\r\n", pEndBufPointer); nRet += StreamWriteStr( pCMap->pStream, sBuffer ); for ( unsigned int nIndex = 0; nIndex < pAttr->pCMAPRange->nCount; nIndex++) { CIDRangeRec *pRange = (CIDRangeRec*)ListItemAt( pAttr->pCMAPRange, nIndex ); pBufPointer = UINT16ToHex( sBuffer, pRange->nFrom, pEndBufPointer); *pBufPointer++ = ' '; pBufPointer = UINT16ToHex( pBufPointer, pRange->nTo, pEndBufPointer); *pBufPointer++ = ' '; pBufPointer = UtilsIToA( pBufPointer, pRange->nCID, pEndBufPointer); UtilsStrCpy( pBufPointer, "\r\n", pEndBufPointer); nRet += StreamWriteStr( pCMap->pStream, sBuffer ); if ( 0 == (nIndex + 1) %100 ) { nPhase--; pBufPointer = (char*)UtilsStrCpy( sBuffer, "endcidrange\r\n\r\n", pEndBufPointer); if ( nPhase > 0 ) pBufPointer = UtilsIToA( pBufPointer, 100, pEndBufPointer); else pBufPointer = UtilsIToA( pBufPointer, nOdd, pEndBufPointer); UtilsStrCpy( pBufPointer, " begincidrange\r\n", pEndBufPointer); nRet += StreamWriteStr( pCMap->pStream, sBuffer ); } if ( OK != nRet ) return NULL; } if ( nOdd > 0 ) pBufPointer = (char*)UtilsStrCpy( sBuffer, "endcidrange\r\n", pEndBufPointer); pBufPointer = (char*)UtilsStrCpy( pBufPointer, "endcmap\r\n", pEndBufPointer); pBufPointer = (char*)UtilsStrCpy( pBufPointer, "CMapName currentdict /CMap defineresource pop\r\n", pEndBufPointer); pBufPointer = (char*)UtilsStrCpy( pBufPointer, "end\r\n", pEndBufPointer); pBufPointer = (char*)UtilsStrCpy( pBufPointer, "end\r\n\r\n", pEndBufPointer); pBufPointer = (char*)UtilsStrCpy( pBufPointer, "%%EndResource\r\n", pEndBufPointer); UtilsStrCpy( pBufPointer, "%%EOF\r\n", pEndBufPointer); nRet += StreamWriteStr( pCMap->pStream, sBuffer); if ( OK != nRet ) return NULL; return pCMap; } static unsigned long CIDBeforeWrite (Dict pObj) { //unsigned long nRet = OK; //if ( ( nRet = CreateDescriptor( pObj ) ) != OK ) // return nRet; //if ( ( nRet = CreateToUnicode( pObj ) ) != OK ) // return nRet; return OK; } static unsigned long CIDOnWrite (Dict pObj, StreamRecPtr pStream) { return OK; // FontAttr pAttr = (FontAttr)pObj->pAttr; //if ( true == pAttr->oExternWidths.bUse ) //{ // unsigned long nRet = OK; // char sBuffer[TEMP_BUFFER_SIZE]; // char *pEndPointer = sBuffer + TEMP_BUFFER_SIZE - 1; // char *pBufPointer; // pBufPointer = (char*)UtilsStrCpy( sBuffer, "/FirstChar ", pEndPointer); // pBufPointer = UtilsIToA( pBufPointer, pAttr->oExternWidths.nFirstChar, pEndPointer); // UtilsStrCpy( pBufPointer, "\012", pEndPointer); // if ( OK != ( nRet = StreamWriteStr( pStream, sBuffer ) ) ) // return nRet; // pBufPointer = (char*)UtilsStrCpy( sBuffer, "/LastChar ", pEndPointer); // pBufPointer = UtilsIToA( pBufPointer, pAttr->oExternWidths.nLastChar, pEndPointer); // UtilsStrCpy( pBufPointer, "\012", pEndPointer); // if ( OK != ( nRet = StreamWriteStr( pStream, sBuffer ) ) ) // return nRet; // char buf[128]; // char *pbuf = buf; // char *eptr = buf + 127; // if ( OK != ( nRet = StreamWriteEscapeName( pStream, "Widths" ) ) ) // return nRet; // if ( OK != ( nRet = StreamWriteStr( pStream, " [\012" ) ) ) // return nRet; // for ( unsigned int nIndex = 0; nIndex <= pAttr->oExternWidths.nLastChar - pAttr->oExternWidths.nFirstChar; nIndex++) // { // pbuf = UtilsIToA ( pbuf, pAttr->oExternWidths.anWidths[nIndex], eptr); // *pbuf++ = ' '; // if ( 0 == (nIndex + 1) % 16 ) // { // UtilsStrCpy(pbuf, "\012", eptr ); // if ( OK != ( nRet = StreamWriteStr( pStream, buf ) ) ) // return nRet; // pbuf = buf; // } // } // UtilsStrCpy( pbuf, "]\012", eptr ); // if ( OK != ( nRet = StreamWriteStr( pStream, buf ) ) ) // return nRet; // if ( pAttr->pEncoder ) // nRet += pAttr->pEncoder->pWriteFn( pAttr->pEncoder, pStream ); // return nRet; //} //if ( (NULL == pAttr->pToUnicode) || (ToUnicodeBuilt != pAttr->pToUnicode->eType) ) //{ // BasicEncoderAttr pEncoderAttr = (BasicEncoderAttr)pAttr->pEncoder->pAttr; // unsigned long nRet = OK; // char sBuffer[TEMP_BUFFER_SIZE]; // char *pEndPointer = sBuffer + TEMP_BUFFER_SIZE - 1; // char *pBufPointer; // pBufPointer = (char*)UtilsStrCpy( sBuffer, "/FirstChar ", pEndPointer); // pBufPointer = UtilsIToA( pBufPointer, pEncoderAttr->nFirstChar, pEndPointer); // UtilsStrCpy( pBufPointer, "\012", pEndPointer); // if ( OK != ( nRet = StreamWriteStr( pStream, sBuffer ) ) ) // return nRet; // pBufPointer = (char*)UtilsStrCpy( sBuffer, "/LastChar ", pEndPointer); // pBufPointer = UtilsIToA( pBufPointer, pEncoderAttr->nLastChar, pEndPointer); // UtilsStrCpy( pBufPointer, "\012", pEndPointer); // if ( OK != ( nRet = StreamWriteStr( pStream, sBuffer ) ) ) // return nRet; // char buf[128]; // char *pbuf = buf; // char *eptr = buf + 127; // if ( OK != ( nRet = StreamWriteEscapeName( pStream, "Widths" ) ) ) // return nRet; // if ( OK != ( nRet = StreamWriteStr( pStream, " [\012" ) ) ) // return nRet; // for ( unsigned int nIndex = pEncoderAttr->nFirstChar; nIndex <= pEncoderAttr->nLastChar; nIndex++) // { // pbuf = UtilsIToA ( pbuf, pAttr->anWidths[nIndex], eptr); // *pbuf++ = ' '; // if ( 0 == (nIndex + 1) % 16 ) // { // UtilsStrCpy(pbuf, "\012", eptr ); // if ( OK != ( nRet = StreamWriteStr( pStream, buf ) ) ) // return nRet; // pbuf = buf; // } // } // UtilsStrCpy( pbuf, "]\012", eptr ); // if ( OK != ( nRet = StreamWriteStr( pStream, buf ) ) ) // return nRet; // return pAttr->pEncoder->pWriteFn( pAttr->pEncoder, pStream ); //} //else //{ // unsigned long nRet = OK; // // Значит кодировка у нас задана в ToUnicode // FontDef pDef = pAttr->pFontDef; // ToUnicodeBuiltAttr pToUnicodeAttr = (ToUnicodeBuiltAttr)pAttr->pToUnicode->pAttr; // char sBuffer[TEMP_BUFFER_SIZE]; // char *pEndPointer = sBuffer + TEMP_BUFFER_SIZE - 1; // char *pBufPointer; // pBufPointer = (char*)UtilsStrCpy( sBuffer, "/FirstChar ", pEndPointer); // pBufPointer = UtilsIToA( pBufPointer, pToUnicodeAttr->nFirstChar, pEndPointer); // UtilsStrCpy( pBufPointer, "\012", pEndPointer); // if ( OK != ( nRet = StreamWriteStr( pStream, sBuffer ) ) ) // return nRet; // pBufPointer = (char*)UtilsStrCpy( sBuffer, "/LastChar ", pEndPointer); // pBufPointer = UtilsIToA( pBufPointer, pToUnicodeAttr->nLastChar, pEndPointer); // UtilsStrCpy( pBufPointer, "\012", pEndPointer); // if ( OK != ( nRet = StreamWriteStr( pStream, sBuffer ) ) ) // return nRet; // char buf[128]; // char *pbuf = buf; // char *eptr = buf + 127; // if ( OK != ( nRet = StreamWriteEscapeName( pStream, "Widths" ) ) ) // return nRet; // if ( OK != ( nRet = StreamWriteStr( pStream, " [\012" ) ) ) // return nRet; // int nCodesCount; // for ( nCodesCount = 0; nCodesCount < 256 && pToUnicodeAttr->anUnicodeMap[nCodesCount][0] != 0xffff; nCodesCount++ ); // for ( unsigned int nIndex = 0; nIndex < nCodesCount; nIndex++ ) // { // unsigned short unUnicode = pAttr->pToUnicode->pToUnicodeFn( pAttr->pToUnicode, nIndex ); // short nCharWidth = 0; // nCharWidth = TTFontDefGetCharWidth( pDef, unUnicode ); // pbuf = UtilsIToA ( pbuf, nCharWidth, eptr); // *pbuf++ = ' '; // if ( 0 == (nIndex + 1) % 16 ) // { // UtilsStrCpy(pbuf, "\012", eptr ); // if ( OK != ( nRet = StreamWriteStr( pStream, buf ) ) ) // return nRet; // pbuf = buf; // } // } // UtilsStrCpy( pbuf, "]\012", eptr ); // if ( OK != ( nRet = StreamWriteStr( pStream, buf ) ) ) // return nRet; // if ( NULL != pAttr->pEncoder ) // pAttr->pEncoder->pWriteFn( pAttr->pEncoder, pStream ); //} //return OK; } static void CIDOnFree (Dict pObj) { FontAttr pAttr = (FontAttr)pObj->pAttr; if ( pAttr ) { if ( pAttr->anWidths ) FreeMem( pObj->oMMgr, pAttr->anWidths ); if ( pAttr->oExternWidths.anWidths ) FreeMem( pObj->oMMgr, pAttr->oExternWidths.anWidths ); FreeMem( pObj->oMMgr, pAttr ); } } //----- CIDTypeFont основные функции ---------------------------------------------------------------- FontDict Type0FontNew (MMgr oMMgr, FontDef pFontDef, EncoderRecPtr pEncoder, Xref pXref, PToUnicode pToUnicode/* = NULL*/) { unsigned long nRet = OK; Dict pFont = DictNew( oMMgr ); if ( !pFont ) return NULL; pFont->pHeader.nObjClass |= OSUBCLASS_FONT; // проверяем типы фонта и кодировки if ( EncoderTypeDoubleByteBuilt != pEncoder->eType ) { SetError( pFont->oError, AVS_OFFICEPDFWRITER_ERROR_INVALID_ENCODER_TYPE, 0); return NULL; } if ( FontDefCID != pFontDef->eType && FontDefTrueType != pFontDef->eType ) { SetError( pFont->oError, AVS_OFFICEPDFWRITER_ERROR_INVALID_FONTDEF_TYPE, 0); return NULL; } FontAttr pAttr = (FontAttr)GetMem( oMMgr, sizeof(FontAttrRec) ); if ( !pAttr ) { DictFree( pFont ); return NULL; } pFont->pHeader.nObjClass |= OSUBCLASS_FONT; pFont->pWriteFn = NULL; pFont->pFreeFn = CIDOnFree; pFont->pAttr = pAttr; CMapEncoderAttr pEncoderAttr = (CMapEncoderAttr)pEncoder->pAttr; UtilsMemSet( pAttr, 0, sizeof(FontAttrRec) ); pAttr->eWritingMode = pEncoderAttr->eWritingMode; pAttr->pTextWidthFn = CIDTextWidth; pAttr->pMeasureTextFn = CIDMeasureText; pAttr->pFontDef = pFontDef; pAttr->pEncoder = pEncoder; pAttr->pToUnicode = pToUnicode; pAttr->pXref = pXref; pAttr->oExternWidths.bUse = false; pAttr->oExternWidths.nLastChar = 0; pAttr->oExternWidths.nFirstChar = 0; pAttr->oExternWidths.anWidths = NULL; if ( OK != XrefAdd( pXref, pFont ) ) return NULL; nRet += DictAddName( pFont, "Type", "Font"); nRet += DictAddName( pFont, "BaseFont", pFontDef->sBaseFont ); nRet += DictAddName( pFont, "Subtype", "Type0"); if ( FontDefCID == pFontDef->eType ) { nRet += DictAddName( pFont, "Encoding", pEncoder->sName ); } else { pAttr->pCMapStream = CreateCMap( pEncoder, pXref ); if ( pAttr->pCMapStream ) { nRet += DictAdd( pFont, "Encoding", pAttr->pCMapStream ); } else return NULL; } if ( OK != nRet ) return NULL; Array oDescendantFonts = ArrayNew( oMMgr ); if ( !oDescendantFonts ) return NULL; if ( OK != DictAdd( pFont, "DescendantFonts", oDescendantFonts ) ) return NULL; if ( FontDefCID == pFontDef->eType ) { pAttr->pDescendantFont = CIDFontType0New( pFont, pXref ); pAttr->eType = FontType0CID; } else { pAttr->pDescendantFont = CIDFontType2New( pFont, pXref ); pAttr->eType = FontType0TT; } if ( !pAttr->pDescendantFont ) return NULL; else if ( OK != ArrayAdd( oDescendantFonts, pAttr->pDescendantFont ) ) return NULL; return pFont; } FontDict Type0FontNew2 (MMgr oMMgr, FontDef pFontDef, EncoderRecPtr pEncoder, Xref pXref, PToUnicode pToUnicode/* = NULL*/) { unsigned long nRet = OK; Dict pFont = DictNew( oMMgr ); if ( !pFont ) return NULL; pFont->pHeader.nObjClass |= OSUBCLASS_FONT; // проверяем типы фонта и кодировки if ( pEncoder && EncoderTypeDoubleByteStream != pEncoder->eType ) { SetError( pFont->oError, AVS_OFFICEPDFWRITER_ERROR_INVALID_ENCODER_TYPE, 0); return NULL; } if ( FontDefCID != pFontDef->eType ) { SetError( pFont->oError, AVS_OFFICEPDFWRITER_ERROR_INVALID_FONTDEF_TYPE, 0); return NULL; } FontAttr pAttr = (FontAttr)GetMem( oMMgr, sizeof(FontAttrRec) ); if ( !pAttr ) { DictFree( pFont ); return NULL; } pFont->pHeader.nObjClass |= OSUBCLASS_FONT; pFont->pBeforeWriteFn = CIDBeforeWrite; pFont->pWriteFn = CIDOnWrite; pFont->pFreeFn = CIDOnFree; pFont->pAttr = pAttr; UtilsMemSet( pAttr, 0, sizeof(FontAttrRec) ); // pAttr->eWritingMode = pEncoderAttr->eWritingMode; pAttr->pTextWidthFn = CIDTextWidth2; pAttr->pMeasureTextFn = CIDMeasureText2; pAttr->pFontDef = pFontDef; pAttr->pEncoder = pEncoder; pAttr->pToUnicode = pToUnicode; pAttr->pXref = pXref; pAttr->oExternWidths.bUse = false; pAttr->oExternWidths.nLastChar = 0; pAttr->oExternWidths.nFirstChar = 0; pAttr->oExternWidths.anWidths = NULL; if ( OK != XrefAdd( pXref, pFont ) ) return NULL; nRet += DictAddName( pFont, "Type", "Font"); nRet += DictAddName( pFont, "BaseFont", pFontDef->sBaseFont ); nRet += DictAddName( pFont, "Subtype", "Type0"); if ( NULL != pEncoder ) { CMapEncoderStreamAttr pEncoderAttr = (CMapEncoderStreamAttr)pEncoder->pAttr; if ( 0 == pEncoderAttr->nType ) { nRet += DictAddName( pFont, "Encoding", pEncoder->sName ); } else // if ( 1 == pEncoderAttr->nType ) { Dict pEncoderData = DictStreamNew( oMMgr, pXref ); if ( !pEncoderData ) return NULL; nRet += DictAdd( pFont, "Encoding", pEncoderData ); if ( OK != StreamWriteToStream( pEncoderAttr->pStream, pEncoderData->pStream, STREAM_FILTER_NONE, NULL ) ) return NULL; #ifdef FILTER_FLATE_DECODE_ENABLED pEncoderData->nFilter |= STREAM_FILTER_FLATE_DECODE; #endif nRet += DictAddName( pEncoderData, "Type", "CMap" ); if ( strlen( pEncoderAttr->sCMapName ) > 0 ) nRet += DictAddName( pEncoderData, "CMapName", pEncoderAttr->sCMapName ); if ( strlen( pEncoderAttr->sRegistry ) > 0 && strlen( pEncoderAttr->sOrdering ) > 0 ) { Dict pInfo = DictNew( oMMgr ); nRet += DictAdd( pInfo, "Registry", StringNew( oMMgr, pEncoderAttr->sRegistry, NULL ) ); nRet += DictAdd( pInfo, "Ordering", StringNew( oMMgr, pEncoderAttr->sOrdering, NULL ) ); nRet += DictAddNumber( pInfo, "Supplement", pEncoderAttr->nSupplement ); nRet += DictAdd( pEncoderData, "CIDSystemInfo", pInfo ); } nRet += DictAddNumber( pEncoderData, "WMode", (int)pEncoderAttr->eWritingMode ); if ( strlen( pEncoderAttr->uUseCMap.sName ) > 0 ) { nRet += DictAddName( pEncoderData, "UseCMap", pEncoderAttr->uUseCMap.sName ); } else if ( StreamValidate( pEncoderAttr->uUseCMap.pStream ) ) { Dict pUseCMap = DictStreamNew( oMMgr, pXref ); if ( !pUseCMap ) return NULL; if ( OK != StreamWriteToStream( pEncoderAttr->uUseCMap.pStream, pUseCMap->pStream, STREAM_FILTER_NONE, NULL ) ) return NULL; #ifdef FILTER_FLATE_DECODE_ENABLED pUseCMap->nFilter |= STREAM_FILTER_FLATE_DECODE; #endif nRet += DictAdd( pEncoderData, "UseCMap", pUseCMap ); } } } Array oDescendantFonts = ArrayNew( oMMgr ); if ( !oDescendantFonts ) return NULL; if ( OK != DictAdd( pFont, "DescendantFonts", oDescendantFonts ) ) return NULL; pAttr->pDescendantFont = CIDFontTypeNew( pFont, pXref ); if ( !pAttr->pDescendantFont ) return NULL; else if ( OK != ArrayAdd( oDescendantFonts, pAttr->pDescendantFont ) ) return NULL; if ( NULL != pToUnicode ) { Dict pToUnicodeDict = DictStreamNew( oMMgr, pXref ); pToUnicode->pWriteFn( pToUnicode, pToUnicodeDict->pStream ); #ifdef FILTER_FLATE_DECODE_ENABLED pToUnicodeDict->nFilter |= STREAM_FILTER_FLATE_DECODE; #endif nRet += DictAdd( pFont, "ToUnicode", pToUnicodeDict ); } if ( OK != nRet ) return NULL; return pFont; }