/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include "GraphicHelpers.hxx" #include "TagLogger.hxx" #include #include "PropertyIds.hxx" #include #include #include #include #include #include #include #include #include #include namespace writerfilter::dmapper { using namespace com::sun::star; PositionHandler::PositionHandler( std::pair& rPositionOffsets, std::pair& rAligns ) : LoggedProperties("PositionHandler"), m_nOrient(text::VertOrientation::NONE), m_nRelation(text::RelOrientation::FRAME), m_nPosition(0), m_rPositionOffsets(rPositionOffsets), m_rAligns(rAligns) { } PositionHandler::~PositionHandler( ) { } void PositionHandler::lcl_attribute( Id aName, Value& rVal ) { sal_Int32 nIntValue = rVal.getInt( ); switch ( aName ) { case NS_ooxml::LN_CT_PosV_relativeFrom: { switch ( nIntValue ) { case NS_ooxml::LN_Value_wordprocessingDrawing_ST_RelFromV_margin: m_nRelation = text::RelOrientation::PAGE_PRINT_AREA; break; case NS_ooxml::LN_Value_wordprocessingDrawing_ST_RelFromV_page: m_nRelation = text::RelOrientation::PAGE_FRAME; break; case NS_ooxml::LN_Value_wordprocessingDrawing_ST_RelFromV_topMargin: m_nRelation = text::RelOrientation::PAGE_PRINT_AREA_TOP; break; case NS_ooxml::LN_Value_wordprocessingDrawing_ST_RelFromV_bottomMargin: m_nRelation = text::RelOrientation::PAGE_PRINT_AREA_BOTTOM; break; case NS_ooxml::LN_Value_wordprocessingDrawing_ST_RelFromV_paragraph: m_nRelation = text::RelOrientation::FRAME; break; case NS_ooxml::LN_Value_wordprocessingDrawing_ST_RelFromV_line: m_nRelation = text::RelOrientation::TEXT_LINE; break; // TODO There are some other unhandled values default: SAL_WARN("writerfilter", "unhandled case (" << nIntValue << ") in NS_ooxml::LN_CT_PosV_relativeFrom"); } } break; case NS_ooxml::LN_CT_PosH_relativeFrom: { switch ( nIntValue ) { case NS_ooxml::LN_Value_wordprocessingDrawing_ST_RelFromH_margin: m_nRelation = text::RelOrientation::PAGE_PRINT_AREA; break; case NS_ooxml::LN_Value_wordprocessingDrawing_ST_RelFromH_page: m_nRelation = text::RelOrientation::PAGE_FRAME; break; case NS_ooxml::LN_Value_wordprocessingDrawing_ST_RelFromH_insideMargin: m_nRelation = text::RelOrientation::PAGE_FRAME; m_bPageToggle = true; break; case NS_ooxml::LN_Value_wordprocessingDrawing_ST_RelFromH_column: m_nRelation = text::RelOrientation::FRAME; break; case NS_ooxml::LN_Value_wordprocessingDrawing_ST_RelFromH_character: m_nRelation = text::RelOrientation::CHAR; break; case NS_ooxml::LN_Value_wordprocessingDrawing_ST_RelFromH_leftMargin: m_nRelation = text::RelOrientation::PAGE_LEFT; break; case NS_ooxml::LN_Value_wordprocessingDrawing_ST_RelFromH_rightMargin: m_nRelation = text::RelOrientation::PAGE_RIGHT; break; // TODO There are some other unhandled values default: SAL_WARN("writerfilter", "unhandled case (" << nIntValue << ") in NS_ooxml::LN_CT_PosH_relativeFrom"); } } break; default: #ifdef DBG_UTIL TagLogger::getInstance().element("unhandled"); #endif break; } } void PositionHandler::lcl_sprm(Sprm& rSprm) { sal_uInt32 nSprmId = rSprm.getId(); switch (nSprmId) { case NS_ooxml::LN_CT_PosH_posOffset: m_nPosition = oox::drawingml::convertEmuToHmm(m_rPositionOffsets.first.toInt32()); m_rPositionOffsets.first.clear(); break; case NS_ooxml::LN_CT_PosV_posOffset: m_nPosition = oox::drawingml::convertEmuToHmm(m_rPositionOffsets.second.toInt32()); m_rPositionOffsets.second.clear(); break; case NS_ooxml::LN_CT_PosH_align: { OUString& rAlign = m_rAligns.first; if (rAlign == "left") m_nOrient = text::HoriOrientation::LEFT; else if (rAlign == "right") m_nOrient = text::HoriOrientation::RIGHT; else if (rAlign == "center") m_nOrient = text::HoriOrientation::CENTER; else if (rAlign == "inside") m_nOrient = text::HoriOrientation::INSIDE; else if (rAlign == "outside") m_nOrient = text::HoriOrientation::OUTSIDE; rAlign.clear(); break; } case NS_ooxml::LN_CT_PosV_align: { OUString& rAlign = m_rAligns.second; if (rAlign == "top") m_nOrient = text::VertOrientation::TOP; else if (rAlign == "bottom") m_nOrient = text::VertOrientation::BOTTOM; else if (rAlign == "center") m_nOrient = text::VertOrientation::CENTER; else if (rAlign == "inside" && m_nRelation == text::RelOrientation::PAGE_PRINT_AREA_BOTTOM) m_nOrient = text::VertOrientation::TOP; else if (rAlign == "outside" && m_nRelation == text::RelOrientation::PAGE_PRINT_AREA_BOTTOM) m_nOrient = text::VertOrientation::BOTTOM; rAlign.clear(); break; } } } sal_Int16 PositionHandler::orientation() const { if( m_nRelation == text::RelOrientation::TEXT_LINE ) { // It appears that to 'line of text' alignment is backwards to other alignments, // 'top' meaning putting on top of the line instead of having top at the line. if( m_nOrient == text::VertOrientation::TOP ) return text::VertOrientation::BOTTOM; else if( m_nOrient == text::VertOrientation::BOTTOM ) return text::VertOrientation::TOP; } return m_nOrient; } WrapHandler::WrapHandler( ) : LoggedProperties("WrapHandler"), m_nType( 0 ), m_nSide( 0 ) { } WrapHandler::~WrapHandler( ) { } void WrapHandler::lcl_attribute( Id aName, Value& rVal ) { switch ( aName ) { case NS_ooxml::LN_CT_Wrap_type: m_nType = sal_Int32( rVal.getInt( ) ); break; case NS_ooxml::LN_CT_Wrap_side: m_nSide = sal_Int32( rVal.getInt( ) ); break; default:; } } void WrapHandler::lcl_sprm( Sprm& ) { } text::WrapTextMode WrapHandler::getWrapMode( ) const { // The wrap values do not map directly to our wrap mode, // e.g. none in .docx actually means through in LO. text::WrapTextMode nMode = text::WrapTextMode_THROUGH; switch ( m_nType ) { case NS_ooxml::LN_Value_vml_wordprocessingDrawing_ST_WrapType_square: // through and tight are somewhat complicated, approximate case NS_ooxml::LN_Value_vml_wordprocessingDrawing_ST_WrapType_tight: case NS_ooxml::LN_Value_vml_wordprocessingDrawing_ST_WrapType_through: { switch ( m_nSide ) { case NS_ooxml::LN_Value_vml_wordprocessingDrawing_ST_WrapSide_left: nMode = text::WrapTextMode_LEFT; break; case NS_ooxml::LN_Value_vml_wordprocessingDrawing_ST_WrapSide_right: nMode = text::WrapTextMode_RIGHT; break; default: nMode = text::WrapTextMode_PARALLEL; } } break; case NS_ooxml::LN_Value_vml_wordprocessingDrawing_ST_WrapType_topAndBottom: nMode = text::WrapTextMode_NONE; break; case NS_ooxml::LN_Value_vml_wordprocessingDrawing_ST_WrapType_none: default: nMode = text::WrapTextMode_THROUGH; } return nMode; } void GraphicZOrderHelper::addItem(uno::Reference const& props, sal_Int64 const relativeHeight) { m_items[ relativeHeight ] = props; } void GraphicZOrderHelper::adjustRelativeHeight(sal_Int64& rRelativeHeight, bool bIsZIndex, bool bIsBehindText, bool bIsInHeader) { // zOrder can be defined either by z-index (VML) or by relativeHeight (DML). // z-index indicates background with a negative value, // while relativeHeight indicates background with behindDoc = true. // // In general, all z-index-defined shapes appear on top of relativeHeight graphics // regardless of the value. // priority order // above text: positive sal_Int32 z-index (opaque/in front of text) // relativeHeight (represented here as a negative sal_Int32, but still opaque) // behind body: negative sal_Int32 z-index (!opaque/in the background) // behindText relativeHeight // (in header) positive z-index // (in header) relativeHeight // (in header) negative z-index // (in header) behindText const sal_Int64 nMaxUnsignedInt32 = SAL_MAX_UINT32; if (!bIsInHeader) { if (bIsZIndex) { // this number is already fine // positive values are the only positive values coming into zOrder, // and negative values will be !opaque (below text) } else if (!bIsBehindText) { assert (rRelativeHeight < 0); // this number is already fine - will be above text, but relativeHeight is negative } else { // reduce to negative level 1 to force below a negative z-index rRelativeHeight -= nMaxUnsignedInt32; } } else // bIsInHeader { if (bIsZIndex && !bIsBehindText) rRelativeHeight -= nMaxUnsignedInt32 * 2; // reduce to negative level 2 else if (!bIsBehindText) rRelativeHeight -= nMaxUnsignedInt32 * 3; // reduce to negative level 3 else if (bIsZIndex) rRelativeHeight -= nMaxUnsignedInt32 * 4; // reduce to negative level 4 else rRelativeHeight -= nMaxUnsignedInt32 * 5; // reduce to negative level 5 } } // The relativeHeight value in .docx is an arbitrary number, where only the relative ordering matters. // But in Writer, the z-order is index in 0..(numitems-1) range, so whenever a new item needs to be // added in the proper z-order, it is necessary to find the proper index. // The key to this function is that later on, when setPropertyValue("ZOrder", ), // SW also automatically increments ALL zOrders >= the one returned for this fly. // Thus, getProperty PROP_Z_ORDER for relativeHeight "x" can return different values for itemZOrder. sal_Int32 GraphicZOrderHelper::findZOrder(sal_Int64 relativeHeight, bool bOldStyle) { // std::map is iterated sorted by key auto it = std::find_if(m_items.cbegin(), m_items.cend(), [relativeHeight, bOldStyle](const Items::value_type& rItem) { // Old-style ordering differs in what should happen when there is already an item with the same z-order: // we belong under it in case of new-style, but we belong above it in case of old-style. return bOldStyle ? (rItem.first > relativeHeight) : (rItem.first >= relativeHeight); } ); sal_Int32 itemZOrderOffset(0); // before the item if( it == m_items.end()) // we're topmost { if( m_items.empty()) return 0; // the lowest --it; itemZOrderOffset = 1; // after the topmost // Check if this shape has a textbox. If so, the textbox will have its own ZOrder, so // suggest a larger offset. bool bTextBox = false; uno::Reference xShape = it->second; uno::Reference xInfo = xShape->getPropertySetInfo(); if (xInfo->hasPropertyByName("TextBox")) { xShape->getPropertyValue("TextBox") >>= bTextBox; } if (bTextBox) { ++itemZOrderOffset; } } // SwXFrame::getPropertyValue throws uno::RuntimeException // when its GetFrameFormat() returns nullptr try { sal_Int32 itemZOrder(0); if( it->second->getPropertyValue(getPropertyName( PROP_Z_ORDER )) >>= itemZOrder ) return itemZOrder + itemZOrderOffset; } catch (const uno::RuntimeException&) { TOOLS_WARN_EXCEPTION("writerfilter", "Exception when getting item z-order"); } SAL_WARN( "writerfilter", "findZOrder() didn't find item z-order" ); return 0; // this should not(?) happen } ExtentHandler::ExtentHandler() { } ExtentHandler::~ExtentHandler() { } void ExtentHandler::attribute(Id nName, Value & rValue) { sal_Int32 nIntValue = rValue.getInt(); switch (nName) { case NS_ooxml::LN_CT_PositiveSize2D_cx: { m_Extent.Width = nIntValue; } break; case NS_ooxml::LN_CT_PositiveSize2D_cy: { m_Extent.Height = nIntValue; } break; default: break; } } void ExtentHandler::sprm(Sprm & rSprm) { sal_uInt32 nSprmId = rSprm.getId(); switch(nSprmId) { case NS_ooxml::LN_CT_Inline_extent: case NS_ooxml::LN_CT_Anchor_extent: { writerfilter::Reference::Pointer_t pProperties = rSprm.getProps(); if( pProperties ) { pProperties->resolve(*this); } } break; default: break; } } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */