diff options
Diffstat (limited to 'vcl/source/gdi')
-rw-r--r-- | vcl/source/gdi/outdev3.cxx | 2 | ||||
-rw-r--r-- | vcl/source/gdi/pdfwriter_impl.cxx | 1272 | ||||
-rw-r--r-- | vcl/source/gdi/pdfwriter_impl.hxx | 55 |
3 files changed, 699 insertions, 630 deletions
diff --git a/vcl/source/gdi/outdev3.cxx b/vcl/source/gdi/outdev3.cxx index 51aad0790a26..28fa4f8f5461 100644 --- a/vcl/source/gdi/outdev3.cxx +++ b/vcl/source/gdi/outdev3.cxx @@ -5601,6 +5601,8 @@ void OutputDevice::DrawTextArray( const Point& rStartPt, const String& rStr, if ( !IsDeviceOutputNecessary() ) return; + if( !mpGraphics && !ImplGetGraphics() ) + return; if( mbInitClipRegion ) ImplInitClipRegion(); if( mbOutputClipped ) diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx index c0205f1f325d..d42e736960d2 100644 --- a/vcl/source/gdi/pdfwriter_impl.cxx +++ b/vcl/source/gdi/pdfwriter_impl.cxx @@ -2814,37 +2814,6 @@ sal_Int32 PDFWriterImpl::emitBuiltinFont( const ImplFontData* pFont, sal_Int32 n return nFontObject; } -typedef int ThreeInts[3]; -static bool getPfbSegmentLengths( const unsigned char* pFontBytes, int nByteLen, - ThreeInts& rSegmentLengths ) -{ - if( !pFontBytes || (nByteLen < 0) ) - return false; - const unsigned char* pPtr = pFontBytes; - const unsigned char* pEnd = pFontBytes + nByteLen; - - for( int i = 0; i < 3; ++i) { - // read segment1 header - if( pPtr+6 >= pEnd ) - return false; - if( (pPtr[0] != 0x80) || (pPtr[1] >= 0x03) ) - return false; - const int nLen = (pPtr[5]<<24) + (pPtr[4]<<16) + (pPtr[3]<<8) + pPtr[2]; - if( nLen <= 0) - return false; - rSegmentLengths[i] = nLen; - pPtr += nLen + 6; - } - - // read segment-end header - if( pPtr+2 >= pEnd ) - return false; - if( (pPtr[0] != 0x80) || (pPtr[1] != 0x03) ) - return false; - - return true; -} - std::map< sal_Int32, sal_Int32 > PDFWriterImpl::emitSystemFont( const ImplFontData* pFont, EmbedFont& rEmbed ) { std::map< sal_Int32, sal_Int32 > aRet; @@ -2960,6 +2929,41 @@ std::map< sal_Int32, sal_Int32 > PDFWriterImpl::emitSystemFont( const ImplFontDa return aRet; } +typedef int ThreeInts[3]; +static bool getPfbSegmentLengths( const unsigned char* pFontBytes, int nByteLen, + ThreeInts& rSegmentLengths ) +{ + if( !pFontBytes || (nByteLen < 0) ) + return false; + const unsigned char* pPtr = pFontBytes; + const unsigned char* pEnd = pFontBytes + nByteLen; + + for( int i = 0; i < 3; ++i) { + // read segment1 header + if( pPtr+6 >= pEnd ) + return false; + if( (pPtr[0] != 0x80) || (pPtr[1] >= 0x03) ) + return false; + const int nLen = (pPtr[5]<<24) + (pPtr[4]<<16) + (pPtr[3]<<8) + pPtr[2]; + if( nLen <= 0) + return false; + rSegmentLengths[i] = nLen; + pPtr += nLen + 6; + } + + // read segment-end header + if( pPtr+2 >= pEnd ) + return false; + if( (pPtr[0] != 0x80) || (pPtr[1] != 0x03) ) + return false; + + return true; +} + +struct FontException : public std::exception +{ +}; + // TODO: always subset instead of embedding the full font => this method becomes obsolete then std::map< sal_Int32, sal_Int32 > PDFWriterImpl::emitEmbeddedFont( const ImplFontData* pFont, EmbedFont& rEmbed ) { @@ -2979,10 +2983,16 @@ std::map< sal_Int32, sal_Int32 > PDFWriterImpl::emitEmbeddedFont( const ImplFont sal_Int32 nToUnicodeStream = 0; sal_uInt8 nEncoding[256]; sal_Ucs nEncodedCodes[256]; + std::vector<sal_Ucs> aUnicodes; + aUnicodes.reserve( 256 ); + sal_Int32 pUnicodesPerGlyph[256]; + sal_Int32 pEncToUnicodeIndex[256]; if( pEncoding ) { - memset( nEncodedCodes, 0, sizeof(nEncodedCodes) ); - memset( nEncoding, 0, sizeof(nEncoding) ); + rtl_zeroMemory( nEncoding, sizeof(nEncoding) ); + rtl_zeroMemory( nEncodedCodes, sizeof(nEncodedCodes) ); + rtl_zeroMemory( pUnicodesPerGlyph, sizeof(pUnicodesPerGlyph) ); + rtl_zeroMemory( pEncToUnicodeIndex, sizeof(pEncToUnicodeIndex) ); for( Ucs2SIntMap::const_iterator it = pEncoding->begin(); it != pEncoding->end(); ++it ) { if( it->second != -1 ) @@ -2990,6 +3000,9 @@ std::map< sal_Int32, sal_Int32 > PDFWriterImpl::emitEmbeddedFont( const ImplFont sal_Int32 nCode = (sal_Int32)(it->second & 0x000000ff); nEncoding[ nCode ] = static_cast<sal_uInt8>( nCode ); nEncodedCodes[ nCode ] = it->first; + pEncToUnicodeIndex[ nCode ] = static_cast<sal_Int32>(aUnicodes.size()); + aUnicodes.push_back( it->first ); + pUnicodesPerGlyph[ nCode ] = 1; } } } @@ -2999,553 +3012,525 @@ std::map< sal_Int32, sal_Int32 > PDFWriterImpl::emitEmbeddedFont( const ImplFont const unsigned char* pFontData = NULL; long nFontLen = 0; sal_Int32 nLength1, nLength2; - if( (pFontData = (const unsigned char*)m_pReferenceDevice->mpGraphics->GetEmbedFontData( pFont, nEncodedCodes, pWidths, aInfo, &nFontLen )) != NULL ) - { - if( (aInfo.m_nFontType & FontSubsetInfo::ANY_TYPE1) == 0 ) - goto streamend; - // see whether it is pfb or pfa; if it is a pfb, fill ranges - // of 6 bytes that are not part of the font program - std::list< int > aSections; - std::list< int >::const_iterator it; - int nIndex = 0; - while( pFontData[nIndex] == 0x80 && nIndex < nFontLen-1 ) - { - aSections.push_back( nIndex ); - if( pFontData[nIndex+1] == 0x03 ) - break; - sal_Int32 nBytes = + try + { + if( (pFontData = (const unsigned char*)m_pReferenceDevice->mpGraphics->GetEmbedFontData( pFont, nEncodedCodes, pWidths, aInfo, &nFontLen )) != NULL ) + { + if( (aInfo.m_nFontType & FontSubsetInfo::ANY_TYPE1) == 0 ) + throw FontException(); + // see whether it is pfb or pfa; if it is a pfb, fill ranges + // of 6 bytes that are not part of the font program + std::list< int > aSections; + std::list< int >::const_iterator it; + int nIndex = 0; + while( pFontData[nIndex] == 0x80 && nIndex < nFontLen-1 ) + { + aSections.push_back( nIndex ); + if( pFontData[nIndex+1] == 0x03 ) + break; + sal_Int32 nBytes = ((sal_Int32)pFontData[nIndex+2]) | ((sal_Int32)pFontData[nIndex+3]) << 8 | ((sal_Int32)pFontData[nIndex+4]) << 16 | ((sal_Int32)pFontData[nIndex+5]) << 24; - nIndex += nBytes+6; - } - - // search for eexec - // TODO: use getPfbSegmentLengths() if possible to skip the search thingies below - nIndex = 0; - int nEndAsciiIndex; - int nBeginBinaryIndex; - int nEndBinaryIndex; - do - { - while( nIndex < nFontLen-4 && - ( pFontData[nIndex] != 'e' || - pFontData[nIndex+1] != 'e' || - pFontData[nIndex+2] != 'x' || - pFontData[nIndex+3] != 'e' || - pFontData[nIndex+4] != 'c' - ) - ) - nIndex++; - // check whether we are in a excluded section - for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it ) - ; - } while( it != aSections.end() && nIndex < nFontLen-4 ); - // this should end the ascii part - if( nIndex > nFontLen-5 ) - goto streamend; - - nEndAsciiIndex = nIndex+4; - // now count backwards until we can account for 512 '0' - // which is the endmarker of the (hopefully) binary data - // do not count the pfb header sections - int nFound = 0; - nIndex = nFontLen-1; - while( nIndex > 0 && nFound < 512 ) - { - for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it ) - ; - if( it == aSections.end() ) - { - // inside the 512 '0' block there may only be whitespace - // according to T1 spec; probably it would be to simple - // if all fonts complied - if( pFontData[nIndex] == '0' ) - nFound++; - else if( nFound > 0 && - pFontData[nIndex] != '\r' && - pFontData[nIndex] != '\t' && - pFontData[nIndex] != '\n' && - pFontData[nIndex] != ' ' ) - break; + nIndex += nBytes+6; } - nIndex--; - } - - if( nIndex < 1 || nIndex <= nEndAsciiIndex ) - goto streamend; - // there may be whitespace to ignore before the 512 '0' - while( pFontData[nIndex] == '\r' || pFontData[nIndex] == '\n' ) - { - nIndex--; - for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it ) - ; - if( it != aSections.end() ) - { - nIndex = (*it)-1; - break; // this is surely a binary boundary, in ascii case it wouldn't matter - } - } - nEndBinaryIndex = nIndex; - // and count forward again to the point where we have nFound '0' - // to get the corect value for nLength3 - sal_Int32 nLength3 = 0; - sal_Int32 nL3Index = nIndex; - while( nFound && nL3Index < nFontLen ) - { - for( it = aSections.begin(); it != aSections.end() && (nL3Index < *it || nL3Index > ((*it) + 5) ); ++it ) - ; - if( it == aSections.end() ) + // search for eexec + // TODO: use getPfbSegmentLengths() if possible to skip the search thingies below + nIndex = 0; + int nEndAsciiIndex; + int nBeginBinaryIndex; + int nEndBinaryIndex; + do { - // inside the 512 '0' block there may only be whitespace - // according to T1 spec; probably it would be to simple - // if all fonts complied - if( pFontData[nL3Index] == '0' ) - nFound--; - nLength3++; - } - nL3Index++; - } - - // search for beginning of binary section - nBeginBinaryIndex = nEndAsciiIndex; - do - { - nBeginBinaryIndex++; - for( it = aSections.begin(); it != aSections.end() && (nBeginBinaryIndex < *it || nBeginBinaryIndex > ((*it) + 5) ); ++it ) - ; - } while( nBeginBinaryIndex < nEndBinaryIndex && - ( pFontData[nBeginBinaryIndex] == '\r' || - pFontData[nBeginBinaryIndex] == '\n' || - it != aSections.end() ) ); - - // it seems to be vital to copy the exact whitespace between binary data - // and eexec, else a invalid font results. so make nEndAsciiIndex - // always immediate in front of nBeginBinaryIndex - nEndAsciiIndex = nBeginBinaryIndex-1; - for( it = aSections.begin(); it != aSections.end() && (nEndAsciiIndex < *it || nEndAsciiIndex > ((*it)+5)); ++it ) - ; - if( it != aSections.end() ) - nEndAsciiIndex = (*it)-1; - - nLength1 = nEndAsciiIndex+1; // including the last character - for( it = aSections.begin(); it != aSections.end() && *it < nEndAsciiIndex; ++it ) - nLength1 -= 6; // decrease by pfb section size - - // if the first four bytes are all ascii hex characters, then binary data - // has to be converted to real binary data - for( nIndex = 0; nIndex < 4 && - ( ( pFontData[ nBeginBinaryIndex+nIndex ] >= '0' && pFontData[ nBeginBinaryIndex+nIndex ] <= '9' ) || - ( pFontData[ nBeginBinaryIndex+nIndex ] >= 'a' && pFontData[ nBeginBinaryIndex+nIndex ] <= 'f' ) || - ( pFontData[ nBeginBinaryIndex+nIndex ] >= 'A' && pFontData[ nBeginBinaryIndex+nIndex ] <= 'F' ) - ); ++nIndex ) - ; - bool bConvertHexData = true; - if( nIndex < 4 ) - { - bConvertHexData = false; - nLength2 = nEndBinaryIndex - nBeginBinaryIndex + 1; // include the last byte - for( it = aSections.begin(); it != aSections.end(); ++it ) - if( *it > nBeginBinaryIndex && *it < nEndBinaryIndex ) - nLength2 -= 6; - } - else - { - // count the hex ascii characters to get nLength2 - nLength2 = 0; - int nNextSectionIndex = 0; - for( it = aSections.begin(); it != aSections.end() && *it < nBeginBinaryIndex; ++it ) - ; - if( it != aSections.end() ) - nNextSectionIndex = *it; - for( nIndex = nBeginBinaryIndex; nIndex <= nEndBinaryIndex; nIndex++ ) + while( nIndex < nFontLen-4 && + ( pFontData[nIndex] != 'e' || + pFontData[nIndex+1] != 'e' || + pFontData[nIndex+2] != 'x' || + pFontData[nIndex+3] != 'e' || + pFontData[nIndex+4] != 'c' + ) + ) + nIndex++; + // check whether we are in a excluded section + for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it ) + ; + } while( it != aSections.end() && nIndex < nFontLen-4 ); + // this should end the ascii part + if( nIndex > nFontLen-5 ) + throw FontException(); + + nEndAsciiIndex = nIndex+4; + // now count backwards until we can account for 512 '0' + // which is the endmarker of the (hopefully) binary data + // do not count the pfb header sections + int nFound = 0; + nIndex = nFontLen-1; + while( nIndex > 0 && nFound < 512 ) { - if( nIndex == nNextSectionIndex ) + for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it ) + ; + if( it == aSections.end() ) { - nIndex += 6; - ++it; - nNextSectionIndex = (it == aSections.end() ? 0 : *it ); + // inside the 512 '0' block there may only be whitespace + // according to T1 spec; probably it would be to simple + // if all fonts complied + if( pFontData[nIndex] == '0' ) + nFound++; + else if( nFound > 0 && + pFontData[nIndex] != '\r' && + pFontData[nIndex] != '\t' && + pFontData[nIndex] != '\n' && + pFontData[nIndex] != ' ' ) + break; } - if( ( pFontData[ nIndex ] >= '0' && pFontData[ nIndex ] <= '9' ) || - ( pFontData[ nIndex ] >= 'a' && pFontData[ nIndex ] <= 'f' ) || - ( pFontData[ nIndex ] >= 'A' && pFontData[ nIndex ] <= 'F' ) ) - nLength2++; + nIndex--; } - DBG_ASSERT( !(nLength2 & 1), "uneven number of hex chars in binary pfa section" ); - nLength2 /= 2; - } - - // now we can actually write the font stream ! -#if OSL_DEBUG_LEVEL > 1 - { - OStringBuffer aLine( " PDFWriterImpl::emitEmbeddedFont" ); - emitComment( aLine.getStr() ); - } -#endif - OStringBuffer aLine( 512 ); - nStreamObject = createObject(); - if( !updateObject(nStreamObject)) - goto streamend; - sal_Int32 nStreamLengthObject = createObject(); - aLine.append( nStreamObject ); - aLine.append( " 0 obj\n" - "<</Length " ); - aLine.append( nStreamLengthObject ); - aLine.append( " 0 R" -#ifndef DEBUG_DISABLE_PDFCOMPRESSION - "/Filter/FlateDecode" -#endif - "/Length1 " ); - aLine.append( nLength1 ); - aLine.append( " /Length2 " ); - aLine.append( nLength2 ); - aLine.append( " /Length3 "); - aLine.append( nLength3 ); - aLine.append( ">>\n" - "stream\n" ); - if( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) - goto streamend; - sal_uInt64 nBeginStreamPos = 0; - osl_getFilePos( m_aFile, &nBeginStreamPos ); + if( nIndex < 1 || nIndex <= nEndAsciiIndex ) + throw FontException(); - beginCompression(); - checkAndEnableStreamEncryption( nStreamObject ); - - // write ascii section - if( aSections.begin() == aSections.end() ) - { - if( ! writeBuffer( pFontData, nEndAsciiIndex+1 ) ) - { - endCompression(); - disableStreamEncryption(); - goto streamend; - } - } - else - { - // first section always starts at 0 - it = aSections.begin(); - nIndex = (*it)+6; - ++it; - while( *it < nEndAsciiIndex ) + // nLength3 is the rest of the file - excluding any section headers + // nIndex now points to the first of the 512 '0' characters marking the + // fixed content portion + sal_Int32 nLength3 = nFontLen - nIndex; + for( it = aSections.begin(); it != aSections.end(); ++it ) { - if( ! writeBuffer( pFontData+nIndex, (*it)-nIndex ) ) + if( *it >= nIndex ) { - endCompression(); - disableStreamEncryption(); - goto streamend; + // special case: nIndex inside a section marker + if( nIndex >= (*it) && (*it)+5 > nIndex ) + nLength3 -= (*it)+5 - nIndex; + else + { + if( *it < nFontLen - 6 ) + nLength3 -= 6; + else // the last section 0x8003 is only 2 bytes after all + nLength3 -= (nFontLen - *it); + } } - nIndex = (*it)+6; - ++it; } - // write partial last section - if( ! writeBuffer( pFontData+nIndex, nEndAsciiIndex-nIndex+1 ) ) - { - endCompression(); - disableStreamEncryption(); - goto streamend; - } - } - // write binary section - if( ! bConvertHexData ) - { - if( aSections.begin() == aSections.end() ) + // there may be whitespace to ignore before the 512 '0' + while( pFontData[nIndex] == '\r' || pFontData[nIndex] == '\n' ) { - if( ! writeBuffer( pFontData+nBeginBinaryIndex, nFontLen-nBeginBinaryIndex ) ) + nIndex--; + for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it ) + ; + if( it != aSections.end() ) { - endCompression(); - disableStreamEncryption(); - goto streamend; + nIndex = (*it)-1; + break; // this is surely a binary boundary, in ascii case it wouldn't matter } } - else + nEndBinaryIndex = nIndex; + + // search for beginning of binary section + nBeginBinaryIndex = nEndAsciiIndex; + do { - for( it = aSections.begin(); *it < nBeginBinaryIndex; ++it ) + nBeginBinaryIndex++; + for( it = aSections.begin(); it != aSections.end() && (nBeginBinaryIndex < *it || nBeginBinaryIndex > ((*it) + 5) ); ++it ) ; - // write first partial section - if( ! writeBuffer( pFontData+nBeginBinaryIndex, (*it) - nBeginBinaryIndex ) ) - { - endCompression(); - disableStreamEncryption(); - goto streamend; - } - // write following sections - while( it != aSections.end() ) - { - nIndex = (*it)+6; - ++it; - if( nIndex < nFontLen ) // last section marker is usually the EOF which has only 2 bytes + } while( nBeginBinaryIndex < nEndBinaryIndex && + ( pFontData[nBeginBinaryIndex] == '\r' || + pFontData[nBeginBinaryIndex] == '\n' || + it != aSections.end() ) ); + + // it seems to be vital to copy the exact whitespace between binary data + // and eexec, else a invalid font results. so make nEndAsciiIndex + // always immediate in front of nBeginBinaryIndex + nEndAsciiIndex = nBeginBinaryIndex-1; + for( it = aSections.begin(); it != aSections.end() && (nEndAsciiIndex < *it || nEndAsciiIndex > ((*it)+5)); ++it ) + ; + if( it != aSections.end() ) + nEndAsciiIndex = (*it)-1; + + nLength1 = nEndAsciiIndex+1; // including the last character + for( it = aSections.begin(); it != aSections.end() && *it < nEndAsciiIndex; ++it ) + nLength1 -= 6; // decrease by pfb section size + + // if the first four bytes are all ascii hex characters, then binary data + // has to be converted to real binary data + for( nIndex = 0; nIndex < 4 && + ( ( pFontData[ nBeginBinaryIndex+nIndex ] >= '0' && pFontData[ nBeginBinaryIndex+nIndex ] <= '9' ) || + ( pFontData[ nBeginBinaryIndex+nIndex ] >= 'a' && pFontData[ nBeginBinaryIndex+nIndex ] <= 'f' ) || + ( pFontData[ nBeginBinaryIndex+nIndex ] >= 'A' && pFontData[ nBeginBinaryIndex+nIndex ] <= 'F' ) + ); ++nIndex ) + ; + bool bConvertHexData = true; + if( nIndex < 4 ) + { + bConvertHexData = false; + nLength2 = nEndBinaryIndex - nBeginBinaryIndex + 1; // include the last byte + for( it = aSections.begin(); it != aSections.end(); ++it ) + if( *it > nBeginBinaryIndex && *it < nEndBinaryIndex ) + nLength2 -= 6; + } + else { - sal_Int32 nSectionLen = (it == aSections.end()) ? nFontLen - nIndex : (*it) - nIndex; - if( ! writeBuffer( pFontData+nIndex, nSectionLen ) ) + // count the hex ascii characters to get nLength2 + nLength2 = 0; + int nNextSectionIndex = 0; + for( it = aSections.begin(); it != aSections.end() && *it < nBeginBinaryIndex; ++it ) + ; + if( it != aSections.end() ) + nNextSectionIndex = *it; + for( nIndex = nBeginBinaryIndex; nIndex <= nEndBinaryIndex; nIndex++ ) { - endCompression(); - disableStreamEncryption(); - goto streamend; + if( nIndex == nNextSectionIndex ) + { + nIndex += 6; + ++it; + nNextSectionIndex = (it == aSections.end() ? 0 : *it ); + } + if( ( pFontData[ nIndex ] >= '0' && pFontData[ nIndex ] <= '9' ) || + ( pFontData[ nIndex ] >= 'a' && pFontData[ nIndex ] <= 'f' ) || + ( pFontData[ nIndex ] >= 'A' && pFontData[ nIndex ] <= 'F' ) ) + nLength2++; } + DBG_ASSERT( !(nLength2 & 1), "uneven number of hex chars in binary pfa section" ); + nLength2 /= 2; } - } - } - } - else - { - unsigned char* pWriteBuffer = (unsigned char*)rtl_allocateMemory( nLength2 ); - memset( pWriteBuffer, 0, nLength2 ); - int nWriteIndex = 0; - - int nNextSectionIndex = 0; - for( it = aSections.begin(); it != aSections.end() && *it < nBeginBinaryIndex; ++it ) - ; - if( it != aSections.end() ) - nNextSectionIndex = *it; - for( nIndex = nBeginBinaryIndex; nIndex <= nEndBinaryIndex; nIndex++ ) - { - if( nIndex == nNextSectionIndex ) - { - nIndex += 6; - ++it; - nNextSectionIndex = (it == aSections.end() ? nFontLen : *it ); - } - unsigned char cNibble = 0x80; - if( pFontData[ nIndex ] >= '0' && pFontData[ nIndex ] <= '9' ) - cNibble = pFontData[nIndex] - '0'; - else if( pFontData[ nIndex ] >= 'a' && pFontData[ nIndex ] <= 'f' ) - cNibble = pFontData[nIndex] - 'a' + 10; - else if( pFontData[ nIndex ] >= 'A' && pFontData[ nIndex ] <= 'F' ) - cNibble = pFontData[nIndex] - 'A' + 10; - if( cNibble != 0x80 ) - { - if( !(nWriteIndex & 1 ) ) - cNibble <<= 4; - pWriteBuffer[ nWriteIndex/2 ] |= cNibble; - nWriteIndex++; - } - } - if( ! writeBuffer( pWriteBuffer, nLength2 ) ) - { - endCompression(); - disableStreamEncryption(); - goto streamend; - } - rtl_freeMemory( pWriteBuffer ); - if( aSections.empty() ) - { - if( ! writeBuffer( pFontData+nIndex, nFontLen-nIndex ) ) - { - endCompression(); - disableStreamEncryption(); - goto streamend; - } - } - else - { - // write rest of this section - if( nIndex < nNextSectionIndex ) - { - if( ! writeBuffer( pFontData+nIndex, nNextSectionIndex - nIndex ) ) + // now we can actually write the font stream ! + #if OSL_DEBUG_LEVEL > 1 { - endCompression(); - disableStreamEncryption(); - goto streamend; + OStringBuffer aLine( " PDFWriterImpl::emitEmbeddedFont" ); + emitComment( aLine.getStr() ); } - } - // write following sections - while( it != aSections.end() ) - { - nIndex = (*it)+6; - ++it; - if( nIndex < nFontLen ) // last section marker is usually the EOF which has only 2 bytes + #endif + OStringBuffer aLine( 512 ); + nStreamObject = createObject(); + if( !updateObject(nStreamObject)) + throw FontException(); + sal_Int32 nStreamLengthObject = createObject(); + aLine.append( nStreamObject ); + aLine.append( " 0 obj\n" + "<</Length " ); + aLine.append( nStreamLengthObject ); + aLine.append( " 0 R" + #ifndef DEBUG_DISABLE_PDFCOMPRESSION + "/Filter/FlateDecode" + #endif + "/Length1 " ); + aLine.append( nLength1 ); + aLine.append( " /Length2 " ); + aLine.append( nLength2 ); + aLine.append( " /Length3 "); + aLine.append( nLength3 ); + aLine.append( ">>\n" + "stream\n" ); + if( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) + throw FontException(); + + sal_uInt64 nBeginStreamPos = 0; + osl_getFilePos( m_aFile, &nBeginStreamPos ); + + beginCompression(); + checkAndEnableStreamEncryption( nStreamObject ); + + // write ascii section + if( aSections.begin() == aSections.end() ) + { + if( ! writeBuffer( pFontData, nEndAsciiIndex+1 ) ) + throw FontException(); + } + else { - sal_Int32 nSectionLen = (it == aSections.end()) ? nFontLen - nIndex : (*it) - nIndex; - if( ! writeBuffer( pFontData+nIndex, nSectionLen ) ) + // first section always starts at 0 + it = aSections.begin(); + nIndex = (*it)+6; + ++it; + while( *it < nEndAsciiIndex ) { - endCompression(); - disableStreamEncryption(); - goto streamend; + if( ! writeBuffer( pFontData+nIndex, (*it)-nIndex ) ) + throw FontException(); + nIndex = (*it)+6; + ++it; } + // write partial last section + if( ! writeBuffer( pFontData+nIndex, nEndAsciiIndex-nIndex+1 ) ) + throw FontException(); } - } - } - } - endCompression(); - disableStreamEncryption(); - - - sal_uInt64 nEndStreamPos = 0; - osl_getFilePos( m_aFile, &nEndStreamPos ); - // and finally close the stream - aLine.setLength( 0 ); - aLine.append( "\nendstream\nendobj\n\n" ); - if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) - goto streamend; + // write binary section + if( ! bConvertHexData ) + { + if( aSections.begin() == aSections.end() ) + { + if( ! writeBuffer( pFontData+nBeginBinaryIndex, nFontLen-nBeginBinaryIndex ) ) + throw FontException(); + } + else + { + for( it = aSections.begin(); *it < nBeginBinaryIndex; ++it ) + ; + // write first partial section + if( ! writeBuffer( pFontData+nBeginBinaryIndex, (*it) - nBeginBinaryIndex ) ) + throw FontException(); + // write following sections + while( it != aSections.end() ) + { + nIndex = (*it)+6; + ++it; + if( nIndex < nFontLen ) // last section marker is usually the EOF which has only 2 bytes + { + sal_Int32 nSectionLen = (it == aSections.end()) ? nFontLen - nIndex : (*it) - nIndex; + if( ! writeBuffer( pFontData+nIndex, nSectionLen ) ) + throw FontException(); + } + } + } + } + else + { + boost::shared_array<unsigned char> pWriteBuffer( new unsigned char[ nLength2 ] ); + rtl_zeroMemory( pWriteBuffer.get(), nLength2 ); + int nWriteIndex = 0; + + int nNextSectionIndex = 0; + for( it = aSections.begin(); it != aSections.end() && *it < nBeginBinaryIndex; ++it ) + ; + if( it != aSections.end() ) + nNextSectionIndex = *it; + for( nIndex = nBeginBinaryIndex; nIndex <= nEndBinaryIndex; nIndex++ ) + { + if( nIndex == nNextSectionIndex ) + { + nIndex += 6; + ++it; + nNextSectionIndex = (it == aSections.end() ? nFontLen : *it ); + } + unsigned char cNibble = 0x80; + if( pFontData[ nIndex ] >= '0' && pFontData[ nIndex ] <= '9' ) + cNibble = pFontData[nIndex] - '0'; + else if( pFontData[ nIndex ] >= 'a' && pFontData[ nIndex ] <= 'f' ) + cNibble = pFontData[nIndex] - 'a' + 10; + else if( pFontData[ nIndex ] >= 'A' && pFontData[ nIndex ] <= 'F' ) + cNibble = pFontData[nIndex] - 'A' + 10; + if( cNibble != 0x80 ) + { + if( !(nWriteIndex & 1 ) ) + cNibble <<= 4; + pWriteBuffer.get()[ nWriteIndex/2 ] |= cNibble; + nWriteIndex++; + } + } + if( ! writeBuffer( pWriteBuffer.get(), nLength2 ) ) + throw FontException(); + if( aSections.empty() ) + { + if( ! writeBuffer( pFontData+nIndex, nFontLen-nIndex ) ) + throw FontException(); + } + else + { + // write rest of this section + if( nIndex < nNextSectionIndex ) + { + if( ! writeBuffer( pFontData+nIndex, nNextSectionIndex - nIndex ) ) + throw FontException(); + } + // write following sections + while( it != aSections.end() ) + { + nIndex = (*it)+6; + ++it; + if( nIndex < nFontLen ) // last section marker is usually the EOF which has only 2 bytes + { + sal_Int32 nSectionLen = (it == aSections.end()) ? nFontLen - nIndex : (*it) - nIndex; + if( ! writeBuffer( pFontData+nIndex, nSectionLen ) ) + throw FontException(); + } + } + } + } + endCompression(); + disableStreamEncryption(); - // write stream length object - aLine.setLength( 0 ); - if( ! updateObject( nStreamLengthObject ) ) - goto streamend; - aLine.append( nStreamLengthObject ); - aLine.append( " 0 obj\n" ); - aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos ) ); - aLine.append( "\nendobj\n\n" ); - if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) - goto streamend; - } - else - { - rtl::OStringBuffer aErrorComment( 256 ); - aErrorComment.append( "GetEmbedFontData failed for font \"" ); - aErrorComment.append( OUStringToOString( pFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ) ); - aErrorComment.append( '\"' ); - if( pFont->GetSlant() == ITALIC_NORMAL ) - aErrorComment.append( " italic" ); - else if( pFont->GetSlant() == ITALIC_OBLIQUE ) - aErrorComment.append( " oblique" ); - aErrorComment.append( " weight=" ); - aErrorComment.append( sal_Int32(pFont->GetWeight()) ); - emitComment( aErrorComment.getStr() ); - } - - if( nStreamObject ) - // write font descriptor - nFontDescriptor = emitFontDescriptor( pFont, aInfo, 0, nStreamObject ); - if( nFontDescriptor ) - { - if( pEncoding ) - nToUnicodeStream = createToUnicodeCMap( nEncoding, nEncodedCodes, sizeof(nEncoding)/sizeof(nEncoding[0]) ); + sal_uInt64 nEndStreamPos = 0; + osl_getFilePos( m_aFile, &nEndStreamPos ); - // write font object - sal_Int32 nObject = createObject(); - if( ! updateObject( nObject ) ) - goto streamend; + // and finally close the stream + aLine.setLength( 0 ); + aLine.append( "\nendstream\nendobj\n\n" ); + if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) + throw FontException(); - OStringBuffer aLine( 1024 ); - aLine.append( nObject ); - aLine.append( " 0 obj\n" - "<</Type/Font/Subtype/Type1/BaseFont/" ); - appendName( aInfo.m_aPSName, aLine ); - aLine.append( "\n" ); - if( !pFont->mbSymbolFlag && pEncoding == 0 ) - aLine.append( "/Encoding/WinAnsiEncoding\n" ); - if( nToUnicodeStream ) - { - aLine.append( "/ToUnicode " ); - aLine.append( nToUnicodeStream ); - aLine.append( " 0 R\n" ); + // write stream length object + aLine.setLength( 0 ); + if( ! updateObject( nStreamLengthObject ) ) + throw FontException(); + aLine.append( nStreamLengthObject ); + aLine.append( " 0 obj\n" ); + aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos ) ); + aLine.append( "\nendobj\n\n" ); + if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) + throw FontException(); } - aLine.append( "/FirstChar 0 /LastChar 255\n" - "/Widths[" ); - for( int i = 0; i < 256; i++ ) + else { - aLine.append( pWidths[i] ); - aLine.append( ((i&15) == 15) ? "\n" : " " ); - } - aLine.append( "]\n" - "/FontDescriptor " ); - aLine.append( nFontDescriptor ); - aLine.append( " 0 R>>\n" - "endobj\n\n" ); - if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) - goto streamend; - - nFontObject = nObject; - - aRet[ rEmbed.m_nNormalFontID ] = nObject; + rtl::OStringBuffer aErrorComment( 256 ); + aErrorComment.append( "GetEmbedFontData failed for font \"" ); + aErrorComment.append( OUStringToOString( pFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ) ); + aErrorComment.append( '\"' ); + if( pFont->GetSlant() == ITALIC_NORMAL ) + aErrorComment.append( " italic" ); + else if( pFont->GetSlant() == ITALIC_OBLIQUE ) + aErrorComment.append( " oblique" ); + aErrorComment.append( " weight=" ); + aErrorComment.append( sal_Int32(pFont->GetWeight()) ); + emitComment( aErrorComment.getStr() ); + } + + if( nStreamObject ) + // write font descriptor + nFontDescriptor = emitFontDescriptor( pFont, aInfo, 0, nStreamObject ); - // write additional encodings - for( std::list< EmbedEncoding >::iterator enc_it = rEmbed.m_aExtendedEncodings.begin(); enc_it != rEmbed.m_aExtendedEncodings.end(); ++enc_it ) + if( nFontDescriptor ) { - sal_Int32 aEncWidths[ 256 ]; - // emit encoding dict - sal_Int32 nEncObject = createObject(); - if( ! updateObject( nEncObject ) ) - goto streamend; - - OutputDevice* pRef = getReferenceDevice(); - pRef->Push( PUSH_FONT | PUSH_MAPMODE ); - pRef->SetMapMode( MapMode( MAP_PIXEL ) ); - Font aFont( pFont->GetFamilyName(), pFont->GetStyleName(), Size( 0, 1000 ) ); - aFont.SetWeight( pFont->GetWeight() ); - aFont.SetItalic( pFont->GetSlant() ); - aFont.SetPitch( pFont->GetPitch() ); - pRef->SetFont( aFont ); - pRef->ImplNewFont(); - - aLine.setLength( 0 ); - aLine.append( nEncObject ); - aLine.append( " 0 obj\n" - "<</Type/Encoding/Differences[ 0\n" ); - int nEncoded = 0; - for( std::vector< EmbedCode >::iterator str_it = enc_it->m_aEncVector.begin(); str_it != enc_it->m_aEncVector.end(); ++str_it ) - { - String aStr( str_it->m_aUnicode ); - aEncWidths[nEncoded] = pRef->GetTextWidth( aStr ); - nEncodedCodes[nEncoded] = str_it->m_aUnicode; - nEncoding[nEncoded] = sal::static_int_cast<sal_uInt8>(nEncoded); - - aLine.append( " /" ); - aLine.append( str_it->m_aName ); - if( !((++nEncoded) & 15) ) - aLine.append( "\n" ); - } - aLine.append( "]>>\n" - "endobj\n\n" ); - - pRef->Pop(); - - if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) - goto streamend; - - nToUnicodeStream = createToUnicodeCMap( nEncoding, nEncodedCodes, nEncoded ); + if( pEncoding ) + nToUnicodeStream = createToUnicodeCMap( nEncoding, &aUnicodes[0], pUnicodesPerGlyph, pEncToUnicodeIndex, sizeof(nEncoding)/sizeof(nEncoding[0]) ); - nObject = createObject(); + // write font object + sal_Int32 nObject = createObject(); if( ! updateObject( nObject ) ) - goto streamend; + throw FontException(); - aLine.setLength( 0 ); + OStringBuffer aLine( 1024 ); aLine.append( nObject ); aLine.append( " 0 obj\n" - "<</Type/Font/Subtype/Type1/BaseFont/" ); + "<</Type/Font/Subtype/Type1/BaseFont/" ); appendName( aInfo.m_aPSName, aLine ); aLine.append( "\n" ); - aLine.append( "/Encoding " ); - aLine.append( nEncObject ); - aLine.append( " 0 R\n" ); + if( !pFont->mbSymbolFlag && pEncoding == 0 ) + aLine.append( "/Encoding/WinAnsiEncoding\n" ); if( nToUnicodeStream ) { aLine.append( "/ToUnicode " ); aLine.append( nToUnicodeStream ); aLine.append( " 0 R\n" ); } - aLine.append( "/FirstChar 0\n" - "/LastChar " ); - aLine.append( (sal_Int32)(nEncoded-1) ); - aLine.append( "\n" - "/Widths[" ); - for( int i = 0; i < nEncoded; i++ ) + aLine.append( "/FirstChar 0 /LastChar 255\n" + "/Widths[" ); + for( int i = 0; i < 256; i++ ) { - aLine.append( aEncWidths[i] ); + aLine.append( pWidths[i] ); aLine.append( ((i&15) == 15) ? "\n" : " " ); } - aLine.append( " ]\n" - "/FontDescriptor " ); + aLine.append( "]\n" + "/FontDescriptor " ); aLine.append( nFontDescriptor ); aLine.append( " 0 R>>\n" - "endobj\n\n" ); + "endobj\n\n" ); if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) - goto streamend; + throw FontException(); - aRet[ enc_it->m_nFontID ] = nObject; + nFontObject = nObject; + + aRet[ rEmbed.m_nNormalFontID ] = nObject; + + // write additional encodings + for( std::list< EmbedEncoding >::iterator enc_it = rEmbed.m_aExtendedEncodings.begin(); enc_it != rEmbed.m_aExtendedEncodings.end(); ++enc_it ) + { + sal_Int32 aEncWidths[ 256 ]; + // emit encoding dict + sal_Int32 nEncObject = createObject(); + if( ! updateObject( nEncObject ) ) + throw FontException(); + + OutputDevice* pRef = getReferenceDevice(); + pRef->Push( PUSH_FONT | PUSH_MAPMODE ); + pRef->SetMapMode( MapMode( MAP_PIXEL ) ); + Font aFont( pFont->GetFamilyName(), pFont->GetStyleName(), Size( 0, 1000 ) ); + aFont.SetWeight( pFont->GetWeight() ); + aFont.SetItalic( pFont->GetSlant() ); + aFont.SetPitch( pFont->GetPitch() ); + pRef->SetFont( aFont ); + pRef->ImplNewFont(); + + aLine.setLength( 0 ); + aLine.append( nEncObject ); + aLine.append( " 0 obj\n" + "<</Type/Encoding/Differences[ 0\n" ); + int nEncoded = 0; + aUnicodes.clear(); + for( std::vector< EmbedCode >::iterator str_it = enc_it->m_aEncVector.begin(); str_it != enc_it->m_aEncVector.end(); ++str_it ) + { + String aStr( str_it->m_aUnicode ); + aEncWidths[nEncoded] = pRef->GetTextWidth( aStr ); + nEncodedCodes[nEncoded] = str_it->m_aUnicode; + nEncoding[nEncoded] = sal::static_int_cast<sal_uInt8>(nEncoded); + pEncToUnicodeIndex[nEncoded] = static_cast<sal_Int32>(aUnicodes.size()); + aUnicodes.push_back( nEncodedCodes[nEncoded] ); + pUnicodesPerGlyph[nEncoded] = 1; + + aLine.append( " /" ); + aLine.append( str_it->m_aName ); + if( !((++nEncoded) & 15) ) + aLine.append( "\n" ); + } + aLine.append( "]>>\n" + "endobj\n\n" ); + + pRef->Pop(); + + if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) + throw FontException(); + + nToUnicodeStream = createToUnicodeCMap( nEncoding, &aUnicodes[0], pUnicodesPerGlyph, pEncToUnicodeIndex, nEncoded ); + + nObject = createObject(); + if( ! updateObject( nObject ) ) + throw FontException(); + + aLine.setLength( 0 ); + aLine.append( nObject ); + aLine.append( " 0 obj\n" + "<</Type/Font/Subtype/Type1/BaseFont/" ); + appendName( aInfo.m_aPSName, aLine ); + aLine.append( "\n" ); + aLine.append( "/Encoding " ); + aLine.append( nEncObject ); + aLine.append( " 0 R\n" ); + if( nToUnicodeStream ) + { + aLine.append( "/ToUnicode " ); + aLine.append( nToUnicodeStream ); + aLine.append( " 0 R\n" ); + } + aLine.append( "/FirstChar 0\n" + "/LastChar " ); + aLine.append( (sal_Int32)(nEncoded-1) ); + aLine.append( "\n" + "/Widths[" ); + for( int i = 0; i < nEncoded; i++ ) + { + aLine.append( aEncWidths[i] ); + aLine.append( ((i&15) == 15) ? "\n" : " " ); + } + aLine.append( " ]\n" + "/FontDescriptor " ); + aLine.append( nFontDescriptor ); + aLine.append( " 0 R>>\n" + "endobj\n\n" ); + if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) + throw FontException(); + + aRet[ enc_it->m_nFontID ] = nObject; + } } } + catch( FontException& ) + { + // these do nothing in case there was no compression or encryption ongoing + endCompression(); + disableStreamEncryption(); + } - streamend: if( pFontData ) m_pReferenceDevice->mpGraphics->FreeEmbedFontData( pFontData, nFontLen ); @@ -3567,11 +3552,15 @@ static void appendSubsetName( int nSubsetID, const OUString& rPSName, OStringBuf appendName( rPSName, rBuffer ); } -sal_Int32 PDFWriterImpl::createToUnicodeCMap( sal_uInt8* pEncoding, sal_Ucs* pUnicodes, int nGlyphs ) +sal_Int32 PDFWriterImpl::createToUnicodeCMap( sal_uInt8* pEncoding, + sal_Ucs* pUnicodes, + sal_Int32* pUnicodesPerGlyph, + sal_Int32* pEncToUnicodeIndex, + int nGlyphs ) { int nMapped = 0, n = 0; for( n = 0; n < nGlyphs; n++ ) - if( pUnicodes[n] ) + if( pUnicodes[pEncToUnicodeIndex[n]] && pUnicodesPerGlyph[n] ) nMapped++; if( nMapped == 0 ) @@ -3599,7 +3588,7 @@ sal_Int32 PDFWriterImpl::createToUnicodeCMap( sal_uInt8* pEncoding, sal_Ucs* pUn int nCount = 0; for( n = 0; n < nGlyphs; n++ ) { - if( pUnicodes[n] ) + if( pUnicodes[pEncToUnicodeIndex[n]] && pUnicodesPerGlyph[n] ) { if( (nCount % 100) == 0 ) { @@ -3611,9 +3600,13 @@ sal_Int32 PDFWriterImpl::createToUnicodeCMap( sal_uInt8* pEncoding, sal_Ucs* pUn aContents.append( '<' ); appendHex( (sal_Int8)pEncoding[n], aContents ); aContents.append( "> <" ); - // TODO: handle unicodes>U+FFFF - appendHex( (sal_Int8)(pUnicodes[n] / 256), aContents ); - appendHex( (sal_Int8)(pUnicodes[n] & 255), aContents ); + // TODO: handle unicodes>U+FFFF + sal_Int32 nIndex = pEncToUnicodeIndex[n]; + for( sal_Int32 j = 0; j < pUnicodesPerGlyph[n]; j++ ) + { + appendHex( (sal_Int8)(pUnicodes[nIndex + j] / 256), aContents ); + appendHex( (sal_Int8)(pUnicodes[nIndex + j] & 255), aContents ); + } aContents.append( ">\n" ); nCount++; } @@ -3777,25 +3770,32 @@ bool PDFWriterImpl::emitFonts() sal_Int32 pGlyphIDs[ 256 ]; sal_Int32 pWidths[ 256 ]; sal_uInt8 pEncoding[ 256 ]; - sal_Ucs pUnicodes[ 256 ]; + sal_Int32 pEncToUnicodeIndex[ 256 ]; + sal_Int32 pUnicodesPerGlyph[ 256 ]; + std::vector<sal_Ucs> aUnicodes; + aUnicodes.reserve( 256 ); int nGlyphs = 1; // fill arrays and prepare encoding index map sal_Int32 nToUnicodeStream = 0; - memset( pGlyphIDs, 0, sizeof( pGlyphIDs ) ); - memset( pEncoding, 0, sizeof( pEncoding ) ); - memset( pUnicodes, 0, sizeof( pUnicodes ) ); + rtl_zeroMemory( pGlyphIDs, sizeof( pGlyphIDs ) ); + rtl_zeroMemory( pEncoding, sizeof( pEncoding ) ); + rtl_zeroMemory( pUnicodesPerGlyph, sizeof( pUnicodesPerGlyph ) ); + rtl_zeroMemory( pEncToUnicodeIndex, sizeof( pEncToUnicodeIndex ) ); for( FontEmitMapping::iterator fit = lit->m_aMapping.begin(); fit != lit->m_aMapping.end();++fit ) { - sal_uInt8 nEnc = fit->second.m_nSubsetGlyphID; + sal_uInt8 nEnc = fit->second.getGlyphId(); DBG_ASSERT( pGlyphIDs[nEnc] == 0 && pEncoding[nEnc] == 0, "duplicate glyph" ); DBG_ASSERT( nEnc <= lit->m_aMapping.size(), "invalid glyph encoding" ); pGlyphIDs[ nEnc ] = fit->first; pEncoding[ nEnc ] = nEnc; - pUnicodes[ nEnc ] = fit->second.m_aUnicode; - if( pUnicodes[ nEnc ] ) + pEncToUnicodeIndex[ nEnc ] = static_cast<sal_Int32>(aUnicodes.size()); + pUnicodesPerGlyph[ nEnc ] = fit->second.countCodes(); + for( sal_Int32 n = 0; n < pUnicodesPerGlyph[ nEnc ]; n++ ) + aUnicodes.push_back( fit->second.getCode( n ) ); + if( fit->second.getCode(0) ) nToUnicodeStream = 1; if( nGlyphs < 256 ) nGlyphs++; @@ -3816,92 +3816,92 @@ bool PDFWriterImpl::emitFonts() CHECK_RETURN( (osl_File_E_None == osl_getFilePos( aFontFile, &nLength1 ) ) ); CHECK_RETURN( (osl_File_E_None == osl_setFilePos( aFontFile, osl_Pos_Absolut, 0 ) ) ); -#if OSL_DEBUG_LEVEL > 1 + #if OSL_DEBUG_LEVEL > 1 { OStringBuffer aLine1( " PDFWriterImpl::emitFonts" ); emitComment( aLine1.getStr() ); } -#endif + #endif sal_Int32 nFontStream = createObject(); sal_Int32 nStreamLengthObject = createObject(); CHECK_RETURN( updateObject( nFontStream ) ); aLine.setLength( 0 ); aLine.append( nFontStream ); aLine.append( " 0 obj\n" - "<</Length " ); + "<</Length " ); aLine.append( (sal_Int32)nStreamLengthObject ); aLine.append( " 0 R" -#ifndef DEBUG_DISABLE_PDFCOMPRESSION - "/Filter/FlateDecode" -#endif - "/Length1 " ); - - sal_uInt64 nStartPos = 0; - if( aSubsetInfo.m_nFontType == FontSubsetInfo::SFNT_TTF ) - { - aLine.append( (sal_Int32)nLength1 ); + #ifndef DEBUG_DISABLE_PDFCOMPRESSION + "/Filter/FlateDecode" + #endif + "/Length1 " ); - aLine.append( ">>\n" - "stream\n" ); - CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); - CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartPos ) ) ); - - // copy font file - beginCompression(); - checkAndEnableStreamEncryption( nFontStream ); - sal_Bool bEOF = sal_False; - do + sal_uInt64 nStartPos = 0; + if( aSubsetInfo.m_nFontType == FontSubsetInfo::SFNT_TTF ) { - char buf[8192]; - sal_uInt64 nRead; - CHECK_RETURN( (osl_File_E_None == osl_readFile( aFontFile, buf, sizeof( buf ), &nRead ) ) ); - CHECK_RETURN( writeBuffer( buf, nRead ) ); - CHECK_RETURN( (osl_File_E_None == osl_isEndOfFile( aFontFile, &bEOF ) ) ); - } while( ! bEOF ); - } - else if( (aSubsetInfo.m_nFontType & FontSubsetInfo::CFF_FONT) != 0 ) - { - // TODO: implement - DBG_ERROR( "PDFWriterImpl does not support CFF-font subsets yet!" ); - } - else if( (aSubsetInfo.m_nFontType & FontSubsetInfo::TYPE1_PFB) != 0 ) // TODO: also support PFA? - { - unsigned char* pBuffer = new unsigned char[ (int)nLength1 ]; - - sal_uInt64 nBytesRead = 0; - CHECK_RETURN( (osl_File_E_None == osl_readFile( aFontFile, pBuffer, nLength1, &nBytesRead ) ) ); - DBG_ASSERT( nBytesRead==nLength1, "PDF-FontSubset read incomplete!" ); - CHECK_RETURN( (osl_File_E_None == osl_setFilePos( aFontFile, osl_Pos_Absolut, 0 ) ) ); - // get the PFB-segment lengths - ThreeInts aSegmentLengths = {0,0,0}; - getPfbSegmentLengths( pBuffer, (int)nBytesRead, aSegmentLengths ); - // the lengths below are mandatory for PDF-exported Type1 fonts - // because the PFB segment headers get stripped! WhyOhWhy. - aLine.append( (sal_Int32)aSegmentLengths[0] ); - aLine.append( "/Length2 " ); - aLine.append( (sal_Int32)aSegmentLengths[1] ); - aLine.append( "/Length3 " ); - aLine.append( (sal_Int32)aSegmentLengths[2] ); - - aLine.append( ">>\n" - "stream\n" ); - CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); - CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartPos ) ) ); - - // emit PFB-sections without section headers - beginCompression(); - checkAndEnableStreamEncryption( nFontStream ); - CHECK_RETURN( writeBuffer( pBuffer+ 6, aSegmentLengths[0] ) ); - CHECK_RETURN( writeBuffer( pBuffer+12 + aSegmentLengths[0], aSegmentLengths[1] ) ); - CHECK_RETURN( writeBuffer( pBuffer+18 + aSegmentLengths[0] + aSegmentLengths[1], aSegmentLengths[2] ) ); - - delete[] pBuffer; - } - else - { - fprintf( stderr, "PDF: CreateFontSubset result in not yet supported format=%d\n",aSubsetInfo.m_nFontType); - aLine.append( "0 >>\nstream\n" ); - } + aLine.append( (sal_Int32)nLength1 ); + + aLine.append( ">>\n" + "stream\n" ); + CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); + CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartPos ) ) ); + + // copy font file + beginCompression(); + checkAndEnableStreamEncryption( nFontStream ); + sal_Bool bEOF = sal_False; + do + { + char buf[8192]; + sal_uInt64 nRead; + CHECK_RETURN( (osl_File_E_None == osl_readFile( aFontFile, buf, sizeof( buf ), &nRead ) ) ); + CHECK_RETURN( writeBuffer( buf, nRead ) ); + CHECK_RETURN( (osl_File_E_None == osl_isEndOfFile( aFontFile, &bEOF ) ) ); + } while( ! bEOF ); + } + else if( (aSubsetInfo.m_nFontType & FontSubsetInfo::CFF_FONT) != 0 ) + { + // TODO: implement + DBG_ERROR( "PDFWriterImpl does not support CFF-font subsets yet!" ); + } + else if( (aSubsetInfo.m_nFontType & FontSubsetInfo::TYPE1_PFB) != 0 ) // TODO: also support PFA? + { + unsigned char* pBuffer = new unsigned char[ (int)nLength1 ]; + + sal_uInt64 nBytesRead = 0; + CHECK_RETURN( (osl_File_E_None == osl_readFile( aFontFile, pBuffer, nLength1, &nBytesRead ) ) ); + DBG_ASSERT( nBytesRead==nLength1, "PDF-FontSubset read incomplete!" ); + CHECK_RETURN( (osl_File_E_None == osl_setFilePos( aFontFile, osl_Pos_Absolut, 0 ) ) ); + // get the PFB-segment lengths + ThreeInts aSegmentLengths = {0,0,0}; + getPfbSegmentLengths( pBuffer, (int)nBytesRead, aSegmentLengths ); + // the lengths below are mandatory for PDF-exported Type1 fonts + // because the PFB segment headers get stripped! WhyOhWhy. + aLine.append( (sal_Int32)aSegmentLengths[0] ); + aLine.append( "/Length2 " ); + aLine.append( (sal_Int32)aSegmentLengths[1] ); + aLine.append( "/Length3 " ); + aLine.append( (sal_Int32)aSegmentLengths[2] ); + + aLine.append( ">>\n" + "stream\n" ); + CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); + CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartPos ) ) ); + + // emit PFB-sections without section headers + beginCompression(); + checkAndEnableStreamEncryption( nFontStream ); + CHECK_RETURN( writeBuffer( pBuffer+ 6, aSegmentLengths[0] ) ); + CHECK_RETURN( writeBuffer( pBuffer+12 + aSegmentLengths[0], aSegmentLengths[1] ) ); + CHECK_RETURN( writeBuffer( pBuffer+18 + aSegmentLengths[0] + aSegmentLengths[1], aSegmentLengths[2] ) ); + + delete[] pBuffer; + } + else + { + fprintf( stderr, "PDF: CreateFontSubset result in not yet supported format=%d\n",aSubsetInfo.m_nFontType); + aLine.append( "0 >>\nstream\n" ); + } endCompression(); disableStreamEncryption(); @@ -3928,7 +3928,7 @@ bool PDFWriterImpl::emitFonts() sal_Int32 nFontDescriptor = emitFontDescriptor( it->first, aSubsetInfo, lit->m_nFontID, nFontStream ); if( nToUnicodeStream ) - nToUnicodeStream = createToUnicodeCMap( pEncoding, pUnicodes, nGlyphs ); + nToUnicodeStream = createToUnicodeCMap( pEncoding, &aUnicodes[0], pUnicodesPerGlyph, pEncToUnicodeIndex, nGlyphs ); sal_Int32 nFontObject = createObject(); CHECK_RETURN( updateObject( nFontObject ) ); @@ -3937,22 +3937,22 @@ bool PDFWriterImpl::emitFonts() aLine.append( " 0 obj\n" ); aLine.append( ((aSubsetInfo.m_nFontType & FontSubsetInfo::ANY_TYPE1) != 0) ? - "<</Type/Font/Subtype/Type1/BaseFont/" : - "<</Type/Font/Subtype/TrueType/BaseFont/" ); + "<</Type/Font/Subtype/Type1/BaseFont/" : + "<</Type/Font/Subtype/TrueType/BaseFont/" ); appendSubsetName( lit->m_nFontID, aSubsetInfo.m_aPSName, aLine ); aLine.append( "\n" - "/FirstChar 0\n" - "/LastChar " ); + "/FirstChar 0\n" + "/LastChar " ); aLine.append( (sal_Int32)(nGlyphs-1) ); aLine.append( "\n" - "/Widths[" ); + "/Widths[" ); for( int i = 0; i < nGlyphs; i++ ) { aLine.append( pWidths[ i ] ); aLine.append( ((i & 15) == 15) ? "\n" : " " ); } aLine.append( "]\n" - "/FontDescriptor " ); + "/FontDescriptor " ); aLine.append( nFontDescriptor ); aLine.append( " 0 R\n" ); if( nToUnicodeStream ) @@ -3962,7 +3962,7 @@ bool PDFWriterImpl::emitFonts() aLine.append( " 0 R\n" ); } aLine.append( ">>\n" - "endobj\n\n" ); + "endobj\n\n" ); CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); aFontIDToObject[ lit->m_nFontID ] = nFontObject; @@ -4011,7 +4011,7 @@ bool PDFWriterImpl::emitFonts() OStringBuffer aFontDict( 1024 ); aFontDict.append( getFontDictObject() ); aFontDict.append( " 0 obj\n" - "<<" ); + "<<" ); int ni = 0; for( std::map< sal_Int32, sal_Int32 >::iterator mit = aFontIDToObject.begin(); mit != aFontIDToObject.end(); ++mit ) { @@ -4020,12 +4020,12 @@ bool PDFWriterImpl::emitFonts() aFontDict.append( ' ' ); aFontDict.append( mit->second ); aFontDict.append( " 0 R" ); - if( ((++ni) & 7) == 0 ) - aFontDict.append( '\n' ); + if( ((++ni) & 7) == 0 ) + aFontDict.append( '\n' ); } // emit builtin font for widget apperances / variable text for( std::map< sal_Int32, sal_Int32 >::iterator it = m_aBuiltinFontToObjectMap.begin(); - it != m_aBuiltinFontToObjectMap.end(); ++it ) + it != m_aBuiltinFontToObjectMap.end(); ++it ) { ImplPdfBuiltinFontData aData(m_aBuiltinFonts[it->first]); it->second = emitBuiltinFont( &aData, it->second ); @@ -6580,12 +6580,14 @@ void PDFWriterImpl::registerGlyphs( int nGlyphs, sal_GlyphId* pGlyphs, sal_Int32* pGlyphWidths, sal_Ucs* pUnicodes, + sal_Int32* pUnicodesPerGlyph, sal_uInt8* pMappedGlyphs, sal_Int32* pMappedFontObjects, const ImplFontData* pFallbackFonts[] ) { const ImplFontData* pDevFont = m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData; - for( int i = 0; i < nGlyphs; i++ ) + sal_Ucs* pCurUnicode = pUnicodes; + for( int i = 0; i < nGlyphs; pCurUnicode += pUnicodesPerGlyph[i] , i++ ) { const int nFontGlyphId = pGlyphs[i] & (GF_IDXMASK | GF_ISCHAR | GF_GSUB); const ImplFontData* pCurrentFont = pFallbackFonts[i] ? pFallbackFonts[i] : pDevFont; @@ -6640,8 +6642,9 @@ void PDFWriterImpl::registerGlyphs( int nGlyphs, // add new glyph to emitted font subset GlyphEmit& rNewGlyphEmit = rSubset.m_aSubsets.back().m_aMapping[ nFontGlyphId ]; - rNewGlyphEmit.m_nSubsetGlyphID = nNewId; - rNewGlyphEmit.m_aUnicode = (pUnicodes ? pUnicodes[i] : 0); + rNewGlyphEmit.setGlyphId( nNewId ); + for( sal_Int32 n = 0; n < pUnicodesPerGlyph[i]; n++ ) + rNewGlyphEmit.addCode( pCurUnicode[n] ); // add new glyph to font mapping Glyph& rNewGlyph = rSubset.m_aMapping[ nFontGlyphId ]; @@ -6678,7 +6681,7 @@ void PDFWriterImpl::registerGlyphs( int nGlyphs, Ucs2OStrMap::const_iterator nonenc_it; sal_Int32 nCurFontID = nFontID; - sal_Ucs cChar = pUnicodes[i]; + sal_Ucs cChar = *pCurUnicode; if( pEncoding ) { enc_it = pEncoding->find( cChar ); @@ -6740,7 +6743,7 @@ void PDFWriterImpl::registerGlyphs( int nGlyphs, pMappedGlyphs[ i ] = (sal_Int8)cChar; pMappedFontObjects[ i ] = nCurFontID; pGlyphWidths[ i ] = m_aFontCache.getGlyphWidth( pCurrentFont, - (pEncoding ? pUnicodes[i] : cChar) | GF_ISCHAR, + (pEncoding ? *pCurUnicode : cChar) | GF_ISCHAR, false, m_pReferenceDevice->mpGraphics ); } @@ -7020,7 +7023,9 @@ void PDFWriterImpl::drawLayout( SalLayout& rLayout, const String& rText, bool bT sal_Int32 pGlyphWidths[nMaxGlyphs]; sal_uInt8 pMappedGlyphs[nMaxGlyphs]; sal_Int32 pMappedFontObjects[nMaxGlyphs]; - sal_Ucs pUnicodes[nMaxGlyphs]; + std::vector<sal_Ucs> aUnicodes; + aUnicodes.reserve( nMaxGlyphs ); + sal_Int32 pUnicodesPerGlyph[nMaxGlyphs]; int pCharPosAry[nMaxGlyphs]; sal_Int32 nAdvanceWidths[nMaxGlyphs]; const ImplFontData* pFallbackFonts[nMaxGlyphs]; @@ -7153,15 +7158,29 @@ void PDFWriterImpl::drawLayout( SalLayout& rLayout, const String& rText, bool bT Point aGNGlyphPos; while( (nGlyphs = rLayout.GetNextGlyphs( nTmpMaxGlyphs, pGlyphs, aGNGlyphPos, nIndex, nAdvanceWidths, pCharPosAry )) != 0 ) { + aUnicodes.clear(); for( int i = 0; i < nGlyphs; i++ ) { pFallbackFonts[i] = rLayout.GetFallbackFontData( pGlyphs[i] ); + // default case: 1 glyph is one unicode + pUnicodesPerGlyph[i] = 1; if( (pGlyphs[i] & GF_ISCHAR) ) - pUnicodes[i] = static_cast<sal_Ucs>(pGlyphs[i] & GF_IDXMASK); + { + aUnicodes.push_back( static_cast<sal_Ucs>(pGlyphs[i] & GF_IDXMASK) ); + } else if( pCharPosAry[i] >= nMinCharPos && pCharPosAry[i] <= nMaxCharPos ) { - pUnicodes[i] = rText.GetChar( sal::static_int_cast<xub_StrLen>(pCharPosAry[i]) ); + int nChars = 1; + aUnicodes.push_back( rText.GetChar( sal::static_int_cast<xub_StrLen>(pCharPosAry[i]) ) ); + pUnicodesPerGlyph[i] = 1; + // try to handle ligatures and such + if( i < nGlyphs-1 ) + { + pUnicodesPerGlyph[i] = nChars = pCharPosAry[i+1] - pCharPosAry[i]; + for( int n = 1; n < nChars; n++ ) + aUnicodes.push_back( rText.GetChar( sal::static_int_cast<xub_StrLen>(pCharPosAry[i]+n) ) ); + } // #i36691# hack that is needed because currently the pGlyphs[] // argument is ignored for embeddable fonts and so the layout // engine's glyph work is ignored (i.e. char mirroring) @@ -7169,17 +7188,21 @@ void PDFWriterImpl::drawLayout( SalLayout& rLayout, const String& rText, bool bT // glyphid (i.e. FreeType's synthetic glyphid for a Type1 font) // back to unicode and then to embeddable font's encoding if( getReferenceDevice()->GetLayoutMode() & TEXT_LAYOUT_BIDI_RTL ) - pUnicodes[i] = static_cast<sal_Ucs>(GetMirroredChar(pUnicodes[i])); + { + size_t nI = aUnicodes.size()-1; + for( int n = 0; n < nChars; n++, nI-- ) + aUnicodes[nI] = static_cast<sal_Ucs>(GetMirroredChar(aUnicodes[nI])); + } } else - pUnicodes[i] = 0; + aUnicodes.push_back( 0 ); // note: in case of ctl one character may result // in multiple glyphs. The current SalLayout // implementations set -1 then to indicate that no direct // mapping is possible } - registerGlyphs( nGlyphs, pGlyphs, pGlyphWidths, pUnicodes, pMappedGlyphs, pMappedFontObjects, pFallbackFonts ); + registerGlyphs( nGlyphs, pGlyphs, pGlyphWidths, &aUnicodes[0], pUnicodesPerGlyph, pMappedGlyphs, pMappedFontObjects, pFallbackFonts ); for( int i = 0; i < nGlyphs; i++ ) { @@ -9513,7 +9536,7 @@ bool PDFWriterImpl::writeBitmapObject( BitmapEmit& rObject, bool bMask ) sal_uInt64 nStartPos = 0; CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartPos )) ); - checkAndEnableStreamEncryption( rObject.m_nObject ); + checkAndEnableStreamEncryption( rObject.m_nObject ); beginCompression(); if( ! bTrueColor || pAccess->GetScanlineFormat() == BMP_FORMAT_24BIT_TC_RGB ) { @@ -9527,7 +9550,7 @@ bool PDFWriterImpl::writeBitmapObject( BitmapEmit& rObject, bool bMask ) else { const int nScanLineBytes = pAccess->Width()*3; - sal_uInt8 *pCol = (sal_uInt8*)rtl_allocateMemory( nScanLineBytes ); + boost::shared_array<sal_uInt8> pCol( new sal_uInt8[ nScanLineBytes ] ); for( int y = 0; y < pAccess->Height(); y++ ) { for( int x = 0; x < pAccess->Width(); x++ ) @@ -9537,9 +9560,8 @@ bool PDFWriterImpl::writeBitmapObject( BitmapEmit& rObject, bool bMask ) pCol[3*x+1] = aColor.GetGreen(); pCol[3*x+2] = aColor.GetBlue(); } - CHECK_RETURN( writeBuffer( pCol, nScanLineBytes ) ); + CHECK_RETURN( writeBuffer( pCol.get(), nScanLineBytes ) ); } - rtl_freeMemory( pCol ); } endCompression(); disableStreamEncryption(); diff --git a/vcl/source/gdi/pdfwriter_impl.hxx b/vcl/source/gdi/pdfwriter_impl.hxx index e058cfa487db..d54aecf35788 100644 --- a/vcl/source/gdi/pdfwriter_impl.hxx +++ b/vcl/source/gdi/pdfwriter_impl.hxx @@ -52,6 +52,8 @@ #include <hash_map> #include <list> +#include <boost/shared_array.hpp> + class ImplFontSelectData; class ImplFontMetricData; class FontSubsetInfo; @@ -270,10 +272,53 @@ public: }; // font subsets - struct GlyphEmit + class GlyphEmit { - sal_Ucs m_aUnicode; - sal_uInt8 m_nSubsetGlyphID; + // performance: actually this should probably a vector; + sal_Ucs m_aBufferedUnicodes[3]; + sal_Int32 m_nUnicodes; + sal_Int32 m_nMaxUnicodes; + boost::shared_array<sal_Ucs> m_pUnicodes; + sal_uInt8 m_nSubsetGlyphID; + + public: + GlyphEmit() : m_nUnicodes(0), m_nSubsetGlyphID(0) + { + rtl_zeroMemory( m_aBufferedUnicodes, sizeof( m_aBufferedUnicodes ) ); + m_nMaxUnicodes = sizeof(m_aBufferedUnicodes)/sizeof(m_aBufferedUnicodes[0]); + } + ~GlyphEmit() + { + } + + void setGlyphId( sal_uInt8 i_nId ) { m_nSubsetGlyphID = i_nId; } + sal_uInt8 getGlyphId() const { return m_nSubsetGlyphID; } + + void addCode( sal_Ucs i_cCode ) + { + if( m_nUnicodes == m_nMaxUnicodes ) + { + sal_Ucs* pNew = new sal_Ucs[ 2 * m_nMaxUnicodes]; + if( m_pUnicodes.get() ) + rtl_copyMemory( pNew, m_pUnicodes.get(), m_nMaxUnicodes * sizeof(sal_Ucs) ); + else + rtl_copyMemory( pNew, m_aBufferedUnicodes, m_nMaxUnicodes * sizeof(sal_Ucs) ); + m_pUnicodes.reset( pNew ); + m_nMaxUnicodes *= 2; + } + if( m_pUnicodes.get() ) + m_pUnicodes[ m_nUnicodes++ ] = i_cCode; + else + m_aBufferedUnicodes[ m_nUnicodes++ ] = i_cCode; + } + sal_Int32 countCodes() const { return m_nUnicodes; } + sal_Ucs getCode( sal_Int32 i_nIndex ) const + { + sal_Ucs nRet = 0; + if( i_nIndex < m_nUnicodes ) + nRet = m_pUnicodes.get() ? m_pUnicodes[ i_nIndex ] : m_aBufferedUnicodes[ i_nIndex ]; + return nRet; + } }; typedef std::map< sal_GlyphId, GlyphEmit > FontEmitMapping; struct FontEmit @@ -860,7 +905,7 @@ i12626 void appendLiteralStringEncrypt( rtl::OStringBuffer& rInString, const sal_Int32 nInObjectNumber, rtl::OStringBuffer& rOutBuffer ); /* creates fonts and subsets that will be emitted later */ - void registerGlyphs( int nGlyphs, sal_GlyphId* pGlyphs, sal_Int32* pGlpyhWidths, sal_Ucs* pUnicodes, sal_uInt8* pMappedGlyphs, sal_Int32* pMappedFontObjects, const ImplFontData* pFallbackFonts[] ); + void registerGlyphs( int nGlyphs, sal_GlyphId* pGlyphs, sal_Int32* pGlpyhWidths, sal_Ucs* pUnicodes, sal_Int32* pUnicodesPerGlyph, sal_uInt8* pMappedGlyphs, sal_Int32* pMappedFontObjects, const ImplFontData* pFallbackFonts[] ); /* emits a text object according to the passed layout */ /* TODO: remove rText as soon as SalLayout will change so that rText is not necessary anymore */ @@ -909,7 +954,7 @@ i12626 /* writes a font descriptor and returns its object id (or 0) */ sal_Int32 emitFontDescriptor( const ImplFontData*, FontSubsetInfo&, sal_Int32 nSubsetID, sal_Int32 nStream ); /* writes a ToUnicode cmap, returns the corresponding stream object */ - sal_Int32 createToUnicodeCMap( sal_uInt8* pEncoding, sal_Ucs* pUnicodes, int nGlyphs ); + sal_Int32 createToUnicodeCMap( sal_uInt8* pEncoding, sal_Ucs* pUnicodes, sal_Int32* pUnicodesPerGlyph, sal_Int32* pEncToUnicodeIndex, int nGlyphs ); /* get resource dict object number */ sal_Int32 getResourceDictObj() |