/* * (c) Copyright Ascensio System SIA 2010-2019 * * This program is a free software product. You can redistribute it and/or * modify it under the terms of the GNU Affero General Public License (AGPL) * version 3 as published by the Free Software Foundation. In accordance with * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect * that Ascensio System SIA expressly excludes the warranty of non-infringement * of any third-party rights. * * This program is distributed WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html * * You can contact Ascensio System SIA at 20A-12 Ernesta Birznieka-Upisha * street, Riga, Latvia, EU, LV-1050. * * The interactive user interfaces in modified source and object code versions * of the Program must display Appropriate Legal Notices, as required under * Section 5 of the GNU AGPL version 3. * * Pursuant to Section 7(b) of the License you must retain the original Product * logo when distributing the program. Pursuant to Section 7(e) we decline to * grant you any rights under trademark law for use of our trademarks. * * All the Product's GUI elements, including illustrations and icon sets, as * well as technical writing content are licensed under the terms of the * Creative Commons Attribution-ShareAlike 4.0 International. See the License * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode * */ #include "DjVuFileImplementation.h" #include "../DesktopEditor/common/File.h" #include "../DesktopEditor/common/Directory.h" #include "../PdfWriter/PdfRenderer.h" #include "../DesktopEditor/graphics/pro/Fonts.h" #include "../DesktopEditor/graphics/pro/Graphics.h" #include "../DesktopEditor/graphics/pro/Image.h" #include "../DesktopEditor/common/StringExt.h" #define VER_DPI 96 #define HOR_DPI 96 #include #ifdef BUILDING_WASM_MODULE #include "../DesktopEditor/graphics/pro/js/wasm/src/serialize.h" #endif namespace NSDjvu { static GUTF8String MakeUTF8String(const std::wstring& wsText) { std::string sText = NSFile::CUtf8Converter::GetUtf8StringFromUnicode(wsText); GUTF8String utf8String(sText.c_str()); return utf8String; } static std::string MakeCString(GUTF8String& strText) { std::string sString(strText.getbuf()); return sString; } static int GetInteger(const std::wstring& wsString) { if (wsString.size() < 1) return 0; try { return std::stoi(wsString); } catch (...) { } try { return static_cast(std::stol(wsString)); } catch (...) { return 0; } } } CDjVuFileImplementation::CDjVuFileImplementation(NSFonts::IApplicationFonts* pFonts) { m_pDoc = NULL; #ifndef BUILDING_WASM_MODULE std::wstring wsTempPath = NSFile::CFileBinary::GetTempPath(); wsTempPath += L"DJVU\\"; m_wsTempDirectory = wsTempPath; NSDirectory::CreateDirectory(m_wsTempDirectory); #endif m_pApplicationFonts = pFonts; } CDjVuFileImplementation::~CDjVuFileImplementation() { #ifndef BUILDING_WASM_MODULE NSDirectory::DeleteDirectory(m_wsTempDirectory); #endif } bool CDjVuFileImplementation::LoadFromFile(const std::wstring& wsSrcFileName, const std::wstring& wsXMLOptions) { m_pDoc = NULL; try { GUTF8String utf8; GURL url = GURL::Filename::UTF8(NSDjvu::MakeUTF8String(wsSrcFileName)); m_pDoc = DjVuDocument::create(url); m_pDoc->wait_get_pages_num(); } catch (...) { return false; } return true; } bool CDjVuFileImplementation::LoadFromMemory(BYTE* data, DWORD length, const std::wstring& wsXmlOptions) { m_pDoc = NULL; try { GP stream = ByteStream::create(data, (size_t)length); m_pDoc = DjVuDocument::create(stream); m_pDoc->wait_get_pages_num(); } catch (...) { return false; } return true; } void CDjVuFileImplementation::Close() { } std::wstring CDjVuFileImplementation::GetTempDirectory() const { return m_wsTempDirectory; } void CDjVuFileImplementation::SetTempDirectory(const std::wstring& wsDirectory) { NSDirectory::DeleteDirectory(m_wsTempDirectory); m_wsTempDirectory = wsDirectory; m_wsTempDirectory += L"\\DJVU\\"; NSDirectory::CreateDirectory(m_wsTempDirectory); } int CDjVuFileImplementation::GetPagesCount() const { if (!m_pDoc) return 0; return m_pDoc->get_pages_num(); } void CDjVuFileImplementation::GetPageInfo(int nPageIndex, double* pdWidth, double* pdHeight, double* pdDpiX, double* pdDpiY) const { if (!m_pDoc) { *pdWidth = 0; *pdHeight = 0; *pdDpiX = 96; *pdDpiY = 96; } #if 0 GP pPage = m_pDoc->get_page(nPageIndex); pPage->wait_for_complete_decode(); pPage->set_rotate(0); *pdWidth = pPage->get_real_width(); *pdHeight = pPage->get_real_height(); *pdDpiX = pPage->get_dpi(); *pdDpiY = pPage->get_dpi(); #endif int nW = 0; int nH = 0; int nDpi = 0; m_pDoc->ReadPageInfo(nPageIndex, nW, nH, nDpi); *pdWidth = nW; *pdHeight = nH; *pdDpiX = nDpi; *pdDpiY = nDpi; } void CDjVuFileImplementation::DrawPageOnRenderer(IRenderer* pRenderer, int nPageIndex, bool* pBreak) { if (!m_pDoc) return; try { GP pPage = m_pDoc->get_page(nPageIndex); pPage->wait_for_complete_decode(); pPage->set_rotate(0); long lRendererType = c_nUnknownRenderer; pRenderer->get_Type(&lRendererType); if (false)//c_nGrRenderer == lRendererType) { CreateGrFrame(pRenderer, pPage, pBreak); } #ifndef BUILDING_WASM_MODULE else if (c_nPDFWriter == lRendererType) { XmlUtils::CXmlNode oText = ParseText(pPage); CreatePdfFrame(pRenderer, pPage, nPageIndex, oText); } #endif else { XmlUtils::CXmlNode oText = ParseText(pPage); CreateFrame(pRenderer, pPage, nPageIndex, oText); } } catch (...) { // белая страница } } BYTE* CDjVuFileImplementation::ConvertToPixels(int nPageIndex, const int& nRasterW, const int& nRasterH, bool bIsFlip) { if (!m_pApplicationFonts) return NULL; NSFonts::IFontManager *pFontManager = m_pApplicationFonts->GenerateFontManager(); NSFonts::IFontsCache* pFontCache = NSFonts::NSFontCache::Create(); pFontCache->SetStreams(m_pApplicationFonts->GetStreams()); pFontManager->SetOwnerCache(pFontCache); NSGraphics::IGraphicsRenderer* pRenderer = NSGraphics::Create(); pRenderer->SetFontManager(pFontManager); double dPageDpiX; double dWidth, dHeight; GetPageInfo(nPageIndex, &dWidth, &dHeight, &dPageDpiX, &dPageDpiX); int nWidth = (nRasterW > 0) ? nRasterW : ((int)dWidth * 96 / dPageDpiX); int nHeight = (nRasterH > 0) ? nRasterH : ((int)dHeight * 96 / dPageDpiX); BYTE* pBgraData = new BYTE[nWidth * nHeight * 4]; if (!pBgraData) return NULL; memset(pBgraData, 0xff, nWidth * nHeight * 4); CBgraFrame oFrame; oFrame.put_Data(pBgraData); oFrame.put_Width(nWidth); oFrame.put_Height(nHeight); oFrame.put_Stride((bIsFlip ? 4 : -4) * nWidth); pRenderer->CreateFromBgraFrame(&oFrame); pRenderer->SetSwapRGB(true); pRenderer->put_Width(dWidth); pRenderer->put_Height(dHeight); bool bBreak = false; DrawPageOnRenderer(pRenderer, nPageIndex, &bBreak); RELEASEINTERFACE(pFontManager); RELEASEOBJECT(pRenderer); oFrame.ClearNoAttack(); return pBgraData; } void CDjVuFileImplementation::ConvertToRaster(int nPageIndex, const std::wstring& wsDstPath, int nImageType, const int& nRasterW, const int& nRasterH) { if (!m_pApplicationFonts) return; NSFonts::IFontManager *pFontManager = m_pApplicationFonts->GenerateFontManager(); NSFonts::IFontsCache* pFontCache = NSFonts::NSFontCache::Create(); pFontCache->SetStreams(m_pApplicationFonts->GetStreams()); pFontManager->SetOwnerCache(pFontCache); NSGraphics::IGraphicsRenderer* pRenderer = NSGraphics::Create(); pRenderer->SetFontManager(pFontManager); double dPageDpiX, dPageDpiY; double dWidth, dHeight; GetPageInfo(nPageIndex, &dWidth, &dHeight, &dPageDpiX, &dPageDpiX); int nWidth = (nRasterW > 0) ? nRasterW : ((int)dWidth * 96 / dPageDpiX); int nHeight = (nRasterH > 0) ? nRasterH : ((int)dHeight * 96 / dPageDpiX); BYTE* pBgraData = new BYTE[nWidth * nHeight * 4]; if (!pBgraData) return; memset(pBgraData, 0xff, nWidth * nHeight * 4); CBgraFrame oFrame; oFrame.put_Data(pBgraData); oFrame.put_Width(nWidth); oFrame.put_Height(nHeight); oFrame.put_Stride(-4 * nWidth); pRenderer->CreateFromBgraFrame(&oFrame); pRenderer->SetSwapRGB(false); pRenderer->put_Width(dWidth); pRenderer->put_Height(dHeight); bool bBreak = false; DrawPageOnRenderer(pRenderer, nPageIndex, &bBreak); oFrame.SaveFile(wsDstPath, nImageType); RELEASEINTERFACE(pFontManager); RELEASEOBJECT(pRenderer); } void CDjVuFileImplementation::ConvertToPdf(const std::wstring& wsDstPath) { CPdfRenderer oPdf(m_pApplicationFonts); bool bBreak = false; for (int nPageIndex = 0, nPagesCount = GetPagesCount(); nPageIndex < nPagesCount; nPageIndex++) { oPdf.NewPage(); double dPageDpiX, dPageDpiY; double dWidth, dHeight; GetPageInfo(nPageIndex, &dWidth, &dHeight, &dPageDpiX, &dPageDpiY); dWidth *= 25.4 / dPageDpiX; dHeight *= 25.4 / dPageDpiY; oPdf.put_Width(dWidth); oPdf.put_Height(dHeight); DrawPageOnRenderer(&oPdf, nPageIndex, &bBreak); //#ifdef _DEBUG printf("%d of %d pages\n", nPageIndex + 1, nPagesCount); //#endif } oPdf.SaveToFile(wsDstPath); } #ifdef BUILDING_WASM_MODULE void getBookmars(const GP& nav, int& pos, int count, NSWasm::CData& out, int level) { while (count > 0 && pos < nav->getBookMarkCount()) { GP gpBookMark; nav->getBookMark(gpBookMark, pos++); GUTF8String str = gpBookMark->url; int endpos; DWORD nPage = str.toULong(1, endpos) - 1; if (endpos == (int)str.length()) { out.AddInt(nPage); out.AddInt(level); out.AddDouble(0.0); GUTF8String description = gpBookMark->displayname; out.WriteString((BYTE*)description.getbuf(), description.length()); } getBookmars(nav, pos, gpBookMark->count, out, level + 1); count--; } } BYTE* CDjVuFileImplementation::GetStructure() { GP nav = m_pDoc->get_djvm_nav(); if (!nav) return NULL; int pos = 0; int count = nav->getBookMarkCount(); if (count <= 0) return NULL; NSWasm::CData oRes; oRes.SkipLen(); getBookmars(nav, pos, count, oRes, 1); oRes.WriteLen(); BYTE* bRes = oRes.GetBuffer(); oRes.ClearWithoutAttack(); return bRes; } BYTE* CDjVuFileImplementation::GetPageGlyphs(int nPageIndex) { return NULL; try { GP pPage = m_pDoc->get_page(nPageIndex); const GP text(DjVuText::create()); const GP text_str(pPage->get_text()); if (!text_str) return NULL; text->decode(text_str); GUTF8String pageText = text->get_xmlText(pPage->get_height()); XmlUtils::CXmlNode hiddenText; XmlUtils::CXmlNode pageColumn; XmlUtils::CXmlNode region; hiddenText.FromXmlStringA(NSDjvu::MakeCString(pageText)); hiddenText.GetNode(L"PAGECOLUMN", pageColumn); pageColumn.GetNode(L"REGION", region); NSWasm::CData oRes; oRes.SkipLen(); XmlUtils::CXmlNodes oParagraphsNodes; region.GetNodes(L"PARAGRAPH", oParagraphsNodes); for (int nParagraphIndex = 0; nParagraphIndex < oParagraphsNodes.GetCount(); nParagraphIndex++) { XmlUtils::CXmlNode oParagraphNode; oParagraphsNodes.GetAt(nParagraphIndex, oParagraphNode); XmlUtils::CXmlNodes oLinesNodes; oParagraphNode.GetNodes(L"LINE", oLinesNodes); for (int nLineIndex = 0; nLineIndex < oLinesNodes.GetCount(); nLineIndex++) { XmlUtils::CXmlNode oLineNode; oLinesNodes.GetAt(nLineIndex, oLineNode); XmlUtils::CXmlNodes oWordsNodes; oLineNode.GetNodes(L"WORD", oWordsNodes); for (int nWordIndex = 0; nWordIndex < oWordsNodes.GetCount(); nWordIndex++) { XmlUtils::CXmlNode oWordNode; oWordsNodes.GetAt(nWordIndex, oWordNode); std::wstring csWord = oWordNode.GetText(); std::wstring csCoords = oWordNode.GetAttribute(L"coords"); double arrCoords[4]; ParseCoords(csCoords, arrCoords, 1); std::string sText = U_TO_UTF8(csWord); oRes.WriteString((BYTE*)sText.c_str(), sText.length()); oRes.AddDouble(arrCoords[0]); oRes.AddDouble(arrCoords[3]); oRes.AddDouble(arrCoords[2] - arrCoords[0]); oRes.AddDouble(arrCoords[1] - arrCoords[3]); } } } oRes.WriteLen(); BYTE* res = oRes.GetBuffer(); oRes.ClearWithoutAttack(); return res; } catch (...) {} return NULL; } BYTE* CDjVuFileImplementation::GetPageLinks (int nPageIndex) { double dPageDpiX, dPageDpiY; double dWidth, dHeight; GetPageInfo(nPageIndex, &dWidth, &dHeight, &dPageDpiX, &dPageDpiY); try { GP pPage = m_pDoc->get_page(nPageIndex); pPage->wait_for_complete_decode(); GP pAnno = pPage->get_decoded_anno(); if (!pAnno) return NULL; GPList map_areas = pAnno->ant->map_areas; NSWasm::CData oRes; oRes.SkipLen(); for (GPosition pos(map_areas); pos; ++pos) { GUTF8String str = map_areas[pos]->url; oRes.WriteString((BYTE*)str.getbuf(), str.length()); // Верхний левый угол double x = map_areas[pos]->get_xmin(); double y = dHeight - map_areas[pos]->get_ymax(); oRes.AddDouble(0.0); oRes.AddDouble(x); oRes.AddDouble(y); oRes.AddDouble(map_areas[pos]->get_xmax() - x); oRes.AddDouble(map_areas[pos]->get_ymax() - map_areas[pos]->get_ymin()); } oRes.WriteLen(); BYTE* res = oRes.GetBuffer(); oRes.ClearWithoutAttack(); return res; } catch (...) {} return NULL; } void CDjVuFileImplementation::GetGlyphs(IRenderer* m_pRenderer, const std::wstring& bsUnicodeText, unsigned int* pGids, double x, double y) { // m_pInternal->GetUnicodes(bsUnicodeText); int nLen = (int)bsUnicodeText.length(); int* pTempUnicodes = new int[nLen]; int nTempUnicodesLen = 0; const wchar_t* pWchars = bsUnicodeText.c_str(); if (sizeof(wchar_t) == 2) { for (int i = 0; i < nLen; i++) { int code = (int)pWchars[i]; if (code >= 0xD800 && code <= 0xDFFF && (i + 1) < nLen) code = 0x10000 + (((code & 0x3FF) << 10) | (0x03FF & pWchars[++i])); pTempUnicodes[nTempUnicodesLen++] = code; } } else for (int i = 0; i < nLen; i++) pTempUnicodes[nTempUnicodesLen++] = (int)pWchars[i]; // m_pInternal->m_oWriter.WriteText(pTempUnicodes, (const int*)pGids, nTempUnicodesLen, x, y, w, h, m_pInternal->m_bIsChangedFontParamBetweenDrawText); bool bIsDumpFont = false; std::wstring sCurrentFontName; double dFontSize; m_pRenderer->get_FontName(&sCurrentFontName); m_pRenderer->get_FontSize(&dFontSize); int nCurrentFont = 0; if ((nCurrentFont != m_lCurrentFont) || (dFontSize != m_dCurrentFontSize)) { m_lCurrentFont = nCurrentFont; m_dCurrentFontSize = dFontSize; bIsDumpFont = true; } // m_oSmartText.CommandText(pTempUnicodes, (const int*)pGids, nTempUnicodesLen, x, y, w, h, bIsDumpFont, this); // 1) сначала определяем точку отсчета и направление baseline double _x1 = x; double _y1 = y; double _x2 = x + 1; double _y2 = y; double sx, shy, shx, sy, tx, ty, tmp; m_pRenderer->GetTransform(&sx, ­, &shx, &sy, &tx, &ty); // m_pTransform->TransformPoint(_x1, _y1); tmp = _x1; _x1 = tmp * sx + _y1 * shx + tx; _y1 = tmp * shy + _y1 * sy + ty; // m_pTransform->TransformPoint(_x2, _y2); tmp = _x2; _x2 = tmp * sx + _y2 * shx + tx; _y2 = tmp * shy + _y2 * sy + ty; double _k = 0; double _b = 0; bool _isConstX = false; if (fabs(_x1 - _x2) < 0.001) { _isConstX = true; _b = _x1; } else { _k = (_y1 - _y2) / (_x1 - _x2); _b = _y1 - _k * _x1; } double dAbsVec = sqrt((_x1 - _x2) * (_x1 - _x2) + (_y1 - _y2) * (_y1 - _y2)); if (dAbsVec == 0) dAbsVec = 1; LONG nCountChars = m_oLine.GetCountChars(); bool bIsNewLine = true; if (0 != nCountChars) { if (_isConstX && m_oLine.m_bIsConstX && fabs(_b - m_oLine.m_dB) < 0.001) bIsNewLine = false; else if (!_isConstX && !m_oLine.m_bIsConstX && fabs(_k - m_oLine.m_dK) < 0.001 && fabs(_b - m_oLine.m_dB) < 0.001) bIsNewLine = false; } if (bIsNewLine && (nCountChars != 0)) { // не совпала baseline. поэтому просто скидываем линию в поток DumpLine(); } // теперь нужно определить сдвиг по baseline относительно destination точки nCountChars = m_oLine.GetCountChars(); double dOffsetX = 0; if (nCountChars == 0) { m_oLine.m_bIsConstX = _isConstX; m_oLine.m_dK = _k; m_oLine.m_dB = _b; m_oLine.m_dX = _x1; m_oLine.m_dY = _y1; m_oLine.m_ex = (_x2 - _x1) / dAbsVec; m_oLine.m_ey = (_y2 - _y1) / dAbsVec; m_oLine.m_dEndX = _x1; m_oLine.m_dEndY = _y1; } else { double _sx = _x1 - m_oLine.m_dEndX; double _sy = _y1 - m_oLine.m_dEndY; double len = sqrt(_sx * _sx + _sy * _sy); if (_sx * m_oLine.m_ex >= 0 && _sy * m_oLine.m_ey >= 0) { // продолжаем линию dOffsetX = len; // теперь посмотрим, может быть нужно вставить пробел?? NSWasm::CHChar* pLastChar = m_oLine.GetTail(); if (dOffsetX > (pLastChar->width + 0.5)) { // вставляем пробел. Пробел у нас будет не совсем пробел. А специфический NSWasm::CHChar* pSpaceChar = m_oLine.AddTail(); pSpaceChar->x = pLastChar->width; pSpaceChar->width = dOffsetX - pLastChar->width; pSpaceChar->unicode = 0xFFFF; pSpaceChar->gid = 0xFFFF; dOffsetX -= pLastChar->width; m_oMeta.WriteBYTE(0); } } else { // буква сдвинута влево относительно предыдущей буквы // на такую ситуацию реагируем просто - просто начинаем новую линию, // предварительно сбросив старую DumpLine(); m_oLine.m_bIsConstX = _isConstX; m_oLine.m_dX = _x1; m_oLine.m_dY = _y1; m_oLine.m_dK = _k; m_oLine.m_dB = _b; m_oLine.m_ex = (_x2 - _x1) / dAbsVec; m_oLine.m_ey = (_y2 - _y1) / dAbsVec; } m_oLine.m_dEndX = _x1; m_oLine.m_dEndY = _y1; } // смотрим, совпадает ли главная часть матрицы. bool bIsTransform = !(fabs(m_pLastTransform.sx() - sx) < 0.001 && fabs(m_pLastTransform.sy() - sy) < 0.001 && fabs(m_pLastTransform.shx() - shx) < 0.001 && fabs(m_pLastTransform.shy() - shy) < 0.001); if (bIsTransform) bIsDumpFont = true; LONG nColor1, nAlpha1; m_pRenderer->get_BrushColor1(&nColor1); m_pRenderer->get_BrushAlpha1(&nAlpha1); bool bIsColor = ((nColor1 != m_nLastBrushColor1) || (nAlpha1 != m_nLastBrushAlpha1)); BYTE nLenMetaCommands = 0; if (bIsColor) nLenMetaCommands += 5; if (bIsTransform) nLenMetaCommands += 17; if (bIsDumpFont) nLenMetaCommands += 13; m_oMeta.WriteBYTE(nLenMetaCommands); double _dumpSize = dFontSize; double _dumpMtx[4]; _dumpMtx[0] = sx; _dumpMtx[1] = shy; _dumpMtx[2] = shx; _dumpMtx[3] = sy; double dTextScale = std::min(sqrt(_dumpMtx[2] * _dumpMtx[2] + _dumpMtx[3] * _dumpMtx[3]), sqrt(_dumpMtx[0] * _dumpMtx[0] + _dumpMtx[1] * _dumpMtx[1])); if ((_dumpSize < 0.1 && dTextScale > 10) || (_dumpSize > 10 && dTextScale < 0.1)) { _dumpSize *= dTextScale; _dumpMtx[0] /= dTextScale; _dumpMtx[1] /= dTextScale; _dumpMtx[2] /= dTextScale; _dumpMtx[3] /= dTextScale; } if (bIsDumpFont) { LONG nFontStyle; m_pRenderer->get_FontStyle(&nFontStyle); m_oMeta.WriteBYTE(41); // CMetafile::ctFontName m_oMeta.AddInt(nCurrentFont); m_oMeta.AddInt(nFontStyle); m_oMeta.WriteDouble(_dumpSize); } if (bIsTransform) { m_pLastTransform.SetElements(sx, shy, shx, sy); m_oLine.m_bIsSetUpTransform = true; m_oLine.m_sx = sx; m_oLine.m_shx = shx; m_oLine.m_shy = shy; m_oLine.m_sy = sy; m_oMeta.WriteBYTE(161); // CMetafile::ctCommandTextTransform m_oMeta.WriteDouble(_dumpMtx[0]); m_oMeta.WriteDouble(_dumpMtx[1]); m_oMeta.WriteDouble(_dumpMtx[2]); m_oMeta.WriteDouble(_dumpMtx[3]); } if (bIsColor) { m_nLastBrushColor1 = nColor1; m_nLastBrushAlpha1 = nAlpha1; m_oMeta.WriteBYTE(22); // CMetafile::ctBrushColor1 LONG lBGR = nColor1; m_oMeta.WriteBYTE((BYTE)(lBGR & 0xFF)); m_oMeta.WriteBYTE((BYTE)((lBGR >> 8) & 0xFF)); m_oMeta.WriteBYTE((BYTE)((lBGR >> 16) & 0xFF)); m_oMeta.WriteBYTE((BYTE)nAlpha1); } NSFonts::IFontManager *m_pFontManager = ((NSGraphics::IGraphicsRenderer*)m_pRenderer)->GetFontManager(); // все, baseline установлен. теперь просто продолжаем линию if (bIsDumpFont) { m_pFontManager->LoadFontFromFile(sCurrentFontName, 0, dFontSize, 72.0, 72.0); m_pFontManager->AfterLoad(); } double dKoef = dFontSize * 25.4 / (72 * abs(m_pFontManager->GetUnitsPerEm())); double dAscender = abs(m_pFontManager->GetAscender()) * dKoef * dAbsVec; double dDescender = abs(m_pFontManager->GetDescender()) * dKoef * dAbsVec; if (m_oLine.m_dAscent < dAscender) m_oLine.m_dAscent = dAscender; if (m_oLine.m_dDescent < dDescender) m_oLine.m_dDescent = dDescender; double dPlusOffset = 0; int* input = NULL; if (pGids) { input = (int*)pGids; m_pFontManager->SetStringGID(TRUE); } else { input = pTempUnicodes; m_pFontManager->SetStringGID(FALSE); } for (int i = 0; i < nTempUnicodesLen; i++) { // double dW = m_oFontManager.MeasureString((const unsigned int*)(input + lIndex), 1, 0, 0, dBoxX, dBoxY, dBoxW, dBoxH); m_pFontManager->LoadString1((unsigned int*)(input + i), 1, 0, 0); TBBox _box = m_pFontManager->MeasureString2(); double dBoxW = abs(_box.fMaxX - _box.fMinX) * 25.4 / 72.0; NSWasm::CHChar* pChar = m_oLine.AddTail(); pChar->unicode = pTempUnicodes[i]; pChar->gid = pGids ? pGids[i] : 0xFFFF; pChar->x = dOffsetX; if (i != 0) dPlusOffset += dOffsetX; dOffsetX = dBoxW; pChar->width = dBoxW * dAbsVec; if (i != 0) m_oMeta.WriteBYTE(0); if (i == (nTempUnicodesLen - 1)) { m_oLine.m_dEndX += dPlusOffset * m_oLine.m_ex; m_oLine.m_dEndY += dPlusOffset * m_oLine.m_ey; } } RELEASEARRAYOBJECTS(pTempUnicodes); } void CDjVuFileImplementation::DumpLine() { if (m_oLine.m_bIsSetUpTransform) { // выставится трансформ!!! // cравнивать нужно с ним!!! m_pLastTransform.SetElements(m_oLine.m_sx, m_oLine.m_shy, m_oLine.m_shx, m_oLine.m_sy); } // скидываем линию в поток pMeta BYTE mask = 0; if (fabs(m_oLine.m_ex - 1.0) < 0.001 && fabs(m_oLine.m_ey) < 0.001) mask |= 0x01; LONG lCountSpaces = 0; LONG lCountSymbols = 0; LONG lCountWords = 0; bool bIsLastSymbol = false; bool bIsGidExist = false; LONG nCount = m_oLine.GetCountChars(); for (LONG i = 0; i < nCount; i++) { NSWasm::CHChar* pChar = &m_oLine.m_pChars[i]; if (pChar->gid != 0xFFFF) { mask |= 0x02; bIsGidExist = true; } if (0xFFFF == pChar->unicode || L' ' == pChar->unicode || L'\t' == pChar->unicode) { lCountSpaces++; if (bIsLastSymbol) { bIsLastSymbol = false; lCountWords++; } } else { bIsLastSymbol = true; lCountSymbols++; } } if (bIsLastSymbol) lCountWords++; if (0 == nCount) { m_oLine.Clear(); m_oMeta.ClearNoAttack(); return; } if (nCount > 1) mask |= 0x04; m_pPageMeta.WriteBYTE(160); // CMetafile::ctCommandTextLine m_pPageMeta.WriteBYTE(mask); m_pPageMeta.WriteDouble(m_oLine.m_dX); m_pPageMeta.WriteDouble(m_oLine.m_dY); if ((mask & 0x01) == 0) { m_pPageMeta.WriteDouble(m_oLine.m_ex); m_pPageMeta.WriteDouble(m_oLine.m_ey); } m_pPageMeta.WriteDouble(m_oLine.m_dAscent); m_pPageMeta.WriteDouble(m_oLine.m_dDescent); LONG _position = 0; if (nCount > 1) { _position = m_pPageMeta.GetSize(); m_pPageMeta.AddInt(0); } BYTE* pBufferMeta = m_oMeta.GetBuffer(); double dWidthLine = 0; double dCurrentGlyphLineOffset = 0; for (LONG lIndexChar = 0; lIndexChar < nCount; lIndexChar++) { NSWasm::CHChar* pChar = &m_oLine.m_pChars[lIndexChar]; // все настроки буквы (m_oMeta) BYTE lLen = *pBufferMeta; pBufferMeta++; if (lLen > 0) m_pPageMeta.Write(pBufferMeta, lLen); pBufferMeta += lLen; // смещение относительно предыдущей буквы (у всех, кроме первой) // юникодное значение // гид (если bIsGidExist == true) // ширина буквы m_pPageMeta.WriteBYTE(80); // CMetafile::ctDrawText if (lIndexChar) m_pPageMeta.WriteDouble2(pChar->x); m_pPageMeta.WriteWCHAR(pChar->unicode); if (bIsGidExist) m_pPageMeta.WriteUSHORT(pChar->gid); m_pPageMeta.WriteDouble2(pChar->width); if (lIndexChar) dCurrentGlyphLineOffset += pChar->x; if (lIndexChar == (nCount - 1)) dWidthLine = dCurrentGlyphLineOffset + pChar->width; } if (nCount > 1) { int* pWidthBuf = (int*)(m_pPageMeta.GetBuffer() + _position); *pWidthBuf = (int)(dWidthLine * 10000); } m_oLine.Clear(); m_oMeta.ClearNoAttack(); m_pPageMeta.WriteBYTE(162); // CMetafile::ctCommandTextLineEnd } #endif void CDjVuFileImplementation::CreateFrame(IRenderer* pRenderer, GP& pPage, int nPage, XmlUtils::CXmlNode& text) { int nWidth = pPage->get_real_width(); int nHeight = pPage->get_real_height(); int nDpi = pPage->get_dpi(); double dPixToMM = 25.4; double dRendDpiX = 0; double dRendDpiY = 0; pRenderer->get_DpiX(&dRendDpiX); pRenderer->get_DpiY(&dRendDpiY); if (0 >= dRendDpiX) dRendDpiX = 72.0; if (0 >= dRendDpiY) dRendDpiY = 72.0; double dRendWidth = 0; double dRendHeight = 0; pRenderer->get_Width(&dRendWidth); pRenderer->get_Height(&dRendHeight); if (0 >= dRendWidth) dRendWidth = 200; if (0 >= dRendHeight) dRendHeight = 300; LONG lImageWidth = (LONG)(dRendDpiX * dRendWidth / dPixToMM); LONG lImageHeight = (LONG)(dRendDpiY * dRendHeight / dPixToMM); long lRendererType = c_nUnknownRenderer; pRenderer->get_Type(&lRendererType); if (c_nPDFWriter == lRendererType) { lImageWidth = pPage->get_real_width(); lImageHeight = pPage->get_real_height(); } else if (c_nHtmlRendrerer == lRendererType) { // TODO: Нужно реализовать функцию // pRenderer->GetMaxImageSize(); //VARIANT var; //renderer->GetAdditionalParam(L"MaxImageSize", &var); //LONG lMaxWidth = var.lVal; //if ((lImageWidth > lMaxWidth) || (lImageHeight > lMaxWidth)) //{ // double dAspect = (double)(lImageWidth) / lImageHeight; // if (lImageWidth > lImageHeight) // { // lImageWidth = lMaxWidth; // lImageHeight = (LONG)(lImageHeight / dAspect); // } // else // { // lImageHeight = lMaxWidth; // lImageWidth = (LONG)(dAspect * lImageHeight); // } //} } BYTE* pBufferDst = new BYTE[4 * lImageHeight * lImageWidth]; if (!pBufferDst) return; Aggplus::CImage oImage; oImage.Create(pBufferDst, lImageWidth, lImageHeight, 4 * lImageWidth); if (pPage->is_legal_photo() || pPage->is_legal_compound()) { GRect oRectAll(0, 0, lImageWidth, lImageHeight); GP pImage = pPage->get_pixmap(oRectAll, oRectAll); BYTE* pBuffer = pBufferDst; for (int j = lImageHeight - 1; j >= 0; --j) { GPixel* pLine = pImage->operator [](j); for (int i = 0; i < lImageWidth; ++i, pBuffer += 4, ++pLine) { pBuffer[0] = pLine->b; pBuffer[1] = pLine->g; pBuffer[2] = pLine->r; pBuffer[3] = 255; } } } else if (pPage->is_legal_bilevel()) { GRect oRectAll(0, 0, lImageWidth, lImageHeight); GP pBitmap = pPage->get_bitmap(oRectAll, oRectAll, 4); int nPaletteEntries = pBitmap->get_grays(); unsigned int* palette = new unsigned int[nPaletteEntries]; // Create palette for the bitmap int color = 0xff0000; int decrement = color / (nPaletteEntries - 1); for (int i = 0; i < nPaletteEntries; ++i) { BYTE level = (BYTE)(color >> 16); palette[i] = (0xFF000000 | level << 16 | level << 8 | level); color -= decrement; } unsigned int* pBuffer = (unsigned int*)pBufferDst; for (int j = lImageHeight - 1; j >= 0; --j) { BYTE* pLine = pBitmap->operator [](j); for (int i = 0; i < lImageWidth; ++i, ++pBuffer, ++pLine) { if (*pLine < nPaletteEntries) { *pBuffer = palette[*pLine]; } else { *pBuffer = palette[0]; } } } RELEASEARRAYOBJECTS(palette); } else { // белый фрейм?? //memset(pBufferDst, 0xFF, 4 * lImageWidth * lImageHeight); GRect oRectAll(0, 0, lImageWidth, lImageHeight); GP pImage = pPage->get_pixmap(oRectAll, oRectAll); if (NULL != pImage) { BYTE* pBuffer = pBufferDst; for (int j = lImageHeight - 1; j >= 0; --j) { GPixel* pLine = pImage->operator [](j); for (int i = 0; i < lImageWidth; ++i, pBuffer += 4, ++pLine) { pBuffer[0] = pLine->b; pBuffer[1] = pLine->g; pBuffer[2] = pLine->r; pBuffer[3] = 255; } } } else { GP pBitmap = pPage->get_bitmap(oRectAll, oRectAll, 4); if (NULL != pBitmap) { int nPaletteEntries = pBitmap->get_grays(); unsigned int* palette = new unsigned int[nPaletteEntries]; // Create palette for the bitmap int color = 0xff0000; int decrement = color / (nPaletteEntries - 1); for (int i = 0; i < nPaletteEntries; ++i) { BYTE level = (BYTE)(color >> 16); palette[i] = (0xFF000000 | level << 16 | level << 8 | level); color -= decrement; } unsigned int* pBuffer = (unsigned int*)pBufferDst; for (int j = lImageHeight - 1; j >= 0; --j) { BYTE* pLine = pBitmap->operator [](j); for (int i = 0; i < lImageWidth; ++i, ++pBuffer, ++pLine) { if (*pLine < nPaletteEntries) { *pBuffer = palette[*pLine]; } else { *pBuffer = palette[0]; } } } RELEASEARRAYOBJECTS(palette); } } } pRenderer->BeginCommand(c_nPageType); if (c_nGrRenderer != lRendererType && c_nHtmlRendrerer != lRendererType && c_nHtmlRendrerer2 != lRendererType) { TextToRenderer(pRenderer, text, dPixToMM / nDpi); } pRenderer->DrawImage((IGrObject*)&oImage, 0, 0, dRendWidth, dRendHeight); pRenderer->EndCommand(c_nPageType); } void CDjVuFileImplementation::CreatePdfFrame(IRenderer* pRenderer, GP& pPage, int nPageIndex, XmlUtils::CXmlNode& oText) { double dPageDpiX, dPageDpiY; double dWidth, dHeight; GetPageInfo(nPageIndex, &dWidth, &dHeight, &dPageDpiX, &dPageDpiY); dWidth *= 25.4 / dPageDpiX; dHeight *= 25.4 / dPageDpiY; pRenderer->BeginCommand(c_nPageType); TextToRenderer(pRenderer, oText, 25.4 / pPage->get_dpi()); LONG lImageWidth = pPage->get_real_width(); LONG lImageHeight = pPage->get_real_height(); CPdfRenderer* pPdf = (CPdfRenderer*)pRenderer; if (pPage->is_legal_photo()) { BYTE* pBufferDst = new BYTE[4 * lImageHeight * lImageWidth]; if (!pBufferDst) return; Aggplus::CImage oImage; oImage.Create(pBufferDst, lImageWidth, lImageHeight, 4 * lImageWidth); GRect oRectAll(0, 0, lImageWidth, lImageHeight); GP pImage = pPage->get_pixmap(oRectAll, oRectAll); BYTE* pBuffer = pBufferDst; for (int j = lImageHeight - 1; j >= 0; --j) { GPixel* pLine = pImage->operator [](j); for (int i = 0; i < lImageWidth; ++i, pBuffer += 4, ++pLine) { pBuffer[0] = pLine->b; pBuffer[1] = pLine->g; pBuffer[2] = pLine->r; pBuffer[3] = 255; } } pRenderer->DrawImage((IGrObject*)&oImage, 0, 0, dWidth, dHeight); } else if (pPage->is_legal_compound()) { GRect oRectAll(0, 0, lImageWidth, lImageHeight); GP pIW44Image = pPage->get_bg44(); if (NULL != pIW44Image) { int nBgWidth = pIW44Image->get_width(); int nBgHeight = pIW44Image->get_height(); GP pBgImage = pIW44Image->get_pixmap(); if (NULL != pBgImage) { BYTE* pBgBuffer = new BYTE[4 * nBgWidth * nBgHeight]; if (!pBgBuffer) return; Aggplus::CImage oBgImage; oBgImage.Create(pBgBuffer, nBgWidth, nBgHeight, 4 * nBgWidth); BYTE* pBuffer = pBgBuffer; for (int j = nBgHeight - 1; j >= 0; --j) { GPixel* pLine = pBgImage->operator [](j); for (int i = 0; i < nBgWidth; ++i, pBuffer += 4, ++pLine) { pBuffer[0] = pLine->b; pBuffer[1] = pLine->g; pBuffer[2] = pLine->r; pBuffer[3] = 255; } } pRenderer->DrawImage((IGrObject*)&oBgImage, 0, 0, dWidth, dHeight); } } GP pImage = pPage->get_fgpm(); if (NULL == pImage) pImage = pPage->get_fg_pixmap(oRectAll); if (NULL != pImage) { unsigned int unPixmapH = pImage->rows(); unsigned int unPixmapW = pImage->columns(); BYTE* pBufferDst = new BYTE[4 * unPixmapH * unPixmapW]; if (!pBufferDst) return; Aggplus::CImage oImage; oImage.Create(pBufferDst, unPixmapW, unPixmapH, 4 * unPixmapW); BYTE* pBuffer = pBufferDst; for (int j = unPixmapH - 1; j >= 0; --j) { GPixel* pLine = pImage->operator [](j); for (int i = 0; i < unPixmapW; ++i, pBuffer += 4, ++pLine) { pBuffer[0] = pLine->b; pBuffer[1] = pLine->g; pBuffer[2] = pLine->r; pBuffer[3] = 255; } } GP pBitmap = pPage->get_bitmap(oRectAll, oRectAll, 4); NSImages::CPixJbig2 oPix; if (oPix.Create(lImageWidth, lImageHeight, 1)) { for (int nY = 0; nY < lImageHeight; nY++) { BYTE* pLine = pBitmap->operator [](nY); for (int nX = 0; nX < lImageWidth; nX++, pLine++) { oPix.SetPixel(nX, lImageHeight - 1 - nY, *pLine); } } pPdf->DrawImageWith1bppMask((IGrObject*)&oImage, &oPix, lImageWidth, lImageHeight, 0, 0, dWidth, dHeight); oPix.Destroy(); } } } else if (pPage->is_legal_bilevel()) { GRect oRectAll(0, 0, lImageWidth, lImageHeight); GP pBitmap = pPage->get_bitmap(oRectAll, oRectAll, 4); NSImages::CPixJbig2 oPix; if (oPix.Create(lImageWidth, lImageHeight, 1)) { for (int nY = 0; nY < lImageHeight; nY++) { BYTE* pLine = pBitmap->operator [](nY); for (int nX = 0; nX < lImageWidth; nX++, pLine++) { oPix.SetPixel(nX, lImageHeight - 1 - nY, *pLine); } } pPdf->DrawImage1bpp(&oPix, lImageWidth, lImageHeight, 0, 0, dWidth, dHeight); oPix.Destroy(); } } else { // белый фрейм?? //memset(pBufferDst, 0xFF, 4 * lImageWidth * lImageHeight); GRect oRectAll(0, 0, lImageWidth, lImageHeight); GP pImage = pPage->get_pixmap(oRectAll, oRectAll); if (NULL != pImage) { BYTE* pBufferDst = new BYTE[4 * lImageHeight * lImageWidth]; if (pBufferDst) { Aggplus::CImage oImage; oImage.Create(pBufferDst, lImageWidth, lImageHeight, 4 * lImageWidth); BYTE* pBuffer = pBufferDst; for (int j = lImageHeight - 1; j >= 0; --j) { GPixel* pLine = pImage->operator [](j); for (int i = 0; i < lImageWidth; ++i, pBuffer += 4, ++pLine) { pBuffer[0] = pLine->b; pBuffer[1] = pLine->g; pBuffer[2] = pLine->r; pBuffer[3] = 255; } } pRenderer->DrawImage((IGrObject*)&oImage, 0, 0, dWidth, dHeight); } } else { GP pBitmap = pPage->get_bitmap(oRectAll, oRectAll, 4); if (NULL != pBitmap) { int nPaletteEntries = pBitmap->get_grays(); if (nPaletteEntries <= 2) { NSImages::CPixJbig2 oPix; if (oPix.Create(lImageWidth, lImageHeight, 1)) { for (int nY = 0; nY < lImageHeight; nY++) { BYTE* pLine = pBitmap->operator [](nY); for (int nX = 0; nX < lImageWidth; nX++, pLine++) { oPix.Create(nX, lImageHeight - 1 - nY, *pLine); } } pPdf->DrawImage1bpp(&oPix, lImageWidth, lImageHeight, 0, 0, dWidth, dHeight); oPix.Destroy(); } } else { BYTE* pBufferDst = new BYTE[4 * lImageHeight * lImageWidth]; if (!pBufferDst) return; Aggplus::CImage oImage; oImage.Create(pBufferDst, lImageWidth, lImageHeight, 4 * lImageWidth); unsigned int* palette = new unsigned int[nPaletteEntries]; // Create palette for the bitmap int color = 0xff0000; int decrement = color / (nPaletteEntries - 1); for (int i = 0; i < nPaletteEntries; ++i) { BYTE level = (BYTE)(color >> 16); palette[i] = (0xFF000000 | level << 16 | level << 8 | level); color -= decrement; } unsigned int* pBuffer = (unsigned int*)pBufferDst; for (int j = lImageHeight - 1; j >= 0; --j) { BYTE* pLine = pBitmap->operator [](j); for (int i = 0; i < lImageWidth; ++i, ++pBuffer, ++pLine) { if (*pLine < nPaletteEntries) { *pBuffer = palette[*pLine]; } else { *pBuffer = palette[0]; } } } RELEASEARRAYOBJECTS(palette); pRenderer->DrawImage((IGrObject*)&oImage, 0, 0, dWidth, dHeight); } } } } pRenderer->EndCommand(c_nPageType); } void CDjVuFileImplementation::CreateGrFrame(IRenderer* pRenderer, GP& pPage, bool* pBreak) { int nWidth = pPage->get_real_width(); int nHeight = pPage->get_real_height(); BYTE* pBufferDst = NULL; LONG lImageWidth = 0; LONG lImageHeight = 0; // TODO: Реализовать для графического рендерера //VARIANT var; //renderer->GetAdditionalParam(L"Pixels", &var); //pBufferDst = (BYTE*)var.lVal; //renderer->GetAdditionalParam(L"PixelsWidth", &var); //lImageWidth = var.lVal; //renderer->GetAdditionalParam(L"PixelsHeight", &var); //lImageHeight = var.lVal; volatile bool* pCancel = pBreak; if ((NULL != pCancel) && (TRUE == *pCancel)) return; if (pPage->is_legal_photo() || pPage->is_legal_compound()) { GRect oRectAll(0, 0, lImageWidth, lImageHeight); GP pImage = pPage->get_pixmap(oRectAll, oRectAll); BYTE* pBuffer = pBufferDst; for (int j = 0; j < lImageHeight; ++j) { GPixel* pLine = pImage->operator [](j); if ((NULL != pCancel) && (TRUE == *pCancel)) return; for (int i = 0; i < lImageWidth; ++i, pBuffer += 4, ++pLine) { pBuffer[0] = pLine->b; pBuffer[1] = pLine->g; pBuffer[2] = pLine->r; pBuffer[3] = 255; } } } else if (pPage->is_legal_bilevel()) { GRect oRectAll(0, 0, lImageWidth, lImageHeight); GP pBitmap = pPage->get_bitmap(oRectAll, oRectAll, 4); int nPaletteEntries = pBitmap->get_grays(); unsigned int* palette = new unsigned int[nPaletteEntries]; // Create palette for the bitmap int color = 0xff0000; int decrement = color / (nPaletteEntries - 1); for (int i = 0; i < nPaletteEntries; ++i) { BYTE level = (BYTE)(color >> 16); palette[i] = (0xFF000000 | level << 16 | level << 8 | level); color -= decrement; } unsigned int* pBuffer = (unsigned int*)pBufferDst; for (int j = 0; j < lImageHeight; ++j) { BYTE* pLine = pBitmap->operator [](j); if ((NULL != pCancel) && (TRUE == *pCancel)) return; for (int i = 0; i < lImageWidth; ++i, ++pBuffer, ++pLine) { if (*pLine < nPaletteEntries) { *pBuffer = palette[*pLine]; } else { *pBuffer = palette[0]; } } } RELEASEARRAYOBJECTS(palette); } else { // белый фрейм?? //memset(pBufferDst, 0xFF, 4 * lImageWidth * lImageHeight); GRect oRectAll(0, 0, lImageWidth, lImageHeight); GP pImage = pPage->get_pixmap(oRectAll, oRectAll); if (NULL != pImage) { BYTE* pBuffer = pBufferDst; for (int j = 0; j < lImageHeight; ++j) { GPixel* pLine = pImage->operator [](j); if ((NULL != pCancel) && (TRUE == *pCancel)) return; for (int i = 0; i < lImageWidth; ++i, pBuffer += 4, ++pLine) { pBuffer[0] = pLine->b; pBuffer[1] = pLine->g; pBuffer[2] = pLine->r; pBuffer[3] = 255; } } return; } GP pBitmap = pPage->get_bitmap(oRectAll, oRectAll, 4); if (NULL != pBitmap) { int nPaletteEntries = pBitmap->get_grays(); unsigned int* palette = new unsigned int[nPaletteEntries]; // Create palette for the bitmap int color = 0xff0000; int decrement = color / (nPaletteEntries - 1); for (int i = 0; i < nPaletteEntries; ++i) { BYTE level = (BYTE)(color >> 16); palette[i] = (0xFF000000 | level << 16 | level << 8 | level); color -= decrement; } unsigned int* pBuffer = (unsigned int*)pBufferDst; for (int j = 0; j < lImageHeight; ++j) { BYTE* pLine = pBitmap->operator [](j); if ((NULL != pCancel) && (TRUE == *pCancel)) return; for (int i = 0; i < lImageWidth; ++i, ++pBuffer, ++pLine) { if (*pLine < nPaletteEntries) { *pBuffer = palette[*pLine]; } else { *pBuffer = palette[0]; } } } RELEASEARRAYOBJECTS(palette); } } } XmlUtils::CXmlNode CDjVuFileImplementation::ParseText(GP pPage) { XmlUtils::CXmlNode paragraph; const GP text(DjVuText::create()); const GP text_str(pPage->get_text()); if (text_str) { text->decode(text_str); GUTF8String pageText = text->get_xmlText(pPage->get_height()); XmlUtils::CXmlNode hiddenText; XmlUtils::CXmlNode pageColumn; XmlUtils::CXmlNode region; hiddenText.FromXmlStringA(NSDjvu::MakeCString(pageText)); hiddenText.GetNode(L"PAGECOLUMN", pageColumn); pageColumn.GetNode(L"REGION", region); region.GetNode(L"PARAGRAPH", paragraph); } return paragraph; } void CDjVuFileImplementation::TextToRenderer(IRenderer* pRenderer, XmlUtils::CXmlNode oTextNode, double dKoef, bool isView) { // Выставим шрифт пустой (чтобы растягивать по всему ректу) pRenderer->put_FontName(L"DjvuEmptyFont"); //std::wstring csText = oTextNode.GetXml(); XmlUtils::CXmlNodes oLinesNodes; oTextNode.GetNodes(L"LINE", oLinesNodes); for (int nLineIndex = 0; nLineIndex < oLinesNodes.GetCount(); ++nLineIndex) { XmlUtils::CXmlNode oLineNode; oLinesNodes.GetAt(nLineIndex, oLineNode); XmlUtils::CXmlNodes oWordsNodes; oLineNode.GetNodes(L"WORD", oWordsNodes); for (int nWordIndex = 0; nWordIndex < oWordsNodes.GetCount(); ++nWordIndex) { XmlUtils::CXmlNode oWordNode; oWordsNodes.GetAt(nWordIndex, oWordNode); std::wstring csWord = oWordNode.GetText(); std::wstring csCoords = oWordNode.GetAttribute(L"coords"); double arrCoords[4]; ParseCoords(csCoords, arrCoords, dKoef); DrawPageText(pRenderer, arrCoords, csWord); } } } void CDjVuFileImplementation::DrawPageText(IRenderer* pRenderer, double* pdCoords, const std::wstring& wsText) { pRenderer->put_FontSize(pdCoords[1] - pdCoords[3]); pRenderer->CommandDrawText(wsText, (float)(pdCoords[0]), (float)(pdCoords[3]), (float)(pdCoords[2] - pdCoords[0]), (float)(pdCoords[1] - pdCoords[3])); #ifdef BUILDING_WASM_MODULE GetGlyphs(pRenderer, wsText, NULL, pdCoords[0], pdCoords[3]); #endif } void CDjVuFileImplementation::ParseCoords(const std::wstring& wsCoordsStr, double* pdCoords, double dKoef) { std::vector vCoords = NSStringExt::Split(wsCoordsStr, L','); if (vCoords.size() >= 4) { for (int nIndex = 0; nIndex < 4; nIndex++) { pdCoords[nIndex] = NSDjvu::GetInteger(vCoords.at(nIndex)) * dKoef; } } }